diff --git a/.editorconfig b/.editorconfig index 0fa425877232c..6d3f8a7f658e7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,3 +6,6 @@ root = true indent_style = space indent_size = 2 insert_final_newline = true + +[*.py] +indent_size = 4 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 20773c55c28cf..3cb1c9371896b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,34 +8,35 @@ # TODO: /.clang-format -/.clang-tidy @egorzhdan +/.clang-tidy @egorzhdan -# TODO: /.dir-locals.el +/.dir-locals.el @al45tair +/.editorconfig @hamishknight # TODO: /.flake8 -# TODO: /.gitattributes +/.gitattributes @shahmishal # .github /.github/ @shahmishal /.github/CODEOWNERS @AnthonyLatsis @shahmishal -/.github/ISSUE_TEMPLATE/ @AnthonyLatsis @hborla @LucianoPAlmeida @shahmishal @xedin -/.github/PULL_REQUEST_TEMPLATE.md @AnthonyLatsis @hborla @LucianoPAlmeida @shahmishal @xedin +/.github/ISSUE_TEMPLATE/ @AnthonyLatsis @hborla @shahmishal @xedin +/.github/PULL_REQUEST_TEMPLATE.md @AnthonyLatsis @hborla @shahmishal @xedin -# TODO: /.gitignore +/.gitignore @shahmishal # TODO: /.mailmap # TODO: /Brewfile -# TODO: /CHANGELOG.md +/CHANGELOG.md @hborla # TODO: /CMakeLists.txt -# TODO: /CODE_OF_CONDUCT.md -# TODO: /CODE_OWNERS.TXT -# TODO: /CONTRIBUTING.md -# TODO: /LICENSE.txt +/CODE_OF_CONDUCT.md @swiftlang/core-team +/CODE_OWNERS.TXT @swiftlang/core-team +/CONTRIBUTING.md @AnthonyLatsis @xedin +/LICENSE.txt @swiftlang/core-team # TODO: /README.md # Runtimes /Runtimes/**/*.cmake @etcwilde @compnerd @edymtt @justice-adams-apple /Runtimes/**/CMakeLists.txt @etcwilde @compnerd @edymtt @justice-adams-apple /Runtimes/Core/cmake/caches/Vendors/Apple/ @etcwilde @shahmishal @edymtt @justice-adams-apple -/Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake @tshortli @etcwilde @compnerd @edymtt @justice-adams-apple +/Runtimes/*/cmake/modules/ExperimentalFeatures.cmake @tshortli @etcwilde @compnerd @edymtt @justice-adams-apple # SwiftCompilerSources /SwiftCompilerSources @eeckstein @@ -60,7 +61,7 @@ /docs/ABI/RegisterUsage.md @al45tair /docs/CrossCompilationModel.md @MaxDesiatov /docs/Generics @slavapestov -/docs/HowToGuides/ @AnthonyLatsis @LucianoPAlmeida @xedin +/docs/HowToGuides/ @AnthonyLatsis @xedin /docs/Optimizer* @eeckstein /docs/SIL* @jckarter /docs/Windows* @compnerd @@ -81,11 +82,11 @@ /include/swift/AST/DiagnosticsClangImporter.def @zoecarver @egorzhdan @beccadax @ian-twilightcoder @Xazax-hun @j-hui @fahadnayyar @susmonteiro /include/swift/AST/DiagnosticsDriver.def @artemcm /include/swift/AST/DiagnosticsFrontend.def @artemcm @tshortli -/include/swift/AST/DiagnosticsIDE.def @ahoppen @bnbarham @hamishknight @rintaro +/include/swift/AST/DiagnosticsIDE.def @bnbarham @hamishknight @rintaro /include/swift/AST/DiagnosticsIRGen.def @rjmccall /include/swift/AST/DiagnosticsModuleDiffer.def @nkcsgexi -/include/swift/AST/DiagnosticsParse.def @ahoppen @bnbarham @CodaFi @DougGregor @hamishknight @rintaro -/include/swift/AST/DiagnosticsRefactoring.def @ahoppen @bnbarham @hamishknight @rintaro +/include/swift/AST/DiagnosticsParse.def @bnbarham @CodaFi @DougGregor @hamishknight @rintaro +/include/swift/AST/DiagnosticsRefactoring.def @bnbarham @hamishknight @rintaro /include/swift/AST/DiagnosticsSIL.def @jckarter /include/swift/AST/Evaluator* @CodaFi @slavapestov /include/swift/Basic/ @DougGregor @@ -94,21 +95,21 @@ /include/swift/DependencyScan @artemcm @cachemeifyoucan /include/swift/Driver*/ @artemcm /include/swift/Frontend*/ @artemcm @tshortli -/include/swift/IDE/ @ahoppen @bnbarham @hamishknight @rintaro +/include/swift/IDE/ @bnbarham @hamishknight @rintaro /include/swift/IRGen/ @rjmccall -/include/swift/Index/ @ahoppen @bnbarham @hamishknight @rintaro +/include/swift/Index/ @bnbarham @hamishknight @rintaro /include/swift/Markup/ @nkcsgexi /include/swift/Migrator/ @nkcsgexi /include/swift/Option/*Options* @tshortli -/include/swift/Parse/ @ahoppen @bnbarham @CodaFi @DougGregor @hamishknight @rintaro +/include/swift/Parse/ @bnbarham @CodaFi @DougGregor @hamishknight @rintaro /include/swift/PrintAsClang @zoecarver @egorzhdan @Xazax-hun @j-hui @fahadnayyar @susmonteiro -/include/swift/Refactoring @ahoppen @bnbarham @hamishknight @rintaro +/include/swift/Refactoring @bnbarham @hamishknight @rintaro /include/swift/Runtime/ @rjmccall @compnerd /include/swift/SIL/ @jckarter -/include/swift/SIL/*Coverage* @ahoppen @bnbarham @hamishknight @rintaro +/include/swift/SIL/*Coverage* @bnbarham @hamishknight @rintaro /include/swift/SIL/*DebugInfo* @adrian-prantl /include/swift/SIL/SILDebug* @adrian-prantl -/include/swift/SIL/SILProfiler.h @ahoppen @bnbarham @hamishknight @rintaro +/include/swift/SIL/SILProfiler.h @bnbarham @hamishknight @rintaro /include/swift/SILOptimizer/ @eeckstein /include/swift/SILOptimizer/Utils/Distributed* @ktoso /include/swift/Sema/ @hborla @slavapestov @xedin @@ -133,7 +134,7 @@ /lib/AST/Evaluator* @CodaFi @slavapestov /lib/AST/ModuleLoader.cpp @artemcm /lib/AST/RequirementMachine/ @slavapestov -/lib/ASTGen/ @ahoppen @bnbarham @CodaFi @hamishknight @rintaro +/lib/ASTGen/ @bnbarham @CodaFi @hamishknight @rintaro /lib/Basic/ @DougGregor /lib/Basic/Windows @compnerd /lib/ClangImporter @zoecarver @egorzhdan @beccadax @ian-twilightcoder @Xazax-hun @j-hui @fahadnayyar @susmonteiro @@ -145,32 +146,34 @@ /lib/DriverTool/sil_opt* @eeckstein /lib/DriverTool/swift_symbolgraph_extract_main.cpp @QuietMisdreavus /lib/Frontend*/ @artemcm @tshortli -/lib/IDE/ @ahoppen @bnbarham @hamishknight @rintaro -/lib/IDETool/ @ahoppen @bnbarham @hamishknight @rintaro +/lib/IDE/ @bnbarham @hamishknight @rintaro +/lib/IDETool/ @bnbarham @hamishknight @rintaro /lib/IRGen/ @rjmccall -/lib/IRGen/*Coverage* @ahoppen @bnbarham @hamishknight @rintaro +/lib/IRGen/*Coverage* @bnbarham @hamishknight @rintaro /lib/IRGen/*Debug* @adrian-prantl /lib/IRGen/*Distributed* @ktoso -/lib/Index/ @ahoppen @bnbarham @hamishknight @rintaro +/lib/Index/ @bnbarham @hamishknight @rintaro /lib/Macros/Sources/SwiftMacros/Swiftify* @hnrklssn @Xazax-hun /lib/Markup/ @nkcsgexi /lib/Migrator/ @nkcsgexi -/lib/Parse/ @ahoppen @bnbarham @CodaFi @DougGregor @hamishknight @rintaro +/lib/Parse/ @bnbarham @CodaFi @DougGregor @hamishknight @rintaro /lib/PrintAsClang @zoecarver @egorzhdan @Xazax-hun @j-hui @fahadnayyar @susmonteiro -/lib/Refactoring/ @ahoppen @bnbarham @hamishknight @rintaro +/lib/Refactoring/ @bnbarham @hamishknight @rintaro /lib/SIL/ @jckarter /lib/SIL/**/*DebugInfo* @adrian-prantl -/lib/SIL/IR/*Coverage* @ahoppen @bnbarham @hamishknight @rintaro +/lib/SIL/IR/*Coverage* @bnbarham @hamishknight @rintaro /lib/SIL/IR/SILDebug* @adrian-prantl /lib/SIL/IR/SILLocation* @adrian-prantl -/lib/SIL/IR/SILProfiler.cpp @ahoppen @bnbarham @hamishknight @rintaro -/lib/SILGen/ @jckarter +/lib/SIL/IR/SILProfiler.cpp @bnbarham @hamishknight @rintaro +/lib/SILGen/ @jckarter @kavon +/lib/SILGen/*Availability* @tshortli /lib/SILGen/*Distributed* @ktoso /lib/SILOptimizer/ @eeckstein /lib/SILOptimizer/**/*DebugInfo* @adrian-prantl /lib/SILOptimizer/Mandatory/ConsumeOperator* @kavon /lib/SILOptimizer/Mandatory/FlowIsolation.cpp @kavon /lib/SILOptimizer/Mandatory/MoveOnly* @kavon +/lib/SILOptimizer/Mandatory/AddressLowering* @kavon /lib/SILOptimizer/Utils/Distributed* @ktoso /lib/Sema/ @hborla @slavapestov @xedin /lib/Sema/*Availability* @tshortli @@ -195,11 +198,13 @@ # stdlib /stdlib/ @swiftlang/standard-librarians /stdlib/private/*Runtime*/ @rjmccall +/stdlib/private/DifferentiationUnittest/ @asl /stdlib/private/SwiftReflectionTest/ @slavapestov /stdlib/public/core/Swiftify* @hnrklssn @Xazax-hun /stdlib/public/*Demangl*/ @rjmccall /stdlib/public/Concurrency/ @ktoso /stdlib/public/Cxx/ @zoecarver @egorzhdan @Xazax-hun @j-hui @fahadnayyar @susmonteiro +/stdlib/public/Differentiation/ @asl /stdlib/public/Distributed/ @ktoso /stdlib/public/Observation/ @phausler /stdlib/public/RuntimeModule/ @al45tair @mikeash @@ -212,7 +217,9 @@ # test /test/*Demangl*/ @rjmccall -/test/ASTGen/ @ahoppen @bnbarham @CodaFi @hamishknight @rintaro +/test/Availability/ @tshortli +/test/ASTGen/ @bnbarham @CodaFi @hamishknight @rintaro +/test/AutoDiff/ @asl /test/Concurrency/ @ktoso /test/Constraints/ @hborla @xedin /test/DebugInfo/ @adrian-prantl @@ -222,14 +229,14 @@ /test/Frontend/ @artemcm @tshortli /test/Generics/ @hborla @slavapestov /test/Generics/inverse* @kavon -/test/IDE/ @ahoppen @bnbarham @hamishknight @rintaro -/test/IRGen/ @rjmccall -/test/Index/ @ahoppen @bnbarham @hamishknight @rintaro +/test/IDE/ @bnbarham @hamishknight @rintaro +/test/IRGen/ @AnthonyLatsis @rjmccall +/test/Index/ @bnbarham @hamishknight @rintaro /test/Interop/ @zoecarver @egorzhdan @Xazax-hun @j-hui @fahadnayyar @susmonteiro @hnrklssn /test/Macros/SwiftifyImport @hnrklssn @Xazax-hun /test/Migrator/ @nkcsgexi -/test/Parse/ @ahoppen @bnbarham @CodaFi @DougGregor @hamishknight @rintaro -/test/Profiler @ahoppen @bnbarham @hamishknight @rintaro +/test/Parse/ @bnbarham @CodaFi @DougGregor @hamishknight @rintaro +/test/Profiler @bnbarham @hamishknight @rintaro /test/Reflection/ @slavapestov /test/Runtime/ @rjmccall /test/SIL/ @jckarter @@ -241,7 +248,7 @@ /test/Sema/ @hborla @slavapestov @xedin /test/Sema/moveonly* @kavon /test/Serialization/ @xymus -/test/SourceKit/ @ahoppen @bnbarham @hamishknight @rintaro +/test/SourceKit/ @bnbarham @hamishknight @rintaro /test/SymbolGraph/ @QuietMisdreavus /test/abi/ @swiftlang/standard-librarians /test/decl/ @hborla @slavapestov @@ -249,7 +256,7 @@ # FIXME: This file could have a dedicated directory. /test/decl/protocol/special/DistributedActor.swift @ktoso /test/expr/ @hborla @slavapestov @xedin -/test/refactoring/ @ahoppen @bnbarham @hamishknight @rintaro +/test/refactoring/ @bnbarham @hamishknight @rintaro /test/sil* @jckarter /test/sil-opt* @eeckstein /test/stdlib/ @swiftlang/standard-librarians @@ -259,13 +266,13 @@ # tools # TODO: /tools /tools/*reflection/ @slavapestov -/tools/SourceKit @ahoppen @bnbarham @hamishknight @rintaro +/tools/SourceKit @bnbarham @hamishknight @rintaro /tools/driver/ @artemcm /tools/lldb-moduleimport-test/ @adrian-prantl /tools/swift-demangle* @rjmccall -/tools/swift-ide-test @ahoppen @bnbarham @hamishknight @rintaro +/tools/swift-ide-test @bnbarham @hamishknight @rintaro /tools/swift-inspect @mikeash @al45tair @compnerd -/tools/swift-refactor @ahoppen @bnbarham @hamishknight @rintaro +/tools/swift-refactor @bnbarham @hamishknight @rintaro # unittests /unittests/*Demangl*/ @rjmccall @@ -273,11 +280,11 @@ /unittests/AST/*Evaluator* @CodaFi @slavapestov /unittests/DependencyScan/ @artemcm @cachemeifyoucan /unittests/Frontend*/ @artemcm @tshortli -/unittests/Parse/ @ahoppen @bnbarham @CodaFi @DougGregor @hamishknight @rintaro +/unittests/Parse/ @bnbarham @CodaFi @DougGregor @hamishknight @rintaro /unittests/Reflection/ @slavapestov /unittests/SIL/ @jckarter /unittests/Sema/ @hborla @xedin -/unittests/SourceKit/ @ahoppen @bnbarham @rintaro @hamishknight +/unittests/SourceKit/ @bnbarham @rintaro @hamishknight /unittests/runtime/ @rjmccall # userdocs @@ -285,24 +292,36 @@ # utils /utils/*windows* @compnerd +/utils/availability-macros.def @swiftlang/standard-librarians +/utils/build.ps1 @compnerd +/utils/build_swift/ @etcwilde @justice-adams-apple @shahmishal /utils/generate-xcode @hamishknight -/utils/gyb_sourcekit_support/ @ahoppen @bnbarham @hamishknight @rintaro -/utils/sourcekit_fuzzer/ @ahoppen @bnbarham @hamishknight @rintaro +/utils/gen-unicode-data @swiftlang/standard-librarians +/utils/gyb @swiftlang/standard-librarians +/utils/gyb_foundation_support.py @swiftlang/standard-librarians +/utils/gyb_sourcekit_support/ @bnbarham @hamishknight @rintaro +/utils/gyb_stdlib_support.py @swiftlang/standard-librarians +/utils/gyb.py @swiftlang/standard-librarians +/utils/sourcekit_fuzzer/ @bnbarham @hamishknight @rintaro /utils/swift-xcodegen/ @hamishknight -/utils/swift_build_support/products/earlyswiftsyntax.py @ahoppen @bnbarham @hamishknight @rintaro -/utils/swift_build_support/products/skstresstester.py @ahoppen @bnbarham @hamishknight @rintaro -/utils/swift_build_support/products/sourcekitlsp.py @ahoppen @bnbarham @hamishknight @rintaro -/utils/swift_build_support/products/swiftformat.py @ahoppen @allevato @bnbarham @hamishknight @rintaro -/utils/swift_build_support/products/swiftsyntax.py @ahoppen @bnbarham @hamishknight @rintaro -/utils/update-checkout* @shahmishal -/utils/update_checkout/ @shahmishal +/utils/swift_build_support/ @etcwilde @justice-adams-apple @shahmishal +/utils/swift_build_support/products/earlyswiftsyntax.py @bnbarham @hamishknight @rintaro +/utils/swift_build_support/products/skstresstester.py @bnbarham @hamishknight @rintaro +/utils/swift_build_support/products/sourcekitlsp.py @bnbarham @hamishknight @rintaro +/utils/swift_build_support/products/swiftformat.py @allevato @bnbarham @hamishknight @rintaro +/utils/swift_build_support/products/swiftsyntax.py @bnbarham @hamishknight @rintaro +/utils/swift_build_support/products/wasm* @bnbarham @MaxDesiatov @kateinoigakukun +/utils/swift_build_support/products/wasi* @bnbarham @MaxDesiatov @kateinoigakukun +/utils/update-checkout* @etcwilde @justice-adams-apple @shahmishal +/utils/update_checkout/ @etcwilde @justice-adams-apple @shahmishal +/utils/update_checkout/update-checkout-config.json @shahmishal /utils/vim/ @compnerd # validation-test /validation-test/Driver/ @artemcm -/validation-test/IDE/ @ahoppen @bnbarham @rintaro @hamishknight +/validation-test/IDE/ @bnbarham @rintaro @hamishknight /validation-test/IRGen/ @rjmccall -/validation-test/Parse/ @ahoppen @bnbarham @CodaFi @DougGregor @hamishknight @rintaro +/validation-test/Parse/ @bnbarham @CodaFi @DougGregor @hamishknight @rintaro /validation-test/Reflection/ @slavapestov /validation-test/Runtime/ @rjmccall /validation-test/SIL/ @jckarter diff --git a/.github/ISSUE_TEMPLATE/task.yml b/.github/ISSUE_TEMPLATE/task.yml index c31a5e9b13306..64dd31ae65c95 100644 --- a/.github/ISSUE_TEMPLATE/task.yml +++ b/.github/ISSUE_TEMPLATE/task.yml @@ -18,7 +18,7 @@ body: This repository hosts the Swift compiler, the Swift standard library, the Swift runtime, SourceKit, and IDE support for the Swift language. It does *not* track feedback on Xcode and other closed source Apple - deeloper software such as SwiftUI and UIKit; please direct it to + developer software such as SwiftUI and UIKit; please direct it to [Feedback Assistant](https://developer.apple.com/bug-reporting) instead. - type: textarea diff --git a/.gitignore b/.gitignore index bd9bbcfcec8cb..f4cb9086f296c 100644 --- a/.gitignore +++ b/.gitignore @@ -89,6 +89,8 @@ Runtimes/**/*.cpp Runtimes/**/*.c Runtimes/**/*.m Runtimes/**/*.mm +Runtimes/**/*.S +Runtimes/**/*.asm Runtimes/**/*.def Runtimes/**/*.gyb Runtimes/**/*.apinotes @@ -97,3 +99,5 @@ Runtimes/**/*.inc Runtimes/**/*.json Runtimes/**/*.modulemap Runtimes/**/*.in +!Runtimes/**/*.cmake.in +!Runtimes/**/CMakeConfig.h.in diff --git a/Brewfile b/Brewfile index d965eb21aaefa..a3dbeb5aa8f4c 100644 --- a/Brewfile +++ b/Brewfile @@ -1,3 +1,2 @@ -brew "cmake" brew "ninja" brew "sccache" diff --git a/CHANGELOG.md b/CHANGELOG.md index d2bb92dd34743..7cc63bc9baf08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,195 @@ > [!NOTE] > This is in reverse chronological order, so newer entries are added to the top. +## Swift (next) + +* Concurrency-related APIs like `Task` and string-processing-related APIs like `Regex` can now be qualified by the name + `Swift`, just like other standard library APIs: + + ```swift + Swift.Task { ... } + func match(_ regex: Swift.Regex<(Substring)>) { ... } + ``` + + The old `_Concurrency` and `_StringProcessing` names are still supported for backwards compatibility, and Embedded + Swift projects must still explicitly `import _Concurrency` to access concurrency APIs. + ## Swift 6.2 +* [SE-0472][]: + Introduced new `Task.immediate` and `taskGroup.addImmediateTask` APIs, which allow a task to run "immediately" in the + calling context if its isolation is compatible with the enclosing one. This can be used to create tasks which execute + without additional scheduling overhead, and allow for finer-grained control over where a task begins running. + + The canonical example for using this new API is using an unstructured immediate task like this: + + ```swift + func synchronous() { // synchronous function + // executor / thread: "T1" + let task: Task = Task.immediate { + // executor / thread: "T1" + guard keepRunning() else { return } // synchronous call (1) + + // executor / thread: "T1" + await noSuspension() // potential suspension point #1 // (2) + + // executor / thread: "T1" + await suspend() // potential suspension point #2 // (3), suspend, (5) + // executor / thread: "other" + } + + // (4) continue execution + // executor / thread: "T1" + } + ``` + +* [SE-0471][]: + Actor and global actor annotated types may now declare a synchronous `isolated deinit`, which allows such deinitializer + to access actor isolated state while deinitializing the actor. This enables actor deinitializers to safely access + and shut down or close resources during an actors deinitialization, without explicitly resorting to unstructured + concurrency tasks. + + ```swift + class NonSendableAhmed { + var state: Int = 0 + } + + @MainActor + class Maria { + let friend: NonSendableAhmed + + init() { + self.friend = NonSendableAhmed() + } + + init(sharingFriendOf otherMaria: Maria) { + // While the friend is non-Sendable, this initializer and + // and the otherMaria are isolated to the MainActor. That is, + // they share the same executor. So, it's OK for the non-Sendable value + // to cross between otherMaria and self. + self.friend = otherMaria.friend + } + + isolated deinit { + // Used to be a potential data race. Now, deinit is also + // isolated on the MainActor, so this code is perfectly + // correct. + friend.state += 1 + } + } + + func example() async { + let m1 = await Maria() + let m2 = await Maria(sharingFriendOf: m1) + doSomething(m1, m2) + } + ``` + +* [SE-0469][]: + Swift concurrency tasks (both unstructured and structured, via the TaskGroup `addTask` APIs) may now be given + human-readable names, which can be used to support debugging and identifying tasks. + + ```swift + let getUsers = Task("Get Users for \(accountID)") { + await users.get(accountID) + } + ``` + +* [SE-0462][]: + Task priority escalation may now be explicitly caused to a `Task`, as well as reacted to using the new task priority escalation handlers: + + ```swift + // priority: low + // priority: high! + await withTaskPriorityEscalationHandler { + await work() + } onPriorityEscalated: { newPriority in // may not be triggered if ->high escalation happened before handler was installed + // do something + } + ``` +* [SE-0461][]: + Nonisolated asynchronous functions may now execute on the calling actor, when the upcoming feature `NonisolatedNonsendingByDefault` + is enabled, or when explicitly opted-into using the `nonisolated(nonsending)` keywords. This allows for fine grained control + over where nonisolated asynchronous functions execute, and allows for the default behavior of their execution to be changed + from always executing on the global concurrent pool, to the calling actor, which can yield noticeable performance improvements + thanks to less executor hopping when nonisolated and isolated code is invoked in sequence. + + This also allows for safely using asynchronous functions on non-sendable types from actors, like so: + + ```swift + class NotSendable { + func performSync() { ... } + + nonisolated(nonsending) + func performAsync() async { ... } + } + + actor MyActor { + let x: NotSendable + + func call() async { + x.performSync() // okay + + await x.performAsync() // okay + } + } + ``` + +* The Swift compiler no longer diagnoses references to declarations that are + potentially unavailable because the platform version might not be new enough + when those references occur inside of contexts that are also unavailable to + that platform. This addresses a long-standing nuisance for multi-platform + code. However, there is also a chance that existing source code may become + ambiguous as a result: + + ```swift + struct A {} + struct B {} + + func potentiallyAmbiguous(_: A) {} + + @available(macOS 99, *) + func potentiallyAmbiguous(_: B) {} + + @available(macOS, unavailable) + func unavailableOnMacOS() { + potentiallyAmbiguous(.init()) // error: ambiguous use of 'init()' + } + ``` + + Code that is now ambiguous as a result should likely be restructured since + disambiguation based on platform introduction alone has never been a reliable + strategy, given that the code would eventually become ambiguous anyways when + the deployment target is raised. + +* [SE-0470][]: + A protocol conformance can be isolated to a specific global actor, meaning that the conformance can only be used by code running on that actor. Isolated conformances are expressed by specifying the global actor on the conformance itself: + + ```swift + protocol P { + func f() + } + + @MainActor + class MyType: @MainActor P { + /*@MainActor*/ func f() { + // must be called on the main actor + } + } + ``` + + Swift will produce diagnostics if the conformance is directly accessed in code that isn't guaranteed to execute in the same global actor. For example: + + ```swift + func acceptP(_ value: T) { } + + /*nonisolated*/ func useIsolatedConformance(myType: MyType) { + acceptP(myType) // error: main actor-isolated conformance of 'MyType' to 'P' cannot be used in nonisolated context + } + ``` + + To address such issues, only use an isolated conformance from code that executes on the same global actor. + * [SE-0419][]: Introduced the new `Runtime` module, which contains a public API that can generate backtraces, presently supported on macOS and Linux. Capturing a @@ -392,7 +579,7 @@ And the module structure to support such applications looks like this: * [SE-0430][]: - Region Based Isolation is now extended to enable the application of an + Region-Based Isolation is now extended to enable the application of an explicit `sending` annotation to function parameters and results. A function parameter or result that is annotated with `sending` is required to be disconnected at the function boundary and thus possesses the capability of @@ -430,7 +617,7 @@ And the module structure to support such applications looks like this: func useValue() { let x = NonSendableType() - let a = await MyActor(x) // Error without Region Based Isolation! + let a = await MyActor(x) // Error without Region-Based Isolation! } ``` @@ -10732,6 +10919,12 @@ using the `.dynamicType` member to retrieve the type of an expression should mig [SE-0442]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md [SE-0444]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md [SE-0458]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0458-strict-memory-safety.md +[SE-0461]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0461-async-function-isolation.md +[SE-0462]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0462-task-priority-escalation-apis.md +[SE-0469]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0469-task-names.md +[SE-0470]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0470-isolated-conformances.md +[SE-0471]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0371-isolated-synchronous-deinit.md +[SE-0472]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0472-task-start-synchronously-on-caller-context.md [#64927]: [#42697]: [#42728]: diff --git a/CMakeLists.txt b/CMakeLists.txt index fa5cf4d0661a9..9eb5c044bf96f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -336,6 +336,8 @@ set(SWIFT_COMPILER_VERSION "" CACHE STRING "The internal version of the Swift compiler") set(CLANG_COMPILER_VERSION "" CACHE STRING "The internal version of the Clang compiler") +set(SWIFT_TOOLCHAIN_VERSION "" CACHE STRING + "The Swift compiler tag") option(SWIFT_DISABLE_DEAD_STRIPPING "Turn off Darwin-specific dead stripping for Swift host tools." FALSE) @@ -462,6 +464,10 @@ option(SWIFT_STDLIB_ASSERTIONS "Enable internal checks for the Swift standard library (useful for debugging the library itself, does not affect checks required for safety)" "${SWIFT_STDLIB_ASSERTIONS_default}") +option(SWIFT_STDLIB_ENABLE_STRICT_AVAILABILITY + "Enable strict availability; this will cause things to break at desk or in CI if the host OS is a lower version than some `@availability` annotations in the runtime code" + FALSE) + option(SWIFT_BUILD_RUNTIME_WITH_HOST_COMPILER "Use the host compiler and not the internal clang to build the swift runtime" FALSE) @@ -624,6 +630,17 @@ set(COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_WATCHOS "2.0") set(COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_XROS "1.0") set(COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_MACCATALYST "13.1") +set(SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_OSX "${SWIFT_DARWIN_DEPLOYMENT_VERSION_OSX}" CACHE STRING + "Deployment target version for building macOS tests") +set(SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_IOS "${SWIFT_DARWIN_DEPLOYMENT_VERSION_IOS}" CACHE STRING + "Deployment target version for building iOS tests") +set(SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_TVOS "${SWIFT_DARWIN_DEPLOYMENT_VERSION_TVOS}" CACHE STRING + "Deployment target version for building tvOS tests") +set(SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_WATCHOS "${SWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS}" CACHE STRING + "Deployment target version for building watchOS tests") +set(SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_XROS "${SWIFT_DARWIN_DEPLOYMENT_VERSION_XROS}" CACHE STRING + "Deployment target version for building visionOS tests") + # # User-configurable debugging options. # @@ -818,6 +835,9 @@ elseif(UNIX) include(UnixCompileRules) endif() +# Add any extra C++ compilation options that were passed down. +add_compile_options($<$:${SWIFT_EXTRA_CXX_FLAGS}>) + if(CMAKE_C_COMPILER_ID MATCHES Clang) add_compile_options($<$,$>:-Werror=gnu>) endif() @@ -828,6 +848,19 @@ if(CMAKE_C_COMPILER_ID MATCHES Clang) add_compile_options($<$:-Werror=c++98-compat-extra-semi>) endif() +if(CMAKE_CXX_COMPILER_ID MATCHES Clang) + include(CheckCXXCompilerFlag) + # Check for '-fsized-deallocation', which we need in IRGen. Clang presumably + # adds this flag as a requirement for C++14+ to avoid a potential source + # compatibility issue with C++11 where the 2-parameter `operator delete` was + # used for placement deletion. + check_cxx_compiler_flag("-fsized-deallocation" + CXX_SUPPORTS_FSIZED_DEALLOCATION) + if(CXX_SUPPORTS_FSIZED_DEALLOCATION) + add_compile_options($<$:-fsized-deallocation>) + endif() +endif() + option(SWIFT_BUILD_SWIFT_SYNTAX "Enable building swift syntax" FALSE) @@ -868,12 +901,7 @@ include(CMakePushCheckState) # Print out path and version of any installed commands message(STATUS "CMake (${CMAKE_COMMAND}) Version: ${CMAKE_VERSION}") -if(XCODE) - set(version_flag -version) -else() - set(version_flag --version) -endif() -execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} ${version_flag} +execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} --version OUTPUT_VARIABLE _CMAKE_MAKE_PROGRAM_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "CMake Make Program (${CMAKE_MAKE_PROGRAM}) Version: ${_CMAKE_MAKE_PROGRAM_VERSION}") @@ -1070,6 +1098,8 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(SWIFT_USE_LINKER_default "") elseif(DISTRO_NAME STREQUAL "Amazon Linux 2023") set(SWIFT_USE_LINKER_default "lld") +elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(SWIFT_USE_LINKER_default "lld") else() get_gold_version(gold_version) if(NOT gold_version) @@ -1141,14 +1171,6 @@ endif() # Configure SDKs. # -if(XCODE) - # FIXME: It used to be the case that Xcode would force - # -m${platform}-version-min flags that would conflict with those computed - # by build-script. version-min flags are deprecated in favor of -target since - # clang-11, so we might be able to undo this. - set(SWIFT_SDKS "OSX") -endif() - # FIXME: the parameters we specify in SWIFT_SDKS are lacking architecture specifics, # so we need to hard-code it. For example, the SDK for Android is just 'ANDROID', # and we have to specify SWIFT_SDK_ANDROID_ARCHITECTURES separately. @@ -1404,8 +1426,9 @@ endif() if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_SDK_OVERLAY) message(STATUS "Building Swift standard library and overlays for SDKs: ${SWIFT_SDKS}") - message(STATUS " Build type: ${SWIFT_STDLIB_BUILD_TYPE}") - message(STATUS " Assertions: ${SWIFT_STDLIB_ASSERTIONS}") + message(STATUS " Build type: ${SWIFT_STDLIB_BUILD_TYPE}") + message(STATUS " Assertions: ${SWIFT_STDLIB_ASSERTIONS}") + message(STATUS " Strict availability: ${SWIFT_STDLIB_ENABLE_STRICT_AVAILABILITY}") message(STATUS "") message(STATUS "Building Swift runtime with:") @@ -1590,22 +1613,6 @@ swift_install_in_component(FILES "LICENSE.txt" DESTINATION "share/swift" COMPONENT license) -# Add a documentation target so that documentation shows up in the -# Xcode project. -if(XCODE) - add_custom_target(Documentation - SOURCES - README.md - docs) - - file(GLOB SWIFT_TOPLEVEL_HEADERS - ${CMAKE_CURRENT_SOURCE_DIR}/include/swift${dir}/*.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/swift${dir}/*.td - ${CMAKE_CURRENT_SOURCE_DIR}/include/swift${dir}/*.def) - add_custom_target(Miscellaneous - SOURCES ${SWIFT_TOPLEVEL_HEADERS}) -endif() - # New standard library build option(SWIFT_ENABLE_NEW_RUNTIME_BUILD "Build Swift runtimes with new build system" OFF) if(SWIFT_ENABLE_NEW_RUNTIME_BUILD) @@ -1633,6 +1640,12 @@ if(SWIFT_ENABLE_NEW_RUNTIME_BUILD) set(stdlib_deployment_version_flag -DCMAKE_OSX_DEPLOYMENT_TARGET=${SWIFT_SDK_${sdk}_DEPLOYMENT_VERSION}) endif() + if(sdk STREQUAL "OSX" AND SWIFT_SDK_${sdk}_DEPLOYMENT_VERSION VERSION_LESS "10.15") + set(build_concurrency NO) + else() + set(build_concurrency YES) + endif() + ExternalProject_Add("${stdlib_target}-core" SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Runtimes/Core" # TODO: Add this once we're ready to start swapping out the libraries @@ -1640,6 +1653,8 @@ if(SWIFT_ENABLE_NEW_RUNTIME_BUILD) # INSTALL_DIR "${CMAKE_BINARY_DIR}/" DEPENDS PopulateRuntimeSourceDir + # To ensure incremental builds work as expected + BUILD_ALWAYS 1 CMAKE_ARGS -DCMAKE_INSTALL_LIBDIR:FILEPATH=lib # Compiler will see mismatched swift modules and fail initial checks @@ -1658,19 +1673,20 @@ if(SWIFT_ENABLE_NEW_RUNTIME_BUILD) -DCMAKE_COLOR_DIAGNOSTICS:BOOLEAN=${CMAKE_COLOR_DIAGNOSTICS} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DSwiftCore_INSTALL_NESTED_SUBDIR=YES - -DSwiftCore_ENABLE_CONCURRENCY=YES) + -DSwiftCore_ENABLE_CONCURRENCY=${build_concurrency}) if(NOT ${CMAKE_CROSSCOMPILING}) add_dependencies("${stdlib_target}-core" swift-frontend) endif() ExternalProject_Get_Property("${stdlib_target}-core" INSTALL_DIR) - ExternalProject_Add("${stdlib_target}-StringProcessing" - SOURCE_DIR - "${CMAKE_CURRENT_SOURCE_DIR}/Runtimes/Supplemental/StringProcessing" + ExternalProject_Add("${stdlib_target}-Overlay" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Runtimes/Overlay" DEPENDS "${stdlib_target}-core" INSTALL_DIR "${INSTALL_DIR}" - INSTALL_COMMAND "" # No install story set up yet + LIST_SEPARATOR "|" + # To ensure incremental builds work as expected + BUILD_ALWAYS 1 CMAKE_ARGS -DBUILD_SHARED_LIBS=YES -DCMAKE_Swift_COMPILER_WORKS:BOOLEAN=YES @@ -1683,7 +1699,32 @@ if(SWIFT_ENABLE_NEW_RUNTIME_BUILD) -DCMAKE_C_COMPILER_TARGET:STRING=${stdlib_target_triple} -DCMAKE_CXX_COMPILER_TARGET:STRING=${stdlib_target_triple} -DCMAKE_COLOR_DIAGNOSTICS:BOOLEAN=${CMAKE_COLOR_DIAGNOSTICS} - -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}) + -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} + -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=TRUE) + + ExternalProject_Add("${stdlib_target}-Supplemental" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Runtimes/Supplemental" + DEPENDS "${stdlib_target}-core" "${stdlib_target}-Overlay" + INSTALL_DIR "${INSTALL_DIR}" + INSTALL_COMMAND "" + LIST_SEPARATOR "|" + # To ensure incremental builds work as expected + BUILD_ALWAYS 1 + CMAKE_ARGS + -DSwift_ENABLE_RUNTIMES=StringProcessing|Synchronization|Distributed|Observation + -DBUILD_SHARED_LIBS=YES + -DCMAKE_Swift_COMPILER_WORKS:BOOLEAN=YES + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX:FILEPATH=${INSTALL_DIR} + -DCMAKE_Swift_COMPILER:FILEPATH=$,${CMAKE_Swift_COMPILER},$,swiftc>> + -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} + -DCMAKE_Swift_COMPILER_TARGET:STRING=${stdlib_target_triple} + -DCMAKE_C_COMPILER_TARGET:STRING=${stdlib_target_triple} + -DCMAKE_CXX_COMPILER_TARGET:STRING=${stdlib_target_triple} + -DCMAKE_COLOR_DIAGNOSTICS:BOOLEAN=${CMAKE_COLOR_DIAGNOSTICS} + -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} + -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=TRUE) endforeach() endforeach() endif() diff --git a/README.md b/README.md index 1f79f00830ff4..aa3222fbb1b40 100644 --- a/README.md +++ b/README.md @@ -5,39 +5,20 @@ # Swift Programming Language - -| | **Architecture** | **Build** | -|---|:---:|:---:| -| **macOS** | x86_64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-macos/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-macos)| -| **Ubuntu 20.04** | x86_64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04)| -| **Ubuntu 20.04** | AArch64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04-aarch64/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04-aarch64)| -| **Ubuntu 22.04** | x86_64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-22_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-ubuntu-22_04)| -| **Ubuntu 22.04** | AArch64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-22_04-aarch64/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-ubuntu-22_04-aarch64)| -| **Ubuntu 24.04** | x86_64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-24_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-ubuntu-24_04)| -| **Ubuntu 24.04** | AArch64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-24_04-aarch64/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-ubuntu-24_04-aarch64)| -| **Amazon Linux 2** | x86_64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-amazon-linux-2/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-amazon-linux-2)| -| **Amazon Linux 2** | AArch64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-amazon-linux-2-aarch64/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-amazon-linux-2-aarch64)| -| **Universal Base Image 9** | x86_64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-ubi-9/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-ubi-9)| -| **Debian 12** | x86_64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-debian-12/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-debian-12)| -| **Debian 12** | AArch64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-debian-12-aarch64/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-debian-12-aarch64)| -| **Fedora 39** | x86_64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-fedora-39/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-fedora-39)| -| **Fedora 39** | AArch64 |[![Build Status](https://ci.swift.org/job/oss-swift-package-fedora-39-aarch64/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-package-fedora-39-aarch64)| -| **Windows 10** | x86_64 |[![Build Status](https://ci-external.swift.org/job/swift-main-windows-toolchain/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/swift-main-windows-toolchain)| -| **Windows 10** | ARM64 |[![Build Status](https://ci-external.swift.org/job/swift-main-windows-toolchain-arm64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/swift-main-windows-toolchain-arm64)| - -**Cross-Compilation Targets** - -| **Target** | **Build** | -|:---:|:---:| -| **wasm32-unknown-wasi** |[![Build Status](https://ci.swift.org/job/oss-swift-pr-test-crosscompile-wasm-ubuntu-20_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-pr-test-crosscompile-wasm-ubuntu-20_04)| - -**Swift Community-Hosted CI Platforms** - -| **OS** | **Architecture** | **Build** | -|---|:---:|:---:| -|**[Android](https://github.com/swiftlang/swift-community-hosted-continuous-integration/blob/main/nodes/x86_64_ubuntu_24_04_android.json)** | X86_64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-24.04-android-build/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-24.04-android-build)| -|**[Android](https://github.com/swiftlang/swift-community-hosted-continuous-integration/blob/main/nodes/aarch64_ubuntu_24_04_android.json)** | AArch64 |[![Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-24.04-android-arm64/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-24.04-android-arm64)| -|**[Windows 2019 (VS 2019)](https://github.com/apple/swift-community-hosted-continuous-integration/blob/main/nodes/x86_64_windows_2019_VS2019.json)** | x86_64 | [![Build Status](https://ci-external.swift.org/job/oss-swift-windows-x86_64-vs2019/lastCompletedBuild/badge/icon)](https://ci-external.swift.org/job/oss-swift-windows-x86_64-vs2019)| +| **OS** | **Status** | +|---:|:---:| +| macOS | [![macOS Universal Build Status](https://ci.swift.org/job/oss-swift-package-macos/lastCompletedBuild/badge/icon?subject=universal)](https://ci.swift.org/job/oss-swift-package-macos)| +| Ubuntu 20.04 | [![Ubuntu 20.04 x86_64 Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04/lastCompletedBuild/badge/icon?subject=x86_64)](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04) [![Ubuntu 20.04 aarch64 Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04-aarch64/lastCompletedBuild/badge/icon?subject=aarch64)](https://ci.swift.org/job/oss-swift-package-ubuntu-20_04-aarch64)| +| Ubuntu 22.04 | [![Ubuntu 22.04 x86_64 Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-22_04/lastCompletedBuild/badge/icon?subject=x86_64)](https://ci.swift.org/job/oss-swift-package-ubuntu-22_04) [![Ubuntu 22.04 aarch64 Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-22_04-aarch64/lastCompletedBuild/badge/icon?subject=aarch64)](https://ci.swift.org/job/oss-swift-package-ubuntu-22_04-aarch64)| +| Ubuntu 24.04 | [![Ubuntu 24.04 x86_64 Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-24_04/lastCompletedBuild/badge/icon?subject=x86_64)](https://ci.swift.org/job/oss-swift-package-ubuntu-24_04) [![Ubuntu 24.04 aarch64 Build Status](https://ci.swift.org/job/oss-swift-package-ubuntu-24_04-aarch64/lastCompletedBuild/badge/icon?subject=aarch64)](https://ci.swift.org/job/oss-swift-package-ubuntu-24_04-aarch64)| +| Amazon Linux 2 | [![Amazon Linux 2 x86_64 Build Status](https://ci.swift.org/job/oss-swift-package-amazon-linux-2/lastCompletedBuild/badge/icon?subject=x86_64)](https://ci.swift.org/job/oss-swift-package-amazon-linux-2) [![Amazon Linux 2 aarch64 Build Status](https://ci.swift.org/job/oss-swift-package-amazon-linux-2-aarch64/lastCompletedBuild/badge/icon?subject=aarch64)](https://ci.swift.org/job/oss-swift-package-amazon-linux-2-aarch64)| +| Debian 12 | [![Debian 12 x86_64 Build Status](https://ci.swift.org/job/oss-swift-package-debian-12/lastCompletedBuild/badge/icon?subject=x86_64)](https://ci.swift.org/job/oss-swift-package-debian-12) [![Debian 12 aarch64 Build Status](https://ci.swift.org/job/oss-swift-package-debian-12-aarch64/lastCompletedBuild/badge/icon?subject=aarch64)](https://ci.swift.org/job/oss-swift-package-debian-12-aarch64)| +| Windows 10 | [![Windows 10 x86_64 Build Status](https://ci-external.swift.org/job/swift-main-windows-toolchain/lastCompletedBuild/badge/icon?subject=x86_64)](https://ci-external.swift.org/job/swift-main-windows-toolchain) [![Windows 10 arm64 Build Status](https://ci-external.swift.org/job/swift-main-windows-toolchain-arm64/lastCompletedBuild/badge/icon?subject=arm64)](https://ci-external.swift.org/job/swift-main-windows-toolchain-arm64)| +| Universal Base Image 9 | [![Universal Base Image 9 x86_64 Build Status](https://ci.swift.org/job/oss-swift-package-ubi-9/lastCompletedBuild/badge/icon?subject=x86_64)](https://ci.swift.org/job/oss-swift-package-ubi-9)| +|**Cross-Compilation Targets**|| +| wasm32-unknown-wasi |[![wasm32-unknown-wasi Build Status](https://ci.swift.org/job/oss-swift-pr-test-crosscompile-wasm-ubuntu-20_04/lastCompletedBuild/badge/icon)](https://ci.swift.org/job/oss-swift-pr-test-crosscompile-wasm-ubuntu-20_04)| +|**Community-Hosted CI Platforms**|| +|[Android](https://github.com/swiftlang/swift-community-hosted-continuous-integration/blob/main/nodes/x86_64_ubuntu_24_04_android.json) | [![Android x86_64 Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-24.04-android-build/lastCompletedBuild/badge/icon?subject=x86_64)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-24.04-android-build) [![Android aarch64 Build Status](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-24.04-android-arm64/lastCompletedBuild/badge/icon?subject=aarch64)](https://ci-external.swift.org/job/oss-swift-RA-linux-ubuntu-24.04-android-arm64)| ## Welcome to Swift diff --git a/Runtimes/Core/CMakeLists.txt b/Runtimes/Core/CMakeLists.txt index 3f1e73c9fa84b..a6428b6b425ab 100644 --- a/Runtimes/Core/CMakeLists.txt +++ b/Runtimes/Core/CMakeLists.txt @@ -21,15 +21,16 @@ # TODO: # Platform support: -# - Work on/Verify cross-compiling -# - Work on/Verify Windows and Linux native builds +# - Work on/Verify Linux native builds # Embedded # -- -Xfrontend -emit-empty-object-file -# Catalyst Support -# -- Will need shadow invocations to generate swiftmodules for Swift parts -# Install *.abi.json, swiftdoc, and swiftsourceinfo cmake_minimum_required(VERSION 3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders set(CMAKE_C_VISIBILITY_PRESET "hidden") set(CMAKE_CXX_VISIBILITY_PRESET "hidden") @@ -38,23 +39,16 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) set(CMAKE_POSITION_INDEPENDENT_CODE YES) set(SwiftCore_CMAKE_MODULES_DIR "${CMAKE_SOURCE_DIR}/cmake/modules") -list(APPEND CMAKE_MODULE_PATH ${SwiftCore_CMAKE_MODULES_DIR}) +list(APPEND CMAKE_MODULE_PATH + ${SwiftCore_CMAKE_MODULES_DIR} + "${CMAKE_SOURCE_DIR}/../cmake/modules") include(CMakeWorkarounds) -# NOTE: always use the 3-component style as the expansion as -# `${PROJECT_VERSION}` will not extend this to the complete form and this can -# change the behaviour for comparison with non-SemVer compliant parsing. If -# possible, use the 4-version component as that is used to differentiate the -# builds of the runtime for Windows. -if($ENV{BUILD_NUMBER}) - # NOTE: SxS modules have a limit on each component being [0-65535]. - # https://learn.microsoft.com/en-us/windows/win32/sbscs/assembly-versions - math(EXPR BUILD_NUMBER "$ENV{BUILD_NUMBER} % 65535") - set(BUILD_NUMBER ".${BUILD_NUMBER}") -endif() + +include(SwiftProjectVersion) project(SwiftCore LANGUAGES C CXX Swift - VERSION 6.1.0${BUILD_NUMBER}) + VERSION ${SWIFT_RUNTIME_VERSION}) # The Swift standard library is not intended for use as a sub-library as part of # another project. It is tightly coupled with the compiler version. @@ -78,14 +72,15 @@ set(SwiftCore_VENDOR_MODULE_DIR "${SwiftCore_CMAKE_MODULES_DIR}/vendor" include(GNUInstallDirs) include(CheckSymbolExists) include(CheckIncludeFileCXX) -include(AvailabilityMacros) include(CompilerSettings) include(DefaultSettings) include(EmitSwiftInterface) +include(InstallSwiftInterface) include(PlatformInfo) include(gyb) include(ResourceEmbedding) include(CatalystSupport) +include(AvailabilityMacros) check_symbol_exists("asl_log" "asl.h" SwiftCore_HAS_ASL) check_symbol_exists("dladdr" "dlfcn.h" SwiftCore_HAS_DLADDR) @@ -103,7 +98,6 @@ defaulted_option(SwiftCore_ENABLE_OBJC_INTEROP "Enable runtime ObjC interop") defaulted_option(SwiftCore_ENABLE_TYPE_PRINTING "Enable printing type names") defaulted_option(SwiftCore_ENABLE_VECTOR_TYPES "Enable vector support") defaulted_option(SwiftCore_ENABLE_REFLECTION "Enable runtime support for mirrors and reflection support") -defaulted_option(SwiftCore_ENABLE_COMMANDLINE_SUPPORT "Enable command line argument support") defaulted_option(SwiftCore_ENABLE_RUNTIME_FUNCTION_COUNTERS "Enable runtime function counter support") defaulted_option(SwiftCore_ENABLE_STDIN "Enable functions that use stdin support") defaulted_option(SwiftCore_ENABLE_ENVIRONMENT "Enable environment variable support") @@ -114,7 +108,9 @@ defaulted_option(SwiftCore_ENABLE_COMPACT_ABSOLUTE_FUNCTION_POINTERS "Resolve ab defaulted_option(SwiftCore_ENABLE_BACKDEPLOYMENT_SUPPORT "Add symbols for runtime backdeployment") defaulted_option(SwiftCore_ENABLE_STDLIB_TRACING "Enable tracing in the runtime. Assumes the presence of os_log(3) and the os_signpost(3) API.") defaulted_option(SwiftCore_ENABLE_CONCURRENCY "Enable Concurrency runtime support") +defaulted_option(SwiftCore_ENABLE_REMOTE_MIRROR "Enable RemoteMirror runtime support") defaulted_set(SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR STRING "Default Concurrency global executor implementation") +option(SwiftCore_ENABLE_COMMANDLINE_SUPPORT "Enable command line argument support" ON) option(SwiftCore_ENABLE_UNICODE_DATA "Include unicode data in Swift runtimes" ON) option(SwiftCore_ENABLE_SHORT_MANGLING_LOOKUPS "Build with fast-path context descriptor lookups based on well-known short manglings." ON) option(SwiftCore_ENABLE_FILESYSTEM_SUPPORT "Build for systems that have a filesystem" ON) @@ -125,6 +121,7 @@ defaulted_option(SwiftCore_ENABLE_BACKTRACING "Enable backtracing runtime suppor defaulted_set(SwiftCore_BACKTRACER_PATH STRING "Set a fixed path to the Swift backtracer") defaulted_option(SwiftCore_ENABLE_FATALERROR_BACKTRACE "Build stdlib fatalError with backtrace output") defaulted_option(SwiftCore_ENABLE_PRESPECIALIZATION "Enable generic metadata prespecialization") +defaulted_option(SwiftCore_ENABLE_STRICT_AVAILABILITY "Enable strict availability; this will cause things to break at desk or in CI if the host OS is a lower version than some `@availability` annotations in the runtime code") option(SwiftCore_ENABLE_CLOBBER_FREED_OBJECTS "" OFF) option(SwiftCore_ENABLE_RUNTIME_LEAK_CHECKER "" OFF) @@ -159,7 +156,7 @@ add_compile_definitions( $<$:-DSWIFT_STDLIB_HAS_DARWIN_LIBMALLOC> # Anything that includes include/swift/Runtime/Config.h $<$:-DSWIFT_THREADING_${SwiftCore_THREADING_PACKAGE}> $<$:-DSWIFT_RUNTIME_ENABLE_LEAK_CHECKER=$> - $<$:-DSWIFT_RUNTIME_CLOBBER_FREED_OBJECTS=$>) + $<$:-DSWIFT_RUNTIME_CLOBBER_FREED_OBJECTS>) add_compile_options( $<$:-fno-rtti> @@ -174,6 +171,8 @@ add_compile_options( # frontned and `clang-cl` (and `clang`) currently do not support `/WX:nnnn`. As # a compromise, treat all linker warnings as errors. add_link_options($<$:LINKER:/WX>) +# Ensure all symbols are fully resolved on Linux +add_link_options($<$:LINKER:-z,defs>) add_compile_options( $<$:-explicit-module-build> @@ -190,7 +189,6 @@ add_compile_options( "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>" "$<$:-no-link-objc-runtime>" "$<$:SHELL:-Xfrontend -enforce-exclusivity=unchecked>" - "$<$:SHELL:-Xfrontend -enable-ossa-modules>" "$<$:SHELL:-Xfrontend -empty-abi-descriptor>" "$<$:SHELL:-Xfrontend -target-min-inlining-version -Xfrontend min>" "$<$>,$>:SHELL:-Xfrontend -disable-objc-interop>" @@ -208,14 +206,20 @@ add_subdirectory(Demangling) add_subdirectory(Threading) add_subdirectory(runtime) add_subdirectory(stubs) -add_subdirectory(CommandLineSupport) add_subdirectory(core) +if(SwiftCore_ENABLE_COMMANDLINE_SUPPORT) + add_subdirectory(CommandLineSupport) +endif() if(SwiftCore_ENABLE_ONONESUPPORT) add_subdirectory(SwiftOnoneSupport) endif() if(SwiftCore_ENABLE_CONCURRENCY) add_subdirectory(Concurrency) endif() +if(SwiftCore_ENABLE_REMOTE_MIRROR) + add_subdirectory(RemoteInspection) + add_subdirectory(SwiftRemoteMirror) +endif() # Inter-project install info export(EXPORT SwiftCoreTargets diff --git a/Runtimes/Core/CommandLineSupport/CMakeLists.txt b/Runtimes/Core/CommandLineSupport/CMakeLists.txt index fa53462384b44..df8dd70c135c7 100644 --- a/Runtimes/Core/CommandLineSupport/CMakeLists.txt +++ b/Runtimes/Core/CommandLineSupport/CMakeLists.txt @@ -1,17 +1,9 @@ -if(SwiftCore_ENABLE_COMMANDLINE_SUPPORT) - add_library(swiftCommandLineSupport STATIC CommandLine.cpp) - target_include_directories(swiftCommandLineSupport PRIVATE - "${SwiftCore_SWIFTC_SOURCE_DIR}/include" - "${PROJECT_BINARY_DIR}/include") - target_compile_definitions(swiftCommandLineSupport PUBLIC - -DSWIFT_STDLIB_HAS_COMMANDLINE) - - target_link_libraries(swiftCommandLineSupport PRIVATE - swiftShims) - - if(NOT BUILD_SHARED_LIBS) - install(TARGETS swiftCommandLineSupport - EXPORT SwiftCoreTargets - COMPONENT SwiftCore_runtime) - endif() -endif() +# TODO(etcwilde) migrate this into subdir subdirectory once the migration is +# completed. +target_sources(swiftCore PRIVATE + CommandLine.cpp) +target_compile_definitions(swiftCore PUBLIC + -DSWIFT_STDLIB_HAS_COMMANDLINE) +target_include_directories(swiftCore PRIVATE + "${SwiftCore_SWIFTC_SOURCE_DIR}/include" + "${PROJECT_BINARY_DIR}/include") diff --git a/Runtimes/Core/Concurrency/CMakeLists.txt b/Runtimes/Core/Concurrency/CMakeLists.txt index 8dd89783d7ac2..6ea008aecdd73 100644 --- a/Runtimes/Core/Concurrency/CMakeLists.txt +++ b/Runtimes/Core/Concurrency/CMakeLists.txt @@ -1,7 +1,8 @@ add_subdirectory(InternalShims) +gyb_expand(Task+init.swift.gyb Task+init.swift) gyb_expand(TaskGroup+addTask.swift.gyb TaskGroup+addTask.swift) -gyb_expand(Task+startSynchronously.swift.gyb Task+startSynchronously.swift) +gyb_expand(Task+immediate.swift.gyb Task+immediate.swift) add_library(swift_Concurrency Actor.cpp @@ -12,9 +13,9 @@ add_library(swift_Concurrency EmbeddedSupport.cpp Error.cpp ExecutorBridge.cpp + ExecutorImpl.cpp ExecutorChecks.cpp GlobalExecutor.cpp - Setup.cpp Task.cpp TaskAlloc.cpp TaskGroup.cpp @@ -23,7 +24,7 @@ add_library(swift_Concurrency ThreadingError.cpp TracingSignpost.cpp "${PROJECT_SOURCE_DIR}/CompatibilityOverride/CompatibilityOverride.cpp" - "${PROJECT_SOURCE_DIR}/linker-support/magic-symbols-for-install-name.c" + "./linker-support/magic-symbols-for-install-name.c" Actor.swift AsyncCompactMapSequence.swift AsyncDropFirstSequence.swift @@ -69,7 +70,6 @@ add_library(swift_Concurrency Deque/Deque+UnsafeHandle.swift Deque/UnsafeMutableBufferPointer+Utilities.swift DiscardingTaskGroup.swift - DummyExecutor.swift Errors.swift Executor.swift ExecutorAssertions.swift @@ -80,6 +80,8 @@ add_library(swift_Concurrency PartialAsyncTask.swift PlatformExecutorDarwin.swift PlatformExecutorLinux.swift + PlatformExecutorFreeBSD.swift + PlatformExecutorOpenBSD.swift PlatformExecutorWindows.swift PriorityQueue.swift SourceCompatibilityShims.swift @@ -89,15 +91,23 @@ add_library(swift_Concurrency Task+TaskExecutor.swift TaskCancellation.swift TaskGroup.swift - TaskGroup+Embedded.swift TaskLocal.swift TaskSleep.swift TaskSleepDuration.swift + UnimplementedExecutor.swift + "${CMAKE_CURRENT_BINARY_DIR}/Task+init.swift" "${CMAKE_CURRENT_BINARY_DIR}/TaskGroup+addTask.swift" - "${CMAKE_CURRENT_BINARY_DIR}/Task+startSynchronously.swift") + "${CMAKE_CURRENT_BINARY_DIR}/Task+immediate.swift") + +if(APPLE) + target_sources(swift_Concurrency PRIVATE + CFExecutor.swift + CFExecutor.cpp) +endif() include(${SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR}.cmake) target_compile_definitions(swift_Concurrency PRIVATE + $<$:-DSWIFT_RUNTIME> $<$:-DSWIFT_TARGET_LIBRARY_NAME=swift_Concurrency> # NOTE: VS2017 <15.8 would round clamp alignment to alignof(max_align_t) which # was non-conformant. Indicate that we wish to use extended alignment. @@ -108,6 +118,7 @@ target_compile_options(swift_Concurrency PRIVATE # NOTE: do not remove until `IsolatedAny` is on by default in all supported # compilers. "$<$:SHELL:-enable-experimental-feature IsolatedAny>" + "$<$:SHELL:-enable-experimental-feature Extern>" # NOTE: enable the async frame pointer on Darwin to faciliate debugging. $<$,$>:-fswift-async-fp=always> "$<$,$>:SHELL:-Xfrontend -swift-async-frame-pointer=always>" @@ -118,10 +129,14 @@ target_include_directories(swift_Concurrency PRIVATE # FIXME: grant access to `runtime/CMakeConfig.h` which should be available # through the swiftRuntime target. "${PROJECT_BINARY_DIR}/include") +# FIXME: Why is this not implicitly in the interface flags? +target_include_directories(swift_Concurrency INTERFACE + "$<$:$$/${SwiftCore_INSTALL_SWIFTMODULEDIR}>>") target_link_libraries(swift_Concurrency PRIVATE swiftShims swiftConcurrencyInternalShims $<$:swiftThreading> + $<$:log> $<$:Synchronization> $<$:mincore> # Link to the runtime that we are just building. @@ -129,6 +144,10 @@ target_link_libraries(swift_Concurrency PRIVATE set_target_properties(swift_Concurrency PROPERTIES Swift_MODULE_NAME _Concurrency LINKER_LANGUAGE CXX) +if(NOT BUILD_SHARED_LIBS AND CMAKE_STATIC_LIBRARY_PREFIX_Swift) + set_target_properties(swift_Concurrency PROPERTIES + PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX_Swift}) +endif() install(TARGETS swift_Concurrency EXPORT SwiftCoreTargets @@ -138,3 +157,7 @@ install(TARGETS swift_Concurrency RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") emit_swift_interface(swift_Concurrency) install_swift_interface(swift_Concurrency) + +# Configure plist creation for Darwin platforms. +generate_plist(swift_Concurrency "${CMAKE_PROJECT_VERSION}" swift_Concurrency) +embed_manifest(swift_Concurrency) diff --git a/Runtimes/Core/Concurrency/dispatch.cmake b/Runtimes/Core/Concurrency/dispatch.cmake index 4d04b582a3b4c..49dee81c3654d 100644 --- a/Runtimes/Core/Concurrency/dispatch.cmake +++ b/Runtimes/Core/Concurrency/dispatch.cmake @@ -1,12 +1,16 @@ find_package(dispatch QUIET REQUIRED) +check_symbol_exists("dispatch_async_swift_job" "dispatch/private.h" + SwiftConcurrency_HAS_DISPATCH_ASYNC_SWIFT_JOB) + target_sources(swift_Concurrency PRIVATE DispatchGlobalExecutor.cpp DispatchExecutor.swift CFExecutor.swift ExecutorImpl.swift) target_compile_definitions(swift_Concurrency PRIVATE + $<$:-DSwiftConcurrency_HAS_DISPATCH_ASYNC_SWIFT_JOB=1> $<$:-DSWIFT_CONCURRENCY_USES_DISPATCH=1>) target_compile_options(swift_Concurrency PRIVATE $<$:-DSWIFT_CONCURRENCY_USES_DISPATCH> diff --git a/Runtimes/Core/Demangling/CMakeLists.txt b/Runtimes/Core/Demangling/CMakeLists.txt index f246c29567366..0ade7ee08aa8f 100644 --- a/Runtimes/Core/Demangling/CMakeLists.txt +++ b/Runtimes/Core/Demangling/CMakeLists.txt @@ -21,7 +21,8 @@ target_compile_definitions(swiftDemangling PRIVATE # compiler, in order to avoid possible ODR violations if both are statically # linked into the same binary. (see also commit message for 5b1daa9055c99904c84862ecc313641fd9b26e63) target_compile_definitions(swiftDemangling PUBLIC - $<$:-DSWIFT_INLINE_NAMESPACE=__runtime>) + -DSWIFT_RUNTIME + $<$:-DSWIFT_INLINE_NAMESPACE=__runtime>) target_include_directories(swiftDemangling PRIVATE diff --git a/Runtimes/Core/LLVMSupport/CMakeLists.txt b/Runtimes/Core/LLVMSupport/CMakeLists.txt index 92e8f651da752..3374c94aa0ac2 100644 --- a/Runtimes/Core/LLVMSupport/CMakeLists.txt +++ b/Runtimes/Core/LLVMSupport/CMakeLists.txt @@ -5,9 +5,10 @@ add_library(swiftLLVMSupport OBJECT SmallPtrSet.cpp SmallVector.cpp StringRef.cpp) -target_compile_options(swiftLLVMSupport - PRIVATE - $<$:-DSWIFT_STDLIB_HAS_ASL>) +target_compile_options(swiftLLVMSupport PRIVATE + $<$:-DSWIFT_STDLIB_HAS_ASL>) +target_link_libraries(swiftLLVMSupport PRIVATE + $<$:log>) if(NOT BUILD_SHARED_LIBS) install(TARGETS swiftLLVMSupport diff --git a/Runtimes/Core/RemoteInspection/CMakeLists.txt b/Runtimes/Core/RemoteInspection/CMakeLists.txt new file mode 100644 index 0000000000000..9e2e01290958a --- /dev/null +++ b/Runtimes/Core/RemoteInspection/CMakeLists.txt @@ -0,0 +1,24 @@ + +add_library(swiftRemoteInspection OBJECT + MetadataSource.cpp + TypeLowering.cpp + TypeRef.cpp + TypeRefBuilder.cpp) +target_compile_definitions(swiftRemoteInspection PRIVATE + $<$:SWIFT_ENABLE_REFLECTION>) +target_include_directories(swiftRemoteInspection PRIVATE + "${PROJECT_BINARY_DIR}/include" + "${SwiftCore_SWIFTC_SOURCE_DIR}/include") +target_include_directories(swiftRemoteInspection PUBLIC + "$") +target_link_libraries(swiftRemoteInspection PRIVATE + swiftDemangling + $<$:swiftDemanglingCR> + swiftLLVMSupport + swiftShims) + +if(NOT BUILD_SHARED_LIBS) + install(TARGETS swiftRemoteInspection + EXPORT SwiftCoreTargets + COMPONENT SwiftCore_runtime) +endif() diff --git a/Runtimes/Core/SwiftOnoneSupport/CMakeLists.txt b/Runtimes/Core/SwiftOnoneSupport/CMakeLists.txt index 2ec1d95048f4c..41005c11d5b37 100644 --- a/Runtimes/Core/SwiftOnoneSupport/CMakeLists.txt +++ b/Runtimes/Core/SwiftOnoneSupport/CMakeLists.txt @@ -41,4 +41,5 @@ emit_swift_interface(swiftSwiftOnoneSupport) install_swift_interface(swiftSwiftOnoneSupport) # Configure plist creation for Darwin platforms. -generate_plist("${CMAKE_PROJECT_NAME}" "${CMAKE_PROJECT_VERSION}" swiftSwiftOnoneSupport) +generate_plist(swiftSwiftOnoneSupport "${CMAKE_PROJECT_VERSION}" swiftSwiftOnoneSupport) +embed_manifest(swiftSwiftOnoneSupport) diff --git a/Runtimes/Core/SwiftRemoteMirror/CMakeLists.txt b/Runtimes/Core/SwiftRemoteMirror/CMakeLists.txt new file mode 100644 index 0000000000000..1b0341e55fb3d --- /dev/null +++ b/Runtimes/Core/SwiftRemoteMirror/CMakeLists.txt @@ -0,0 +1,35 @@ + +add_library(swiftRemoteMirror + SwiftRemoteMirror.cpp) +target_compile_definitions(swiftRemoteMirror PUBLIC + $<$>:swiftRemoteMirror_STATIC>) +target_link_libraries(swiftRemoteMirror PRIVATE + swiftRemoteInspection) +target_include_directories(swiftRemoteMirror PRIVATE + "${PROJECT_BINARY_DIR}/include" + "${SwiftCore_SWIFTC_SOURCE_DIR}/include") +target_link_libraries(swiftRemoteMirror PRIVATE + # FIXME(compnerd) why is this required? These should be transitively applied + # from `swiftRemoteInspection` + swiftDemangling + $<$:swiftDemanglingCR> + swiftLLVMSupport + swiftShims) + +install(TARGETS swiftRemoteMirror + EXPORT SwiftCoreTargets + COMPONENT SwiftCore_runtime + ARCHIVE DESTINATION "${SwiftCore_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftCore_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +install(FILES + ${SwiftCore_SWIFTC_SOURCE_DIR}/include/swift/SwiftRemoteMirror/MemoryReaderInterface.h + ${SwiftCore_SWIFTC_SOURCE_DIR}/include/swift/SwiftRemoteMirror/Platform.h + ${SwiftCore_SWIFTC_SOURCE_DIR}/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h + ${SwiftCore_SWIFTC_SOURCE_DIR}/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h + ${SwiftCore_SWIFTC_SOURCE_DIR}/include/swift/SwiftRemoteMirror/module.modulemap + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/swift/SwiftRemoteMirror") + +# Configure plist creation for Darwin platforms. +generate_plist("${CMAKE_PROJECT_NAME}" "${CMAKE_PROJECT_VERSION}" swiftRemoteMirror) +embed_manifest(swiftRemoteMirror) diff --git a/Runtimes/Core/cmake/caches/Vendors/Apple/apple-common.cmake b/Runtimes/Core/cmake/caches/Vendors/Apple/apple-common.cmake index 4a921adfa6b0c..abf1ecbb46827 100644 --- a/Runtimes/Core/cmake/caches/Vendors/Apple/apple-common.cmake +++ b/Runtimes/Core/cmake/caches/Vendors/Apple/apple-common.cmake @@ -12,6 +12,8 @@ set(SwiftCore_ENABLE_VECTOR_TYPES ON CACHE BOOL "") set(SwiftCore_ENABLE_RUNTIME_FUNCTION_COUNTERS ON CACHE BOOL "") set(SwiftCore_ENABLE_BACKDEPLOYMENT_SUPPORT ON CACHE BOOL "") set(SwiftCore_ENABLE_FILESYSTEM_SUPPORT ON CACHE BOOL "") +set(SwiftCore_ENABLE_STRICT_AVAILABILITY ON CACHE BOOL "") + set(SwiftCore_OPTIMIZATION_REMARKS "bitstream" CACHE STRING "") set(SwiftCore_INSTALL_NESTED_SUBDIR OFF CACHE BOOL "") diff --git a/Runtimes/Core/cmake/interface/SwiftCoreConfig.cmake.in b/Runtimes/Core/cmake/interface/SwiftCoreConfig.cmake.in index 40a5469e08589..f6e2f73333873 100644 --- a/Runtimes/Core/cmake/interface/SwiftCoreConfig.cmake.in +++ b/Runtimes/Core/cmake/interface/SwiftCoreConfig.cmake.in @@ -4,3 +4,5 @@ include("${CMAKE_CURRENT_LIST_DIR}/SwiftCoreTargets.cmake") set(SwiftCore_ENABLE_LIBRARY_EVOLUTION @SwiftCore_ENABLE_LIBRARY_EVOLUTION@) set(SwiftCore_ENABLE_VECTOR_TYPES @SwiftCore_ENABLE_VECTOR_TYPES@) + +set(SwiftCore_ENABLE_BACKDEPLOYMENT_SUPPORT @SwiftCore_ENABLE_BACKDEPLOYMENT_SUPPORT@) diff --git a/Runtimes/Core/cmake/modules/AvailabilityMacros.cmake b/Runtimes/Core/cmake/modules/AvailabilityMacros.cmake index b1dd4920c6b80..5f3f21985ef9e 100644 --- a/Runtimes/Core/cmake/modules/AvailabilityMacros.cmake +++ b/Runtimes/Core/cmake/modules/AvailabilityMacros.cmake @@ -1,5 +1,42 @@ -file(STRINGS "${SwiftCore_SWIFTC_SOURCE_DIR}/utils/availability-macros.def" availability_defs) +configure_file("${SwiftCore_SWIFTC_SOURCE_DIR}/utils/availability-macros.def" + "${CMAKE_CURRENT_BINARY_DIR}/availability-macros.def" + COPYONLY) +file(STRINGS "${CMAKE_CURRENT_BINARY_DIR}/availability-macros.def" availability_defs) list(FILTER availability_defs EXCLUDE REGEX "^\\s*(#.*)?$") foreach(def ${availability_defs}) - add_compile_options("$<$:SHELL:-Xfrontend -define-availability -Xfrontend \"${def}\">") + list(APPEND availability_definitions "-Xfrontend -define-availability -Xfrontend \"${def}\"") + + if("${def}" MATCHES "SwiftStdlib .*") + # For each SwiftStdlib x.y, also define StdlibDeploymentTarget x.y, which, + # will expand to the current `-target` platform if the macro defines a + # newer platform as its availability. + # + # There is a setting, SwiftCore_ENABLE_STRICT_AVAILABILITY, which if set + # ON will cause us to use the "proper" availability instead. + string(REPLACE "SwiftStdlib" "StdlibDeploymentTarget" current "${def}") + if(NOT SwiftCore_ENABLE_STRICT_AVAILABILITY AND SwiftCore_SWIFT_AVAILABILITY_PLATFORM) + if("${SwiftCore_SWIFT_AVAILABILITY_PLATFORM}" STREQUAL "macOS" AND "${SwiftCore_VARIANT_AVAILABILITY_PLATFORM}" STREQUAL "iOS") + string(REGEX MATCH "iOS ([0-9]+(\.[0-9]+)+)" ios_platform_version "${def}") + string(REGEX MATCH "[0-9]+(\.[0-9]+)+" ios_version "${ios_platform_version}") + string(REGEX MATCH "macOS ([0-9]+(\.[0-9]+)+)" macos_platform_version "${def}") + string(REGEX MATCH "[0-9]+(\.[0-9]+)+" macos_version "${macos_platform_version}") + if((NOT macos_version STREQUAL "9999" OR NOT ios_version STREQUAL "9999") AND (macos_version VERSION_GREATER CMAKE_OSX_DEPLOYMENT_TARGET OR ios_version VERSION_GREATER SwiftCore_VARIANT_DEPLOYMENT_VERSION)) + string(REGEX REPLACE ":.*" ": macOS ${CMAKE_OSX_DEPLOYMENT_VERSION}, iOS ${SwiftCore_VARIANT_DEPLOYMENT_VERSION}" current "${current}") + endif() + else() + string(REGEX MATCH "${SwiftCore_SWIFT_AVAILABILITY_PLATFORM} ([0-9]+(\.[0-9]+)+)" platform_version "${def}") + string(REGEX MATCH "[0-9]+(\.[0-9]+)+" version "${platform_version}") + if(NOT version STREQUAL "9999" AND version VERSION_GREATER CMAKE_OSX_DEPLOYMENT_TARGET) + string(REGEX REPLACE ":.*" ":${SwiftCore_SWIFT_AVAILABILITY_PLATFORM} ${CMAKE_OSX_DEPLOYMENT_TARGET}" current "${current}") + endif() + endif() + endif() + list(APPEND availability_definitions "-Xfrontend -define-availability -Xfrontend \"${current}\"") + endif() endforeach() + +list(JOIN availability_definitions "\n" availability_definitions) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/availability-macros.rsp" + CONTENT "${availability_definitions}") +add_compile_options("$<$:SHELL:${CMAKE_Swift_RESPONSE_FILE_FLAG}${CMAKE_CURRENT_BINARY_DIR}/availability-macros.rsp>") diff --git a/Runtimes/Core/cmake/modules/CatalystSupport.cmake b/Runtimes/Core/cmake/modules/CatalystSupport.cmake index 7f761007414ea..3e331b81ed9b5 100644 --- a/Runtimes/Core/cmake/modules/CatalystSupport.cmake +++ b/Runtimes/Core/cmake/modules/CatalystSupport.cmake @@ -34,5 +34,20 @@ if(SwiftCore_COMPILER_VARIANT_TARGET) set(SwiftCore_VARIANT_MODULE_TRIPLE "${module_triple}" CACHE STRING "Triple used for installed swift{module,interface} files for the target variant") mark_as_advanced(SwiftCore_VARIANT_MODULE_TRIPLE) message(CONFIGURE_LOG "Swift target variant module triple: ${module_triple}") + + string(JSON triple GET "${target_info_json}" "target" "triple") + if(triple MATCHES "apple-([a-zA-Z]+)([0-9]+[.0-9]*)-macabi") + set(SwiftCore_VARIANT_DEPLOYMENT_VERSION "${CMAKE_MATCH_2}") + mark_as_advanced(SwiftCore_VARIANT_DEPLOYMENT_VERSION) + message(CONFIGURE_LOG "Swift target variant deployment version: ${SwiftCore_VARIANT_DEPLOYMENT_VERSION}") + endif() + endif() + + if(SwiftCore_EXPERIMENTAL_EMIT_VARIANT_MODULE) + check_compiler_flag(Swift "-experimental-emit-variant-module" HAVE_Swift_EMIT_VARIANT_MODULE_FLAG) + if(HAVE_Swift_EMIT_VARIANT_MODULE_FLAG) + add_compile_options( + "$<$:-experimental-emit-variant-module>") + endif() endif() endif() diff --git a/Runtimes/Core/cmake/modules/DefaultSettings.cmake b/Runtimes/Core/cmake/modules/DefaultSettings.cmake index e6fc1f5820ef0..e7a590054857f 100644 --- a/Runtimes/Core/cmake/modules/DefaultSettings.cmake +++ b/Runtimes/Core/cmake/modules/DefaultSettings.cmake @@ -6,11 +6,12 @@ # the variable with `-DSwiftCore_*` on the commandline. set(SwiftCore_ENABLE_BACKTRACING_default OFF) # TODO: enable this by default -set(SwiftCore_ENABLE_COMMANDLINE_SUPPORT_default OFF) # TODO: enable this by default set(SwiftCore_ENABLE_STDIN_default ON) set(SwiftCore_ENABLE_TYPE_PRINTING_default ON) +set(SwiftCore_ENABLE_STRICT_AVAILABILITY_default OFF) + set(SwiftCore_BACKTRACER_PATH_default "") # Provide a boolean option that a user can optionally enable. @@ -20,7 +21,7 @@ macro(defaulted_option variable helptext) if(NOT DEFINED ${variable}_default) set(${variable}_default OFF) endif() - option(${variable} ${helptext} ${${variable}_default}) + option(${variable} "${helptext}" ${${variable}_default}) endmacro() # Create a defaulted cache entry @@ -41,6 +42,7 @@ if(APPLE) set(SwiftCore_ENABLE_RUNTIME_OS_VERSIONING_default ON) set(SwiftCore_ENABLE_OVERRIDABLE_RETAIN_RELEASE_default ON) set(SwiftCore_ENABLE_CONCURRENCY_default NO) + set(SwiftCore_ENABLE_REMOTE_MIRROR_default NO) set(SwiftCore_THREADING_PACKAGE_default "DARWIN") set(SwiftCore_ENABLE_PRESPECIALIZATION_default ON) set(SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR_default "dispatch") @@ -48,9 +50,12 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "WASM") set(SwiftCore_OBJECT_FORMAT_default "elf") set(SwiftCore_THREADING_PACKAGE_default "NONE") set(SwiftCore_ENABLE_CONCURRENCY_default NO) + set(SwiftCore_ENABLE_REMOTE_MIRROR_default NO) set(SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR_default "none") elseif(LINUX OR ANDROID OR BSD) set(SwiftCore_OBJECT_FORMAT_default "elf") + + set(SwiftCore_ENABLE_REFLECTION_default ON) set(SwiftCore_ENABLE_FATALERROR_BACKTRACE_default ON) if(LINUX) set(SwiftCore_THREADING_PACKAGE_default "LINUX") @@ -59,15 +64,26 @@ elseif(LINUX OR ANDROID OR BSD) set(SwiftCore_THREADING_PACKAGE_default "PTHREADS") endif() set(SwiftCore_ENABLE_CONCURRENCY_default NO) + set(SwiftCore_ENABLE_REMOTE_MIRROR_default NO) set(SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR_default "dispatch") elseif(WIN32) set(SwiftCore_OBJECT_FORMAT_default "coff") + + set(SwiftCore_ENABLE_LIBRARY_EVOLUTION_default ${BUILD_SHARED_LIBS}) set(SwiftCore_ENABLE_REFLECTION_default ON) set(SwiftCore_ENABLE_FATALERROR_BACKTRACE_default ON) + set(SwiftCore_ENABLE_OVERRIDABLE_RETAIN_RELEASE_default ON) set(SwiftCore_ENABLE_CONCURRENCY_default NO) + set(SwiftCore_ENABLE_REMOTE_MIRROR_default NO) set(SwiftCore_THREADING_PACKAGE_default "WIN32") - set(SwiftCore_ENABLE_PRESPECIALIZATION_default ON) + # FIXME(swiftlang/swift#84780) - generic prespecialization seems to cause + # errors + set(SwiftCore_ENABLE_PRESPECIALIZATION_default OFF) set(SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR_default "dispatch") + + set(SwiftCore_ENABLE_VECTOR_TYPES_default ON) + set(SwiftCore_ENABLE_FILESYSTEM_SUPPORT_default ON) + set(SwiftCore_INSTALL_NESTED_SUBDIR_default ON) endif() include("${SwiftCore_VENDOR_MODULE_DIR}/DefaultSettings.cmake" OPTIONAL) diff --git a/Runtimes/Core/cmake/modules/DetectedPlatformInfo.cmake.in b/Runtimes/Core/cmake/modules/DetectedPlatformInfo.cmake.in new file mode 100644 index 0000000000000..7833ade0e49ba --- /dev/null +++ b/Runtimes/Core/cmake/modules/DetectedPlatformInfo.cmake.in @@ -0,0 +1,8 @@ +set(SwiftCore_SIZEOF_POINTER @SwiftCore_SIZEOF_POINTER@) +set(SwiftCore_MODULE_TRIPLE @SwiftCore_MODULE_TRIPLE@) +set(SwiftCore_PLATFORM_SUBIDR @SwiftCore_PLATFORM_SUBIDR@) +set(SwiftCore_ARCH_SUBDIR @SwiftCore_ARCH_SUBDIR@) + +set(SwiftCore_SWIFT_AVAILABILITY_PLATFORM @SwiftCore_SWIFT_AVAILABILITY_PLATFORM@) + +set(SwiftCore_PLATFORM_INFO_SET 1) diff --git a/Runtimes/Core/cmake/modules/EmitSwiftInterface.cmake b/Runtimes/Core/cmake/modules/EmitSwiftInterface.cmake index 88cf2427a68f5..fc8f9c4c3eae2 100644 --- a/Runtimes/Core/cmake/modules/EmitSwiftInterface.cmake +++ b/Runtimes/Core/cmake/modules/EmitSwiftInterface.cmake @@ -8,66 +8,61 @@ function(emit_swift_interface target) # Generate the target-variant binary swift module when performing zippered # build + # Clean this up once CMake has nested swiftmodules in the build directory: + # https://gitlab.kitware.com/cmake/cmake/-/merge_requests/10664 + # https://cmake.org/cmake/help/git-stage/policy/CMP0195.html + + # We can't expand the Swift_MODULE_NAME target property in a generator + # expression or it will fail saying that the target doesn't exist. + get_target_property(module_name ${target} Swift_MODULE_NAME) + if(NOT module_name) + set(module_name ${target}) + endif() + set(module_directory "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule") + # Account for an existing swiftmodule file + # generated with the previous logic + if(EXISTS "${module_directory}" AND NOT IS_DIRECTORY "${module_directory}") + message(STATUS "Removing regular file ${module_directory} to support nested swiftmodule generation") + file(REMOVE "${module_directory}") + endif() + target_compile_options(${target} PRIVATE + "$<$:SHELL:-emit-module-path ${module_directory}/${SwiftCore_MODULE_TRIPLE}.swiftmodule>") + set_property(TARGET "${target}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${module_directory}/${SwiftCore_MODULE_TRIPLE}.swiftmodule" + "${module_directory}/${SwiftCore_MODULE_TRIPLE}.swiftdoc" + "${module_directory}/${SwiftCore_MODULE_TRIPLE}.swiftsourceinfo") if(SwiftCore_VARIANT_MODULE_TRIPLE) - set(variant_module_tmp_dir "${CMAKE_CURRENT_BINARY_DIR}/${target}-${SwiftCore_VARIANT_MODULE_TRIPLE}") - file(MAKE_DIRECTORY "${variant_module_tmp_dir}") target_compile_options(${target} PRIVATE - "$<$:SHELL:-emit-variant-module-path ${variant_module_tmp_dir}/${target}.swiftmodule>") + "$<$:SHELL:-emit-variant-module-path ${module_directory}/${SwiftCore_VARIANT_MODULE_TRIPLE}.swiftmodule>") + set_property(TARGET "${target}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${module_directory}/${SwiftCore_VARIANT_MODULE_TRIPLE}.swiftmodule" + "${module_directory}/${SwiftCore_VARIANT_MODULE_TRIPLE}.swiftdoc" + "${module_directory}/${SwiftCore_VARIANT_MODULE_TRIPLE}.swiftsourceinfo") endif() + add_custom_command(OUTPUT "${module_directory}/${SwiftCore_MODULE_TRIPLE}.swiftmodule" + DEPENDS ${target}) + target_sources(${target} + INTERFACE + $) # Generate textual swift interfaces is library-evolution is enabled if(SwiftCore_ENABLE_LIBRARY_EVOLUTION) target_compile_options(${target} PRIVATE - $<$:-emit-module-interface-path$${CMAKE_CURRENT_BINARY_DIR}/$.swiftinterface> - $<$:-emit-private-module-interface-path$${CMAKE_CURRENT_BINARY_DIR}/$.private.swiftinterface> - $<$:-library-level$api> - $<$:-Xfrontend$-require-explicit-availability=ignore>) - - # Emit catalyst swiftmodules and interfaces - if(SwiftCore_VARIANT_MODULE_TRIPLE) - target_compile_options(${target} PRIVATE - "$<$:SHELL:-emit-variant-module-interface-path ${variant_module_tmp_dir}/${target}.swiftinterface>" - "$<$:SHELL:-emit-variant-private-module-interface-path ${variant_module_tmp_dir}/${target}.private.swiftinterface>") - endif() - endif() -endfunction() - -# Install the generated swift interface file for the target if library evolution -# is enabled. -function(install_swift_interface target) - # Install binary swift modules - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/$.swiftmodule" - RENAME "${SwiftCore_MODULE_TRIPLE}.swiftmodule" - DESTINATION "${SwiftCore_INSTALL_SWIFTMODULEDIR}/$.swiftmodule" - COMPONENT SwiftCore_development) - if(SwiftCore_VARIANT_MODULE_TRIPLE) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}-${SwiftCore_VARIANT_MODULE_TRIPLE}/${target}.swiftmodule" - RENAME "${SwiftCore_VARIANT_MODULE_TRIPLE}.swiftmodule" - DESTINATION "${SwiftCore_INSTALL_SWIFTMODULEDIR}/$.swiftmodule" - COMPONENT SwiftCore_development) - endif() - - # Install Swift interfaces if library-evolution is enabled - if(SwiftCore_ENABLE_LIBRARY_EVOLUTION) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/$.swiftinterface" - RENAME "${SwiftCore_MODULE_TRIPLE}.swiftinterface" - DESTINATION "${SwiftCore_INSTALL_SWIFTMODULEDIR}/$.swiftmodule" - COMPONENT SwiftCore_development) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/$.private.swiftinterface" - RENAME "${SwiftCore_MODULE_TRIPLE}.private.swiftinterface" - DESTINATION "${SwiftCore_INSTALL_SWIFTMODULEDIR}/$.swiftmodule" - COMPONENT SwiftCore_development) - - # Install catalyst interface files + $<$:-emit-module-interface-path$${module_directory}/${SwiftCore_MODULE_TRIPLE}.swiftinterface> + $<$:-emit-private-module-interface-path$${module_directory}/${SwiftCore_MODULE_TRIPLE}.private.swiftinterface>) + set_property(TARGET "${target}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${module_directory}/${SwiftCore_MODULE_TRIPLE}.swiftinterface" + "${module_directory}/${SwiftCore_MODULE_TRIPLE}.private.swiftinterface") if(SwiftCore_VARIANT_MODULE_TRIPLE) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}-${SwiftCore_VARIANT_MODULE_TRIPLE}/${target}.swiftinterface" - RENAME "${SwiftCore_VARIANT_MODULE_TRIPLE}.swiftinterface" - DESTINATION "${SwiftCore_INSTALL_SWIFTMODULEDIR}/$.swiftmodule" - COMPONENT SwiftCore_development) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}-${SwiftCore_VARIANT_MODULE_TRIPLE}/${target}.private.swiftinterface" - RENAME "${SwiftCore_VARIANT_MODULE_TRIPLE}.private.swiftinterface" - DESTINATION "${SwiftCore_INSTALL_SWIFTMODULEDIR}/$.swiftmodule" - COMPONENT SwiftCore_development) + target_compile_options(${target} PRIVATE + "$<$:SHELL:-emit-variant-module-interface-path ${module_directory}/${SwiftCore_VARIANT_MODULE_TRIPLE}.swiftinterface>" + "$<$:SHELL:-emit-variant-private-module-interface-path ${module_directory}/${SwiftCore_VARIANT_MODULE_TRIPLE}.private.swiftinterface>") + set_property(TARGET "${target}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${module_directory}/${SwiftCore_VARIANT_MODULE_TRIPLE}.swiftinterface" + "${module_directory}/${SwiftCore_VARIANT_MODULE_TRIPLE}.private.swiftinterface") endif() + target_compile_options(${target} PRIVATE + $<$:-library-level$api> + $<$:-Xfrontend$-require-explicit-availability=ignore>) endif() endfunction() diff --git a/Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake b/Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake index bc6b0bfd69ad1..d11fb6e8fcab3 100644 --- a/Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake +++ b/Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake @@ -4,10 +4,13 @@ add_compile_options( "$<$:SHELL:-enable-experimental-feature SE427NoInferenceOnExtension>" "$<$:SHELL:-enable-experimental-feature NonescapableTypes>" "$<$:SHELL:-enable-experimental-feature LifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature InoutLifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependenceMutableAccessors>" "$<$:SHELL:-enable-experimental-feature MemberImportVisibility>" "$<$:SHELL:-enable-experimental-feature TypedThrows>" "$<$:SHELL:-enable-experimental-feature Macros>" "$<$:SHELL:-enable-experimental-feature FreestandingMacros>" "$<$:SHELL:-enable-experimental-feature BitwiseCopyable>" "$<$:SHELL:-enable-experimental-feature Extern>" + "$<$:SHELL:-enable-experimental-feature AllowUnsafeAttribute>" "$<$:SHELL:-enable-experimental-feature ValueGenerics>") diff --git a/Runtimes/Core/cmake/modules/InstallSwiftInterface.cmake b/Runtimes/Core/cmake/modules/InstallSwiftInterface.cmake new file mode 100644 index 0000000000000..e246eba7d6a2c --- /dev/null +++ b/Runtimes/Core/cmake/modules/InstallSwiftInterface.cmake @@ -0,0 +1,13 @@ + +# Install the generated swift interface files for the target. +function(install_swift_interface target) + # Swiftmodules are already in the directory structure + get_target_property(module_name ${target} Swift_MODULE_NAME) + if(NOT module_name) + set(module_name ${target}) + endif() + + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule" + DESTINATION "${SwiftCore_INSTALL_SWIFTMODULEDIR}" + COMPONENT SwiftCore_development) +endfunction() diff --git a/Runtimes/Core/cmake/modules/PlatformInfo.cmake b/Runtimes/Core/cmake/modules/PlatformInfo.cmake index 999d6b0595f1b..49ccc06aa4759 100644 --- a/Runtimes/Core/cmake/modules/PlatformInfo.cmake +++ b/Runtimes/Core/cmake/modules/PlatformInfo.cmake @@ -1,3 +1,9 @@ +include("${PROJECT_BINARY_DIR}/build/DetectedPlatformInfo.cmake" OPTIONAL) + +if(SwiftCore_PLATFORM_INFO_SET) + return() +endif() + if(NOT SwiftCore_SIZEOF_POINTER) set(SwiftCore_SIZEOF_POINTER "${CMAKE_SIZEOF_VOID_P}" CACHE STRING "Size of a pointer in bytes") message(CONFIGURE_LOG "Stdlib Pointer size: ${CMAKE_SIZEOF_VOID_P}") @@ -36,3 +42,51 @@ if(NOT SwiftCore_ARCH_SUBDIR) message(CONFIGURE_LOG "Swift Arch: ${arch}") endif() + +# Note: *moduleTriple* doesn't have an "x" on the end of "macos"; just to be +# safe, we support both cases here. +set(availability_platform_macos "macOS") +set(availaiblity_platform_macosx "macOS") +set(availability_platform_ios "iOS") +set(availability_platform_watchos "watchOS") +set(availability_platform_tvos "tvOS") +set(availability_platform_xros "visionOS") +set(availability_platform_bridgeos "bridgeOS") + +if(NOT SwiftCore_SWIFT_AVAILABILITY_PLATFORM) + if(SwiftCore_MODULE_TRIPLE MATCHES ".*-([^-]+)-simulator$") + set(platform "${CMAKE_MATCH_1}") + elseif(SwiftCore_MODULE_TRIPLE MATCHES ".*-([^-]+)-msvc$") + set(platform "${CMAKE_MATCH_1}") + elseif(SwiftCore_MODULE_TRIPLE MATCHES ".*-([^-]+)$") + set(platform "${CMAKE_MATCH_1}") + else() + message(WARNING "Unable to extract platform name from triple ${SwiftCore_MODULE_TRIPLE}") + endif() + + if(availability_platform_${platform}) + set(SwiftCore_SWIFT_AVAILABILITY_PLATFORM "${availability_platform_${platform}}") + else() + set(SwiftCore_SWIFT_AVAILABILITY_PLATFORM "unknown") + message(WARNING "Unknown platform ${platform} for availability") + endif() +endif() + +set(SwiftCore_VARIANT_AVAILABILITY_PLATFORM "none") +if(SwiftCore_VARIANT_MODULE_TRIPLE) + if(SwiftCore_VARIANT_MODULE_TRIPLE MATCHES ".*-([^-]+)$") + set(platform "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Unable to extract platform name from triple ${SwiftCore_VARIANT_MODULE_TRIPLE}") + endif() + + if(availability_platform_${platform}) + set(SwiftCore_VARIANT_AVAILABILITY_PLATFORM "${availability_platform_${platform}}") + else() + message(WARNING "Unknown platform ${platform} for variant availability") + endif() +endif() + +configure_file("${CMAKE_CURRENT_LIST_DIR}/DetectedPlatformInfo.cmake.in" + "${PROJECT_BINARY_DIR}/build/DetectedPlatformInfo.cmake" + @ONLY) diff --git a/Runtimes/Core/core/CMakeLists.txt b/Runtimes/Core/core/CMakeLists.txt index 20df62386e987..8dcd95ec870bc 100644 --- a/Runtimes/Core/core/CMakeLists.txt +++ b/Runtimes/Core/core/CMakeLists.txt @@ -154,6 +154,8 @@ add_library(swiftCore Span/RawSpan.swift Span/MutableSpan.swift Span/MutableRawSpan.swift + Span/OutputSpan.swift + Span/OutputRawSpan.swift StaticString.swift StaticPrint.swift Stride.swift @@ -188,7 +190,6 @@ add_library(swiftCore StringWordBreaking.swift Substring.swift SwiftNativeNSArray.swift - SwiftSettings.swift TemporaryAllocation.swift ThreadLocalStorage.swift UIntBuffer.swift @@ -208,6 +209,13 @@ add_library(swiftCore UnsafeRawPointer.swift UTFEncoding.swift UTF8.swift + UTF8EncodingError.swift + UTF8Span.swift + UTF8SpanBits.swift + UTF8SpanComparisons.swift + UTF8SpanFundamentals.swift + UTF8SpanInternalHelpers.swift + UTF8SpanIterators.swift UTF16.swift UTF32.swift Unicode.swift # ORDER DEPENDENCY: must follow new unicode support @@ -253,13 +261,19 @@ if(NOT LINUX AND NOT ANDROID) endif() if(SwiftCore_ENABLE_COMMANDLINE_SUPPORT) - target_sources(swiftCore PRIVATE CommandLine.swift) - target_link_libraries(swiftCore PRIVATE swiftCommandLineSupport) + target_sources(swiftCore PRIVATE + CommandLine.swift) endif() if(SwiftCore_ENABLE_VECTOR_TYPES) - gyb_expand(SIMDConcreteOperations.swift.gyb - SIMDConcreteOperations.swift + gyb_expand(SIMDFloatConcreteOperations.swift.gyb + SIMDFloatConcreteOperations.swift + FLAGS "-DCMAKE_SIZEOF_VOID_P=${SwiftCore_SIZEOF_POINTER}") + gyb_expand(SIMDIntegerConcreteOperations.swift.gyb + SIMDIntegerConcreteOperations.swift + FLAGS "-DCMAKE_SIZEOF_VOID_P=${SwiftCore_SIZEOF_POINTER}") + gyb_expand(SIMDMaskConcreteOperations.swift.gyb + SIMDMaskConcreteOperations.swift FLAGS "-DCMAKE_SIZEOF_VOID_P=${SwiftCore_SIZEOF_POINTER}") gyb_expand(SIMDVectorTypes.swift.gyb SIMDVectorTypes.swift @@ -267,13 +281,19 @@ if(SwiftCore_ENABLE_VECTOR_TYPES) target_sources(swiftCore PRIVATE SIMDVector.swift - "${CMAKE_CURRENT_BINARY_DIR}/SIMDConcreteOperations.swift" + "${CMAKE_CURRENT_BINARY_DIR}/SIMDFloatConcreteOperations.swift" + "${CMAKE_CURRENT_BINARY_DIR}/SIMDIntegerConcreteOperations.swift" + "${CMAKE_CURRENT_BINARY_DIR}/SIMDMaskConcreteOperations.swift" "${CMAKE_CURRENT_BINARY_DIR}/SIMDVectorTypes.swift") endif() set_target_properties(swiftCore PROPERTIES Swift_MODULE_NAME Swift LINKER_LANGUAGE CXX) +if(NOT BUILD_SHARED_LIBS AND CMAKE_STATIC_LIBRARY_PREFIX_Swift) + set_target_properties(swiftCore PROPERTIES + PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX_Swift}) +endif() target_compile_definitions(swiftCore PRIVATE @@ -294,7 +314,8 @@ target_compile_options(swiftCore PRIVATE $<$:-nostdimport> "$<$:SHELL:-Xfrontend -group-info-path -Xfrontend ${CMAKE_CURRENT_SOURCE_DIR}/GroupInfo.json>" "$<$:SHELL:-Xfrontend -disable-objc-attr-requires-foundation-module>" - "$<$:SHELL:-Xfrontend -require-explicit-availability=ignore>") + "$<$:SHELL:-Xfrontend -require-explicit-availability=ignore>" + "$<$,$>:SHELL:-Xfrontend -previous-module-installname-map-file -Xfrontend ${CMAKE_CURRENT_SOURCE_DIR}/PreviousModuleInstallName.json>") if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel") # Using these in MinSizeRel would result in a 15% increase in the binary size target_compile_options(swiftCore PRIVATE @@ -315,7 +336,9 @@ target_link_libraries(swiftCore swiftThreading $<$>:swiftrt$<$:T>> PUBLIC - swiftShims) + swiftShims + INTERFACE + swiftRuntimeCMakeConfig) string(TOLOWER "${SwiftCore_OBJECT_FORMAT}" SwiftCore_OBJECT_FORMAT_lc) if("${SwiftCore_OBJECT_FORMAT_lc}" STREQUAL "elf") @@ -362,7 +385,7 @@ emit_swift_interface(swiftCore) install_swift_interface(swiftCore) # Configure plist creation for Darwin platforms. -generate_plist("${CMAKE_PROJECT_NAME}" "${CMAKE_PROJECT_VERSION}" swiftCore) +generate_plist(swiftCore "${CMAKE_PROJECT_VERSION}" swiftCore) embed_manifest(swiftCore) include("${SwiftCore_VENDOR_MODULE_DIR}/swiftCore.cmake" OPTIONAL) diff --git a/Runtimes/Core/runtime/CMakeConfig.h.in b/Runtimes/Core/runtime/CMakeConfig.h.in index 1aa1576a3bc4a..80d2e8b7d72ff 100644 --- a/Runtimes/Core/runtime/CMakeConfig.h.in +++ b/Runtimes/Core/runtime/CMakeConfig.h.in @@ -4,9 +4,6 @@ #ifndef SWIFT_RUNTIME_CMAKECONFIG_H #define SWIFT_RUNTIME_CMAKECONFIG_H -#cmakedefine01 SWIFT_BNI_OS_BUILD -#cmakedefine01 SWIFT_BNI_XCODE_BUILD - #define SWIFT_VERSION_MAJOR "@SwiftCore_VERSION_MAJOR@" #define SWIFT_VERSION_MINOR "@SwiftCore_VERSION_MINOR@" diff --git a/Runtimes/Core/runtime/CMakeLists.txt b/Runtimes/Core/runtime/CMakeLists.txt index bed1399425a1e..d226c7380fe71 100644 --- a/Runtimes/Core/runtime/CMakeLists.txt +++ b/Runtimes/Core/runtime/CMakeLists.txt @@ -1,19 +1,11 @@ -# TODO: clean this up so it's less Apple-specific. -# Detect B&I builds. -set(SWIFT_BNI_OS_BUILD FALSE) -set(SWIFT_BNI_XCODE_BUILD FALSE) -if(DEFINED ENV{RC_XBS}) - if((NOT DEFINED ENV{RC_XCODE} OR NOT "$ENV{RC_XCODE}") AND (NOT DEFINED ENV{RC_PLAYGROUNDS} OR NOT "$ENV{RC_PLAYGROUNDS}")) - set(SWIFT_BNI_OS_BUILD TRUE) - else() - set(SWIFT_BNI_XCODE_BUILD TRUE) - endif() -endif() - configure_file("CMakeConfig.h.in" "${PROJECT_BINARY_DIR}/include/swift/Runtime/CMakeConfig.h" ESCAPE_QUOTES @ONLY) +add_library(swiftRuntimeCMakeConfig INTERFACE) +target_include_directories(swiftRuntimeCMakeConfig INTERFACE + $<$:$/${CMAKE_INSTALL_INCLUDEDIR}>>) + add_library(swiftRuntime OBJECT "${PROJECT_SOURCE_DIR}/CompatibilityOverride/CompatibilityOverride.cpp" AnyHashableSupport.cpp @@ -144,6 +136,14 @@ if(SwiftCore_ENABLE_OBJC_INTEROP) ObjCRuntimeGetImageNameFromClass.mm) endif() +install(FILES + "${PROJECT_BINARY_DIR}/include/swift/Runtime/CMakeConfig.h" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/swift/Runtime" + COMPONENT SwiftCore_development) +install(TARGETS swiftRuntimeCMakeConfig + EXPORT SwiftCoreTargets + COMPONENT SwiftCore_development) + if(NOT BUILD_SHARED_LIBS) install(TARGETS swiftRuntime EXPORT SwiftCoreTargets diff --git a/Runtimes/Overlay/Android/Android/CMakeLists.txt b/Runtimes/Overlay/Android/Android/CMakeLists.txt new file mode 100644 index 0000000000000..2b6ffee693c3e --- /dev/null +++ b/Runtimes/Overlay/Android/Android/CMakeLists.txt @@ -0,0 +1,31 @@ + +gyb_expand(tgmath.swift.gyb tgmath.swift) + +add_library(swiftAndroid + tgmath.swift + Android.swift + Platform.swift + POSIXError.swift + TiocConstants.swift) +set_target_properties(swiftAndroid PROPERTIES + Swift_MODULE_NAME Android) +target_compile_definitions(swiftAndroid PRIVATE + $<$:SWIFT_ENABLE_REFLECTION>) +target_link_libraries(swiftAndroid PUBLIC + SwiftAndroid) +target_link_libraries(swiftAndroid PRIVATE + swiftCore) + +# FIXME: Why is this not implicitly in the interface flags? +target_include_directories(swiftAndroid INTERFACE + "$<$:$$/${SwiftCore_INSTALL_SWIFTMODULEDIR}>>") + +install(TARGETS swiftAndroid + EXPORT SwiftOverlayTargets + ARCHIVE DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swiftAndroid) +install_swift_interface(swiftAndroid) + +embed_manifest(swiftAndroid) diff --git a/Runtimes/Overlay/Android/CMakeLists.txt b/Runtimes/Overlay/Android/CMakeLists.txt new file mode 100644 index 0000000000000..8642382b6efe5 --- /dev/null +++ b/Runtimes/Overlay/Android/CMakeLists.txt @@ -0,0 +1,4 @@ + +add_subdirectory(clang) +add_subdirectory(Android) +add_subdirectory(Math) diff --git a/Runtimes/Overlay/Android/Math/CMakeLists.txt b/Runtimes/Overlay/Android/Math/CMakeLists.txt new file mode 100644 index 0000000000000..5e1d53a3ddff0 --- /dev/null +++ b/Runtimes/Overlay/Android/Math/CMakeLists.txt @@ -0,0 +1,18 @@ + +add_library(swift_math + Math.swift) +set_target_properties(swift_math PROPERTIES + Swift_MODULE_NAME math) +target_link_libraries(swift_math PRIVATE + SwiftAndroid + swiftCore) + +install(TARGETS swift_math + EXPORT SwiftOverlayTargets + ARCHIVE DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swift_math) +install_swift_interface(swift_math) + +embed_manifest(swift_math) diff --git a/Runtimes/Overlay/Android/clang/CMakeLists.txt b/Runtimes/Overlay/Android/clang/CMakeLists.txt new file mode 100644 index 0000000000000..2ca7b561b1f0c --- /dev/null +++ b/Runtimes/Overlay/Android/clang/CMakeLists.txt @@ -0,0 +1,41 @@ + +# FIXME: how do we determine the sysroot? `CMAKE_SYSROOT` does not contain the sysroot. +file(CONFIGURE + OUTPUT android-ndk-overlay.yaml + CONTENT [[ +--- +version: 0 +case-sensitive: false +use-external-names: false +roots: + - name: "@CMAKE_ANDROID_NDK@/toolchains/llvm/prebuilt/windows-x86_64/sysroot/usr/include" + type: directory + contents: + - name: module.modulemap + type: file + external-contents: "@CMAKE_CURRENT_SOURCE_DIR@/android.modulemap" + - name: SwiftAndroidNDK.h + type: file + external-contents: "@CMAKE_CURRENT_SOURCE_DIR@/SwiftAndroidNDK.h" + - name: SwiftBionic.h + type: file + external-contents: "@CMAKE_CURRENT_SOURCE_DIR@/SwiftBionic.h" +]] +ESCAPE_QUOTES @ONLY NEWLINE_STYLE LF) + +add_library(SwiftAndroid INTERFACE) +target_compile_options(SwiftAndroid INTERFACE + "$<$:SHELL:-Xcc --sysroot=\"${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/sysroot\">" + "$<$:SHELL:-vfsoverlay ${CMAKE_CURRENT_BINARY_DIR}/android-ndk-overlay.yaml>") + +install(TARGETS SwiftAndroid + EXPORT SwiftOverlayTargets) +install(FILES + android.modulemap + SwiftAndroidNDK.h + SwiftBionic.h + DESTINATION ${CMAKE_INSTALL_LIBDIR}/swift/${SwiftOverlay_PLATFORM_SUBDIR}/${SwiftOverlay_ARCH_SUBDIR}) +install(FILES + posix_filesystem.apinotes + spawn.apinotes + DESTINATION ${CMAKE_INSTALL_LIBDIR}/swift/apinotes) diff --git a/Runtimes/Overlay/CMakeLists.txt b/Runtimes/Overlay/CMakeLists.txt index 030a1c326587d..61afeb8f6459d 100644 --- a/Runtimes/Overlay/CMakeLists.txt +++ b/Runtimes/Overlay/CMakeLists.txt @@ -1,26 +1,29 @@ cmake_minimum_required(VERSION 3.26...3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders set(CMAKE_C_VISIBILITY_PRESET "hidden") set(CMAKE_CXX_VISIBILITY_PRESET "hidden") set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") - -# NOTE: always use the 3-component style as the expansion as -# `${PROJECT_VERSION}` will not extend this to the complete form and this can -# change the behaviour for comparison with non-SemVer compliant parsing. If -# possible, use the 4-version component as that is used to differentiate the -# builds of the runtime for Windows. -if($ENV{BUILD_NUMBER}) - # NOTE: SxS modules have a limit on each component being [0-65535]. - # https://learn.microsoft.com/en-us/windows/win32/sbscs/assembly-versions - math(EXPR BUILD_NUMBER "$ENV{BUILD_NUMBER} % 65535") - set(BUILD_NUMBER ".${BUILD_NUMBER}") +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/cmake/modules" + "${CMAKE_SOURCE_DIR}/../cmake/modules") + +if(POLICY CMP0157 AND CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + cmake_policy(SET CMP0157 OLD) endif() + +include(SwiftProjectVersion) project(SwiftOverlay LANGUAGES C CXX Swift - VERSION 6.1.0${BUILD_NUMBER}) + VERSION ${SWIFT_RUNTIME_VERSION}) + +set(CMAKE_Swift_LANGUAGE_VERSION 5) find_package(SwiftCore) @@ -30,26 +33,92 @@ set(SwiftOverlay_SWIFTC_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../../" CACHE FILEPATH "Path to the root source directory of the Swift compiler") +set(${PROJECT_NAME}_VENDOR_MODULE_DIR "${CMAKE_SOURCE_DIR}/cmake/modules/vendor" + CACHE FILEPATH "Location for private build system extension") + include(GNUInstallDirs) include(gyb) include(AvailabilityMacros) include(DefaultSettings) include(EmitSwiftInterface) +include(InstallSwiftInterface) include(PlatformInfo) include(ResourceEmbedding) +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/Settings.cmake" OPTIONAL) + defaulted_option(SwiftOverlay_ENABLE_REFLECTION "Enable runtime support for mirrors and reflection support") +defaulted_option(SwiftOverlay_ENABLE_CXX_INTEROP "Enable C++ Interop support overlays") option(SwiftOverlay_INSTALL_NESTED_SUBDIR "Install libraries under a platform and architecture subdirectory" ON) set(SwiftOverlay_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${SwiftOverlay_PLATFORM_SUBDIR}/${SwiftOverlay_ARCH_SUBDIR}>") set(SwiftOverlay_INSTALL_SWIFTMODULEDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${SwiftOverlay_PLATFORM_SUBDIR}>") +option(SwiftOverlay_ENABLE_LIBRARY_EVOLUTION "Generate ABI resilient runtime libraries" + ${SwiftCore_ENABLE_LIBRARY_EVOLUTION}) +option(SwiftOverlay_ENABLE_BACKDEPLOYMENT_SUPPORT "Add symbols for runtime backdeployment" + ${SwiftCore_ENABLE_BACKDEPLOYMENT_SUPPORT}) + +add_compile_definitions( + $<$:SWIFT_STDLIB_SUPPORT_BACK_DEPLOYMENT>) + add_compile_options( + $<$:-explicit-module-build> + $<$:-nostdlibimport> + $<$:-strict-memory-safety> + "$<$:SHELL:-Xfrontend -enable-lexical-lifetimes=false>" "$<$:SHELL:-Xfrontend -disable-implicit-concurrency-module-import>" - "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>") + "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>" + "$<$:SHELL:-Xfrontend -enforce-exclusivity=unchecked>" + "$<$:SHELL:-Xfrontend -target-min-inlining-version -Xfrontend min>" + "$<$,$>:-enable-library-evolution>" + "$<$,$>:SHELL:-Xfrontend -prespecialize-generic-metadata>") + +include(ExperimentalFeatures) + +# LNK4049: symbol 'symbol' defined in 'filename.obj' is imported +# LNK4286: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' +# LNK4217: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' in function 'function' +# +# We cannot selectively filter the linker warnings as we do not use the MSVC +# frontned and `clang-cl` (and `clang`) currently do not support `/WX:nnnn`. As +# a compromise, treat all linker warnings as errors. +add_link_options($<$:LINKER:/WX>) +# Ensure all symbols are fully resolved on Linux +add_link_options($<$:LINKER:-z,defs>) + +include(ExperimentalFeatures) add_subdirectory(clang) +if(ANDROID) + add_subdirectory(Android) +endif() if(WIN32) add_subdirectory(Windows) endif() + +if(SwiftOverlay_ENABLE_CXX_INTEROP) + add_subdirectory(Cxx) +endif() + +# Inter-project install info +export(EXPORT SwiftOverlayTargets + FILE "cmake/SwiftOverlay/SwiftOverlayTargets.cmake") +install(EXPORT SwiftOverlayTargets + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/SwiftOverlay" + FILE "SwiftOverlayTargets.cmake" + COMPONENT SwiftOverlayCMake) +include(CMakePackageConfigHelpers) +configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/interface/SwiftOverlayConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/SwiftOverlay/SwiftOverlayConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/SwiftOverlay") +write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/cmake/SwiftOverlay/SwiftOverlayConfigVersion.cmake" + VERSION "${PROJECT_VERSION}" + COMPATIBILITY ExactVersion) +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/cmake/SwiftOverlay/SwiftOverlayConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/SwiftOverlay/SwiftOverlayConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/SwiftOverlay") + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/swiftOverlay.cmake" OPTIONAL) diff --git a/Runtimes/Overlay/Cxx/CMakeLists.txt b/Runtimes/Overlay/Cxx/CMakeLists.txt new file mode 100644 index 0000000000000..5f43f25409f7c --- /dev/null +++ b/Runtimes/Overlay/Cxx/CMakeLists.txt @@ -0,0 +1,49 @@ + +include(CheckSymbolExists) +check_symbol_exists(_LIBCPP_VERSION "version" HAVE_LIBCPP_VERSION) +check_symbol_exists(__GLIBCXX__ "version" HAVE___GLIBCXX__) + +if(NOT APPLE) + add_subdirectory(cxxshim) +endif() +if(HAVE___GLIBCXX__) + add_subdirectory(libstdcxx) +endif() +add_subdirectory(std) + +add_library(swiftCxx STATIC + CxxConvertibleToBool.swift + CxxConvertibleToCollection.swift + CxxDictionary.swift + CxxOptional.swift + CxxPair.swift + CxxRandomAccessCollection.swift + CxxSequence.swift + CxxSet.swift + CxxSpan.swift + CxxVector.swift + UnsafeCxxIterators.swift) +set_target_properties(swiftCxx PROPERTIES + Swift_MODULE_NAME Cxx) +target_compile_options(swiftCxx PRIVATE + "$<$:-cxx-interoperability-mode=default>" + "$<$:-warn-implicit-overrides>" + # This module should not pull in the C++ standard library, so we disable it + # explicitly. For functionality that depends on the C++ stdlib, use C++ + # stdlib overlay (`swiftstd` module). + "$<$:SHELL:-Xcc -nostdinc++>" + "$<$:SHELL:-enable-experimental-feature AllowUnsafeAttribute>" + "$<$:SHELL:-enable-experimental-feature BuiltinModule>" + "$<$:SHELL:-enable-experimental-feature Lifetimes>") +target_link_libraries(swiftCxx PRIVATE + swiftCore) + +install(TARGETS swiftCxx + EXPORT SwiftOverlayTargets + ARCHIVE DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swiftCxx) +install_swift_interface(swiftCxx) + +embed_manifest(swiftCxx) diff --git a/Runtimes/Overlay/Cxx/cxxshim/CMakeLists.txt b/Runtimes/Overlay/Cxx/cxxshim/CMakeLists.txt new file mode 100644 index 0000000000000..de0e8b1fa5b7c --- /dev/null +++ b/Runtimes/Overlay/Cxx/cxxshim/CMakeLists.txt @@ -0,0 +1,17 @@ + +add_library(cxxshim INTERFACE) +target_compile_options(cxxshim INTERFACE + "$<$:SHELL:-Xcc -fmodule-map-file=${CMAKE_CURRENT_SOURCE_DIR}/libcxxshim.modulemap>") +target_include_directories(cxxshim INTERFACE + $<$:$>) + +install(TARGETS cxxshim + EXPORT SwiftOverlayTargets + ARCHIVE DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +install(FILES + libcxxshim.h + libcxxshim.modulemap + libcxxstdlibshim.h + DESTINATION "${CMAKE_INSTALL_LIBDIR}/swift/${SwiftOverlay_PLATFORM_SUBDIR}") diff --git a/Runtimes/Overlay/Cxx/libstdcxx/CMakeLists.txt b/Runtimes/Overlay/Cxx/libstdcxx/CMakeLists.txt new file mode 100644 index 0000000000000..4050cd5be0efc --- /dev/null +++ b/Runtimes/Overlay/Cxx/libstdcxx/CMakeLists.txt @@ -0,0 +1,16 @@ + +add_library(libstdcxx INTERFACE) +target_compile_options(libstdcxx INTERFACE + "$<$:SHELL:-Xcc -fmodule-map-file=${CMAKE_CURRENT_SOURCE_DIR}/libstdcxx.modulemap>") +target_include_directories(libstdcxx INTERFACE + $<$:$>) + +install(TARGETS libstdcxx + EXPORT SwiftOverlayTargets + ARCHIVE DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +install(FILES + libstdcxx.h + libstdcxx.modulemap + DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}") diff --git a/Runtimes/Overlay/Cxx/std/CMakeLists.txt b/Runtimes/Overlay/Cxx/std/CMakeLists.txt new file mode 100644 index 0000000000000..7463b92290c8e --- /dev/null +++ b/Runtimes/Overlay/Cxx/std/CMakeLists.txt @@ -0,0 +1,44 @@ + +add_library(swiftCxxStdlib STATIC + std.swift + Chrono.swift + String.swift) +set_target_properties(swiftCxxStdlib PROPERTIES + Swift_MODULE_NAME CxxStdlib) +target_compile_options(swiftCxxStdlib PRIVATE + "-strict-memory-safety" + "-cxx-interoperability-mode=default" + "SHELL:-enable-experimental-feature AllowUnsafeAttribute" + "SHELL:-enable-experimental-feature Lifetimes" + # This flag is unnecessary when building with newer compilers that allow using + # C++ symbols in resilient overlays (see f4204568). + "SHELL:-enable-experimental-feature AssumeResilientCxxTypes" + # The varying modularization of the C++ standard library on different + # platforms makes it difficult to enable MemberImportVisibility for this + # module + "SHELL:-disable-upcoming-feature MemberImportVisibility" + "SHELL:-Xfrontend -module-interface-preserve-types-as-written") +# NOTE: We need to setup the sysroot here as we need to ensure that we pick up +# the module.map from the C++ runtime for the `std` (spelt `CxxStdlib`) import. +target_compile_options(swiftCxxStdlib PRIVATE + "$<$:SHELL:-Xcc --sysroot -Xcc ${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/sysroot>") +target_link_libraries(swiftCxxStdlib PRIVATE + $<$:libstdcxx> + $<$>:cxxshim> + swiftCxx + swiftCore + swift_Builtin_float + $<$:SwiftAndroid> + $<$:ClangModules>) + +install(FILES std.apinotes + DESTINATION ${CMAKE_INSTALL_LIBDIR}/swift/apinotes) +install(TARGETS swiftCxxStdlib + EXPORT SwiftOverlayTargets + ARCHIVE DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swiftCxxStdlib) +install_swift_interface(swiftCxxStdlib) + +embed_manifest(swiftCxxStdlib) diff --git a/Runtimes/Overlay/Windows/CRT/CMakeLists.txt b/Runtimes/Overlay/Windows/CRT/CMakeLists.txt index 0c67c3f5fd683..d93e1efd30150 100644 --- a/Runtimes/Overlay/Windows/CRT/CMakeLists.txt +++ b/Runtimes/Overlay/Windows/CRT/CMakeLists.txt @@ -12,12 +12,19 @@ set_target_properties(swiftCRT PROPERTIES target_compile_definitions(swiftCRT PRIVATE $<$:SWIFT_ENABLE_REFLECTION>) target_compile_options(swiftCRT PRIVATE + "SHELL:-Xfrontend -disable-force-load-symbols" "SHELL:-Xcc -D_USE_MATH_DEFINES") +target_link_libraries(swiftCRT PUBLIC + ClangModules) target_link_libraries(swiftCRT PRIVATE - ClangModules swiftCore) +# FIXME: Why is this not implicitly in the interface flags? +target_include_directories(swiftCRT INTERFACE + "$<$:$$/${SwiftCore_INSTALL_SWIFTMODULEDIR}>>") + install(TARGETS swiftCRT + EXPORT SwiftOverlayTargets ARCHIVE DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" LIBRARY DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/Runtimes/Overlay/Windows/WinSDK/CMakeLists.txt b/Runtimes/Overlay/Windows/WinSDK/CMakeLists.txt index 9140c71e0261f..954449a6a8b71 100644 --- a/Runtimes/Overlay/Windows/WinSDK/CMakeLists.txt +++ b/Runtimes/Overlay/Windows/WinSDK/CMakeLists.txt @@ -3,13 +3,21 @@ add_library(swiftWinSDK WinSDK.swift) set_target_properties(swiftWinSDK PROPERTIES Swift_MODULE_NAME WinSDK) -target_compile_definitions(swiftCRT PRIVATE +target_compile_definitions(swiftWinSDK PRIVATE $<$:SWIFT_ENABLE_REFLECTION>) +target_compile_options(swiftWinSDK PRIVATE + "SHELL:-Xfrontend -disable-force-load-symbols") +target_link_libraries(swiftWinSDK PUBLIC + ClangModules) target_link_libraries(swiftWinSDK PRIVATE - ClangModules swiftCore) +# FIXME: Why is this not implicitly in the interface flags? +target_include_directories(swiftWinSDK INTERFACE + "$<$:$$/${SwiftCore_INSTALL_SWIFTMODULEDIR}>>") + install(TARGETS swiftWinSDK + EXPORT SwiftOverlayTargets ARCHIVE DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" LIBRARY DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/Runtimes/Overlay/Windows/clang/CMakeLists.txt b/Runtimes/Overlay/Windows/clang/CMakeLists.txt index 8f2872d1c8ce1..1d88b8ed024ad 100644 --- a/Runtimes/Overlay/Windows/clang/CMakeLists.txt +++ b/Runtimes/Overlay/Windows/clang/CMakeLists.txt @@ -18,14 +18,20 @@ roots: contents: - name: module.modulemap type: file - external-contents: "@CMAKE_CURRENT_SOURCE_DIR@/winsdk.modulemap" + external-contents: "@CMAKE_CURRENT_SOURCE_DIR@/winsdk_um.modulemap" + - name: "@WindowsSdkDir@/Include/@WindowsSDKVersion@/shared" + type: directory + contents: + - name: module.modulemap + type: file + external-contents: "@CMAKE_CURRENT_SOURCE_DIR@/winsdk_shared.modulemap" - name: "@UniversalCRTSdkDir@/Include/@UCRTVersion@/ucrt" type: directory contents: - name: module.modulemap type: file external-contents: "@CMAKE_CURRENT_SOURCE_DIR@/ucrt.modulemap" - - name: "@VCToolsInstallDir@include" + - name: "@VCToolsInstallDir@/include" type: directory contents: - name: module.modulemap @@ -41,9 +47,12 @@ add_library(ClangModules INTERFACE) target_compile_options(ClangModules INTERFACE "$<$:SHELL:-vfsoverlay ${CMAKE_CURRENT_BINARY_DIR}/windows-sdk-overlay.yaml>") +install(TARGETS ClangModules + EXPORT SwiftOverlayTargets) install(FILES ucrt.modulemap vcruntime.apinotes vcruntime.modulemap - winsdk.modulemap + winsdk_um.modulemap + winsdk_shared.modulemap DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}) diff --git a/Runtimes/Overlay/clang/CMakeLists.txt b/Runtimes/Overlay/clang/CMakeLists.txt index c1c91ad2ea126..3be1089f19847 100644 --- a/Runtimes/Overlay/clang/CMakeLists.txt +++ b/Runtimes/Overlay/clang/CMakeLists.txt @@ -1,3 +1,4 @@ +include(CatalystSupport) gyb_expand(float.swift.gyb float.swift) @@ -7,12 +8,14 @@ add_library(swift_Builtin_float set_target_properties(swift_Builtin_float PROPERTIES Swift_MODULE_NAME _Builtin_float) target_compile_options(swift_Builtin_float PRIVATE - "$<$:SHELL:-Xfrontend -module-abi-name -Xfrontend Darwin>") + "$<$,$>:SHELL:-Xfrontend -module-abi-name -Xfrontend Darwin>" + $<$:-autolink-force-load>) target_link_libraries(swift_Builtin_float PRIVATE $<$:ClangModules> swiftCore) install(TARGETS swift_Builtin_float + EXPORT SwiftOverlayTargets ARCHIVE DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" LIBRARY DESTINATION "${SwiftOverlay_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/apple-common.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/apple-common.cmake new file mode 100644 index 0000000000000..7d12600d66551 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/apple-common.cmake @@ -0,0 +1,9 @@ +set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "") +set(BUILD_SHARED_LIBS YES CACHE BOOL "") +set(SwiftOverlay_INSTALL_NESTED_SUBDIR OFF CACHE BOOL "") +set(SwiftOverlay_ENABLE_LIBRARY_EVOLUTION ON CACHE BOOL "") +set(SwiftOverlay_ENABLE_BACKDEPLOYMENT_SUPPORT ON CACHE BOOL "") + +set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_C_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_Swift_FLAGS_MINSIZEREL "-Osize -g" CACHE STRING "") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..ae24d97764daa --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake new file mode 100644 index 0000000000000..1abed5f91b6aa --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake new file mode 100644 index 0000000000000..67641eca76a51 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake @@ -0,0 +1,13 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR freestanding CACHE STRING "") +set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake new file mode 100644 index 0000000000000..cb68431415894 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftOverlay_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftOverlay_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftOverlay_COMPILER_VARIANT_TARGET "arm64-apple-ios${SwiftOverlay_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..3fd83a015d1ea --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake new file mode 100644 index 0000000000000..d736e811590f5 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..4460ad2964b69 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake new file mode 100644 index 0000000000000..93f3f0b2d2de4 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake new file mode 100644 index 0000000000000..640e274535d9a --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64_32 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake new file mode 100644 index 0000000000000..b8f623468d603 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake new file mode 100644 index 0000000000000..64e842e8f9240 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftOverlay_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftOverlay_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftOverlay_COMPILER_VARIANT_TARGET "arm64e-apple-ios${SwiftOverlay_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake new file mode 100644 index 0000000000000..ddbe7c4371305 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-XROS.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-XROS.cmake new file mode 100644 index 0000000000000..2c30d3335bb15 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-XROS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR xros CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake new file mode 100644 index 0000000000000..ec5c239c808b0 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..a4cb83b667cc2 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake new file mode 100644 index 0000000000000..5e951ed23ef8a --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftOverlay_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftOverlay_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftOverlay_COMPILER_VARIANT_TARGET "x86_64-apple-ios${SwiftOverlay_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..7863d93d416bb --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake new file mode 100644 index 0000000000000..b6c469cfd6c65 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..2a206f8574844 --- /dev/null +++ b/Runtimes/Overlay/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftOverlay_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftOverlay_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Overlay/cmake/interface/SwiftOverlayConfig.cmake.in b/Runtimes/Overlay/cmake/interface/SwiftOverlayConfig.cmake.in new file mode 100644 index 0000000000000..eaabdc1972d67 --- /dev/null +++ b/Runtimes/Overlay/cmake/interface/SwiftOverlayConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/SwiftOverlayTargets.cmake") + +set(SwiftOverlay_ENABLE_REFLECTION @SwiftOverlay_ENABLE_REFLECTION@) diff --git a/Runtimes/Overlay/cmake/modules/CatalystSupport.cmake b/Runtimes/Overlay/cmake/modules/CatalystSupport.cmake new file mode 100644 index 0000000000000..69c341a1d58e6 --- /dev/null +++ b/Runtimes/Overlay/cmake/modules/CatalystSupport.cmake @@ -0,0 +1,48 @@ +include(CheckCompilerFlag) + +# Add flags for generating the zippered target variant in the build + +# Initialize `${PROJECT_NAME}_VARIANT_MODULE_TRIPLE` if the driver is able to emit +# modules for the target variant. + +if(${PROJECT_NAME}_COMPILER_VARIANT_TARGET) + add_compile_options( + "$<$:SHELL:-darwin-target-variant ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>" + "$<$:SHELL:-target-variant ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>" + + # TODO: Remove me once we have a driver with + # https://github.com/swiftlang/swift-driver/pull/1803 + "$<$:SHELL:-Xclang-linker -darwin-target-variant -Xclang-linker ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>") + + add_link_options( + "$<$:SHELL:-darwin-target-variant ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>" + "$<$:SHELL:-target-variant ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>" + + # TODO: Remove me once we have a driver with + # https://github.com/swiftlang/swift-driver/pull/1803 + "$<$:SHELL:-Xclang-linker -darwin-target-variant -Xclang-linker ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>") + + # TODO: Once we are guaranteed to have a driver with the variant module path + # support everywhere, we should integrate this into PlatformInfo.cmake + check_compiler_flag(Swift "-emit-variant-module-path ${CMAKE_CURRENT_BINARY_DIR}/CompilerID/variant.swiftmodule" HAVE_Swift_VARIANT_MODULE_PATH_FLAG) + if(HAVE_Swift_VARIANT_MODULE_PATH_FLAG) + # Get variant module triple + set(module_triple_command "${CMAKE_Swift_COMPILER}" -print-target-info -target ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}) + execute_process(COMMAND ${module_triple_command} OUTPUT_VARIABLE target_info_json) + message(CONFIGURE_LOG "Swift target variant info: ${target_info_json}") + + + string(JSON module_triple GET "${target_info_json}" "target" "moduleTriple") + set(${PROJECT_NAME}_VARIANT_MODULE_TRIPLE "${module_triple}" CACHE STRING "Triple used for installed swift{module,interface} files for the target variant") + mark_as_advanced(${PROJECT_NAME}_VARIANT_MODULE_TRIPLE) + message(CONFIGURE_LOG "Swift target variant module triple: ${module_triple}") + endif() + + if(${PROJECT_NAME}_EXPERIMENTAL_EMIT_VARIANT_MODULE) + check_compiler_flag(Swift "-experimental-emit-variant-module" HAVE_Swift_EMIT_VARIANT_MODULE_FLAG) + if(HAVE_Swift_EMIT_VARIANT_MODULE_FLAG) + add_compile_options( + "$<$:-experimental-emit-variant-module>") + endif() + endif() +endif() diff --git a/Runtimes/Overlay/cmake/modules/DefaultSettings.cmake b/Runtimes/Overlay/cmake/modules/DefaultSettings.cmake index 1b9c4b6f991c9..259f24aa20a18 100644 --- a/Runtimes/Overlay/cmake/modules/DefaultSettings.cmake +++ b/Runtimes/Overlay/cmake/modules/DefaultSettings.cmake @@ -26,12 +26,16 @@ endmacro() if(APPLE) set(SwiftOverlay_ENABLE_REFLECTION_default ON) + set(SwiftOverlay_ENABLE_CXX_INTEROP_default OFF) elseif(CMAKE_SYSTEM_NAME STREQUAL "WASM") set(SwiftOverlay_ENABLE_REFLECTION_default OFF) + set(SwiftOverlay_ENABLE_CXX_INTEROP_default OFF) elseif(LINUX OR ANDROID OR BSD) set(SwiftOverlay_ENABLE_REFLECTION_default OFF) + set(SwiftOverlay_ENABLE_CXX_INTEROP_default OFF) elseif(WIN32) - set(SwiftOverlay_ENABLE_REFLECTION_default OFF) + set(SwiftOverlay_ENABLE_REFLECTION_default ON) + set(SwiftOverlay_ENABLE_CXX_INTEROP_default OFF) endif() include("${SwiftOverlay_VENDOR_MODULE_DIR}/DefaultSettings.cmake" OPTIONAL) diff --git a/Runtimes/Overlay/cmake/modules/EmitSwiftInterface.cmake b/Runtimes/Overlay/cmake/modules/EmitSwiftInterface.cmake index 95aa11017794a..df4a7c51afef6 100644 --- a/Runtimes/Overlay/cmake/modules/EmitSwiftInterface.cmake +++ b/Runtimes/Overlay/cmake/modules/EmitSwiftInterface.cmake @@ -8,61 +8,61 @@ function(emit_swift_interface target) # Generate the target-variant binary swift module when performing zippered # build + # Clean this up once CMake has nested swiftmodules in the build directory: + # https://gitlab.kitware.com/cmake/cmake/-/merge_requests/10664 + # https://cmake.org/cmake/help/git-stage/policy/CMP0195.html + + # We can't expand the Swift_MODULE_NAME target property in a generator + # expression or it will fail saying that the target doesn't exist. + get_target_property(module_name ${target} Swift_MODULE_NAME) + if(NOT module_name) + set(module_name ${target}) + endif() + set(module_directory "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule") + # Account for an existing swiftmodule file + # generated with the previous logic + if(EXISTS "${module_directory}" AND NOT IS_DIRECTORY "${module_directory}") + message(STATUS "Removing regular file '${module_directory}' to support nested swiftmodule generation") + file(REMOVE ${module_directory}) + endif() + target_compile_options(${target} PRIVATE + "$<$:SHELL:-emit-module-path ${module_directory}/${SwiftOverlay_MODULE_TRIPLE}.swiftmodule>") + set_property(TARGET "${target}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${module_directory}/${SwiftOverlay_MODULE_TRIPLE}.swiftmodule" + "${module_directory}/${SwiftOverlay_MODULE_TRIPLE}.swiftdoc" + "${module_directory}/${SwiftOverlay_MODULE_TRIPLE}.swiftsourceinfo") if(SwiftOverlay_VARIANT_MODULE_TRIPLE) - set(variant_module_tmp_dir "${CMAKE_CURRENT_BINARY_DIR}/${target}-${SwiftOverlay_VARIANT_MODULE_TRIPLE}") - file(MAKE_DIRECTORY "${variant_module_tmp_dir}") target_compile_options(${target} PRIVATE - "$<$:SHELL:-emit-variant-module-path ${variant_module_tmp_dir}/${target}.swiftmodule>") + "$<$:SHELL:-emit-variant-module-path ${module_directory}/${SwiftOverlay_VARIANT_MODULE_TRIPLE}.swiftmodule>") + set_property(TARGET "${target}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${module_directory}/${SwiftOverlay_VARIANT_MODULE_TRIPLE}.swiftmodule" + "${module_directory}/${SwiftOverlay_VARIANT_MODULE_TRIPLE}.swiftdoc" + "${module_directory}/${SwiftOverlay_VARIANT_MODULE_TRIPLE}.swiftsourceinfo") endif() + add_custom_command(OUTPUT "${module_directory}/${SwiftOverlay_MODULE_TRIPLE}.swiftmodule" + DEPENDS ${target}) + target_sources(${target} + INTERFACE + $) # Generate textual swift interfaces is library-evolution is enabled if(SwiftOverlay_ENABLE_LIBRARY_EVOLUTION) target_compile_options(${target} PRIVATE - $<$:-emit-module-interface-path$${CMAKE_CURRENT_BINARY_DIR}/$.swiftinterface> - $<$:-emit-private-module-interface-path$${CMAKE_CURRENT_BINARY_DIR}/$.private.swiftinterface> - $<$:-library-level$api> - $<$:-Xfrontend$-require-explicit-availability=ignore>) - - # Emit catalyst swiftmodules and interfaces - if(SwiftOverlay_VARIANT_MODULE_TRIPLE) - target_compile_options(${target} PRIVATE - "$<$:SHELL:-emit-variant-module-interface-path ${variant_module_tmp_dir}/${target}.swiftinterface>" - "$<$:SHELL:-emit-variant-private-module-interface-path ${variant_module_tmp_dir}/${target}.private.swiftinterface>") - endif() - endif() -endfunction() - -# Install the generated swift interface file for the target if library evolution -# is enabled. -function(install_swift_interface target) - # Install binary swift modules - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/$.swiftmodule" - RENAME "${SwiftOverlay_MODULE_TRIPLE}.swiftmodule" - DESTINATION "${SwiftOverlay_INSTALL_SWIFTMODULEDIR}/$.swiftmodule") - if(SwiftOverlay_VARIANT_MODULE_TRIPLE) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}-${SwiftOverlay_VARIANT_MODULE_TRIPLE}/${target}.swiftmodule" - RENAME "${SwiftOverlay_VARIANT_MODULE_TRIPLE}.swiftmodule" - DESTINATION "${SwiftOverlay_INSTALL_SWIFTMODULEDIR}/$.swiftmodule") - endif() - - # Install Swift interfaces if library-evolution is enabled - if(SwiftOverlay_ENABLE_LIBRARY_EVOLUTION) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/$.swiftinterface" - RENAME "${SwiftOverlay_MODULE_TRIPLE}.swiftinterface" - DESTINATION "${SwiftOverlay_INSTALL_SWIFTMODULEDIR}/$.swiftmodule") - - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/$.private.swiftinterface" - RENAME "${SwiftOverlay_MODULE_TRIPLE}.private.swiftinterface" - DESTINATION "${SwiftOverlay_INSTALL_SWIFTMODULEDIR}/$.swiftmodule") - - # Install catalyst interface files + $<$:-emit-module-interface-path$${module_directory}/${SwiftOverlay_MODULE_TRIPLE}.swiftinterface> + $<$:-emit-private-module-interface-path$${module_directory}/${SwiftOverlay_MODULE_TRIPLE}.private.swiftinterface>) + set_property(TARGET "${target}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${module_directory}/${SwiftOverlay_MODULE_TRIPLE}.swiftinterface" + "${module_directory}/${SwiftOverlay_MODULE_TRIPLE}.private.swiftinterface") if(SwiftOverlay_VARIANT_MODULE_TRIPLE) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}-${SwiftOverlay_VARIANT_MODULE_TRIPLE}/${target}.swiftinterface" - RENAME "${SwiftOverlay_VARIANT_MODULE_TRIPLE}.swiftinterface" - DESTINATION "${SwiftOverlay_INSTALL_SWIFTMODULEDIR}/$.swiftmodule") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}-${SwiftOverlay_VARIANT_MODULE_TRIPLE}/${target}.private.swiftinterface" - RENAME "${SwiftOverlay_VARIANT_MODULE_TRIPLE}.private.swiftinterface" - DESTINATION "${SwiftOverlay_INSTALL_SWIFTMODULEDIR}/$.swiftmodule") + target_compile_options(${target} PRIVATE + "$<$:SHELL:-emit-variant-module-interface-path ${module_directory}/${SwiftOverlay_VARIANT_MODULE_TRIPLE}.swiftinterface>" + "$<$:SHELL:-emit-variant-private-module-interface-path ${module_directory}/${SwiftOverlay_VARIANT_MODULE_TRIPLE}.private.swiftinterface>") + set_property(TARGET "${target}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${module_directory}/${SwiftOverlay_VARIANT_MODULE_TRIPLE}.swiftinterface" + "${module_directory}/${SwiftOverlay_VARIANT_MODULE_TRIPLE}.private.swiftinterface") endif() + target_compile_options(${target} PRIVATE + $<$:-library-level$api> + $<$:-Xfrontend$-require-explicit-availability=ignore>) endif() endfunction() diff --git a/Runtimes/Overlay/cmake/modules/ExperimentalFeatures.cmake b/Runtimes/Overlay/cmake/modules/ExperimentalFeatures.cmake new file mode 100644 index 0000000000000..311630bac1ce4 --- /dev/null +++ b/Runtimes/Overlay/cmake/modules/ExperimentalFeatures.cmake @@ -0,0 +1,9 @@ +add_compile_options( + "$<$:SHELL:-enable-experimental-feature NoncopyableGenerics2>" + "$<$:SHELL:-enable-experimental-feature SuppressedAssociatedTypes>" + "$<$:SHELL:-enable-experimental-feature SE427NoInferenceOnExtension>" + "$<$:SHELL:-enable-experimental-feature NonescapableTypes>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature InoutLifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependenceMutableAccessors>" + "$<$:SHELL:-enable-upcoming-feature MemberImportVisibility>") diff --git a/Runtimes/Overlay/cmake/modules/InstallSwiftInterface.cmake b/Runtimes/Overlay/cmake/modules/InstallSwiftInterface.cmake new file mode 100644 index 0000000000000..8a7fbfe113c4b --- /dev/null +++ b/Runtimes/Overlay/cmake/modules/InstallSwiftInterface.cmake @@ -0,0 +1,13 @@ + +# Install the generated swift interface files for the target. +function(install_swift_interface target) + # Swiftmodules are already in the directory structure + get_target_property(module_name ${target} Swift_MODULE_NAME) + if(NOT module_name) + set(module_name ${target}) + endif() + + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule" + DESTINATION "${SwiftOverlay_INSTALL_SWIFTMODULEDIR}" + COMPONENT SwiftOverlay_development) +endfunction() diff --git a/Runtimes/Resync.cmake b/Runtimes/Resync.cmake index 59d95df91d14e..92e08c7821d8c 100644 --- a/Runtimes/Resync.cmake +++ b/Runtimes/Resync.cmake @@ -56,6 +56,8 @@ function(copy_library_sources name from_prefix to_prefix) "${ARG_ROOT}/${from_prefix}/${name}/*.c" "${ARG_ROOT}/${from_prefix}/${name}/*.mm" "${ARG_ROOT}/${from_prefix}/${name}/*.m" + "${ARG_ROOT}/${from_prefix}/${name}/*.S" + "${ARG_ROOT}/${from_prefix}/${name}/*.asm" "${ARG_ROOT}/${from_prefix}/${name}/*.def" "${ARG_ROOT}/${from_prefix}/${name}/*.gyb" "${ARG_ROOT}/${from_prefix}/${name}/*.apinotes" @@ -86,6 +88,8 @@ set(CoreLibs CommandLineSupport core SwiftOnoneSupport + RemoteInspection + SwiftRemoteMirror Concurrency Concurrency/InternalShims) @@ -96,23 +100,71 @@ endforeach() message(STATUS "plist[${StdlibSources}/Info.plist.in] -> Core/Info.plist.in") copy_files("" "Core" FILES "Info.plist.in") +message(STATUS "plist[${StdlibSources}/Info.plist.in] -> Supplemental/Distributed/Info.plist.in") +copy_files("" "Supplemental/Distributed" FILES "Info.plist.in") + +message(STATUS "plist[${StdlibSources}/Info.plist.in] -> Supplemental/Differentiation/Info.plist.in") +copy_files("" "Supplemental/Differentiation" FILES "Info.plist.in") + +message(STATUS "plist[${StdlibSources}/Info.plist.in] -> Supplemental/Observation/Info.plist.in") +copy_files("" "Supplemental/Observation" FILES "Info.plist.in") + +message(STATUS "plist[${StdlibSources}/Info.plist.in] -> Supplemental/StringProcessing/Info.plist.in") +copy_files("" "Supplemental/StringProcessing" FILES "Info.plist.in") + +message(STATUS "plist[${StdlibSources}/Info.plist.in] -> Supplemental/Synchronization/Info.plist.in") +copy_files("" "Supplemental/Synchronization" FILES "Info.plist.in") + +message(STATUS "plist[${StdlibSources}/Info.plist.in] -> Supplemental/Volatile/Info.plist.in") +copy_files("" "Supplemental/Volatile" FILES "Info.plist.in") + +message(STATUS "plist[${StdlibSources}/Info.plist.in] -> Supplemental/Runtime/Info.plist.in") +copy_files("" "Supplemental/Runtime" FILES "Info.plist.in") + # Platform Overlays # Copy magic linker symbols -copy_library_sources("linker-support" "" "Overlay") +copy_library_sources("linker-support" "public/ClangOverlays" "Overlay") message(STATUS "Clang[${StdlibSources}/public/ClangOverlays] -> ${CMAKE_CURRENT_LIST_DIR}/Overlay/clang") copy_files(public/ClangOverlays Overlay/clang FILES float.swift.gyb) +copy_library_sources("Cxx" "public" "Overlay") + +# Android Overlay +message(STATUS "Android modulemaps[${StdlibSources}/Platform] -> ${CMAKE_CURRENT_LIST_DIR}/Overlay/Android/clang") +copy_files(public/Platform Overlay/Android/clang + FILES + android.modulemap + posix_filesystem.apinotes + spawn.apinotes + SwiftAndroidNDK.h + SwiftBionic.h) + +message(STATUS "Android Android[${StdlibSources}/Platform] -> ${CMAKE_CURRENT_LIST_DIR}/Overlay/Android/Android") +copy_files(public/Platform Overlay/Android/Android + FILES + Android.swift + Platform.swift + POSIXError.swift + TiocConstants.swift + tgmath.swift.gyb) + +message(STATUS "Android Math[${StdlibSources}/Platform] -> ${CMAKE_CURRENT_LIST_DIR}/Overlay/Android/Math") +copy_files(public/Platform Overlay/Android/Math + FILES + Math.swift) + # Windows Overlay message(STATUS "WinSDK[${StdlibSources}/public/Windows] -> ${CMAKE_CURRENT_LIST_DIR}/Overlay/Windows/WinSDK") copy_files(public/Windows Overlay/Windows/WinSDK FILES WinSDK.swift) -message(STATUS "Windows Modulemaps[${StdlibSources}/Platform] -> ${CMAKE_CURRENT_LIST_DIR}/Overlay/Windows/clang") +message(STATUS "Windows modulemaps[${StdlibSources}/Platform] -> ${CMAKE_CURRENT_LIST_DIR}/Overlay/Windows/clang") copy_files(public/Platform Overlay/Windows/clang FILES ucrt.modulemap - winsdk.modulemap + winsdk_um.modulemap + winsdk_shared.modulemap vcruntime.modulemap vcruntime.apinotes) @@ -125,12 +177,6 @@ copy_files(public/Platform Overlay/Windows/CRT TiocConstants.swift tgmath.swift.gyb) -# TODO: Add source directories for the platform overlays, supplemental -# libraries, and test support libraries. - -# Supplemental Libraries - -# Copy StringProcessing, RegexParser, RegexBuilder if(NOT DEFINED StringProcessing_ROOT_DIR) find_path(StringProcessing_ROOT_DIR "swift-experimental-string-processing/Package.swift" @@ -138,6 +184,14 @@ if(NOT DEFINED StringProcessing_ROOT_DIR) endif() message(STATUS "String Processing Root: ${StringProcessing_ROOT_DIR}") +# Supplemental Libraries +copy_library_sources(Differentiation "public" "Supplemental") +copy_library_sources(Distributed "public" "Supplemental") +copy_library_sources(Observation "public" "Supplemental") +copy_library_sources(Synchronization "public" "Supplemental") +copy_library_sources(Volatile "public" "Supplemental") +copy_library_sources("" "public/RuntimeModule" "Supplemental/Runtime") + copy_library_sources(_RegexParser "Sources" "Supplemental/StringProcessing" ROOT "${StringProcessing_ROOT_DIR}/swift-experimental-string-processing") copy_library_sources(_StringProcessing "Sources" "Supplemental/StringProcessing" @@ -146,3 +200,6 @@ copy_library_sources(_CUnicode "Sources" "Supplemental/StringProcessing/_StringP ROOT "${StringProcessing_ROOT_DIR}/swift-experimental-string-processing") copy_library_sources(RegexBuilder "Sources" "Supplemental/StringProcessing" ROOT "${StringProcessing_ROOT_DIR}/swift-experimental-string-processing") + +copy_library_sources("linker-support" "" "Supplemental/Differentiation") +copy_library_sources(include "" "Supplemental/Distributed") diff --git a/Runtimes/Supplemental/CMakeLists.txt b/Runtimes/Supplemental/CMakeLists.txt new file mode 100644 index 0000000000000..0b64b98c7c7e1 --- /dev/null +++ b/Runtimes/Supplemental/CMakeLists.txt @@ -0,0 +1,125 @@ +cmake_minimum_required(VERSION 3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders + +project(SwiftRuntime LANGUAGES Swift C CXX) + +include(ExternalProject) +include(GNUInstallDirs) + +set(SwiftRuntime_SWIFTC_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../../") + +foreach(lib ${Swift_ENABLE_RUNTIMES}) + string(TOLOWER ${lib} name) + set(SwiftRuntime_ENABLE_${name} YES) +endforeach() + +if(SwiftCore_DIR) + set(SwiftCore_DIR_FLAG "-DSwiftCore_DIR=${SwiftCore_DIR}") +endif() + +if(CMAKE_MAKE_PROGRAM) + set(MAKE_PROGRAM_FLAG "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") +endif() + +set(COMMON_OPTIONS + -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} + -DSwift_SDKROOT=${Swift_SDKROOT} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR} + -DCMAKE_INSTALL_NAME_DIR=${CMAKE_INSTALL_NAME_DIR} + -DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=${CMAKE_BUILD_WITH_INSTALL_NAME_DIR} + -DCMAKE_INSTALL_PREFIX= + -DCMAKE_COLOR_DIAGNOSTICS=${CMAKE_COLOR_DIAGNOSTICS} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_Swift_COMPILER=${CMAKE_Swift_COMPILER} + -DCMAKE_C_COMPILER_TARGET=${CMAKE_C_COMPILER_TARGET} + -DCMAKE_CXX_COMPILER_TARGET=${CMAKE_CXX_COMPILER_TARGET} + -DCMAKE_ASM_COMPILER_TARGET=${CMAKE_ASM_COMPILER_TARGET} + -DCMAKE_Swift_COMPILER_TARGET=${CMAKE_Swift_COMPILER_TARGET} + -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=${CMAKE_FIND_PACKAGE_PREFER_CONFIG} + ${SwiftCore_DIR_FLAG} + ${MAKE_PROGRAM_FLAG}) + +# StringProcessing +if(SwiftRuntime_ENABLE_stringprocessing) + ExternalProject_Add(StringProcessing + PREFIX "StringProcessing" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/StringProcessing" + INSTALL_DIR "${CMAKE_INSTALL_PREFIX}" + INSTALL_COMMAND "" + # To ensure incremental builds work as expected + BUILD_ALWAYS 1 + CMAKE_ARGS + ${COMMON_OPTIONS}) +endif() + +# Synchronization +if(SwiftRuntime_ENABLE_synchronization) + ExternalProject_Add(Synchronization + PREFIX "Synchronization" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Synchronization" + INSTALL_DIR "${CMAKE_INSTALL_PREFIX}" + INSTALL_COMMAND "" + # To ensure incremental builds work as expected + BUILD_ALWAYS 1 + CMAKE_ARGS + ${COMMON_OPTIONS}) +endif() + +# Distributed +if(SwiftRuntime_ENABLE_distributed) + ExternalProject_Add(Distributed + PREFIX "Distributed" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Distributed" + INSTALL_DIR "${CMAKE_INSTALL_PREFIX}" + INSTALL_COMMAND "" + # To ensure incremental builds work as expected + BUILD_ALWAYS 1 + CMAKE_ARGS + ${COMMON_OPTIONS}) +endif() + + +# Differentiation +if(SwiftRuntime_ENABLE_differentiation) + ExternalProject_Add(Differentiation + PREFIX "Differentiation" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Differentiation" + INSTALL_DIR "${CMAKE_INSTALL_PREFIX}" + INSTALL_COMMAND "" + # To ensure incremental builds work as expected + BUILD_ALWAYS 1 + CMAKE_ARGS + ${COMMON_OPTIONS}) +endif() + +# Observation +if(SwiftRuntime_ENABLE_observation) + ExternalProject_Add(Observation + PREFIX "Observation" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Observation" + INSTALL_DIR "${CMAKE_INSTALL_PREFIX}" + INSTALL_COMMAND "" + # To ensure incremental builds work as expected + BUILD_ALWAYS 1 + CMAKE_ARGS + ${COMMON_OPTIONS}) +endif() + +# Runtime +if(SwiftRuntime_ENABLE_runtime) + ExternalProject_Add(Runtime + PREFIX "Runtime" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Runtime" + INSTALL_DIR "${CMAKE_INSTALL_PREFIX}" + INSTALL_COMMAND "" + # To ensure incremental builds work as expected + BUILD_ALWAYS 1 + CMAKE_ARGS + ${COMMON_OPTIONS}) +endif() diff --git a/Runtimes/Supplemental/Differentiation/CMakeLists.txt b/Runtimes/Supplemental/Differentiation/CMakeLists.txt new file mode 100644 index 0000000000000..3e0b42124aeff --- /dev/null +++ b/Runtimes/Supplemental/Differentiation/CMakeLists.txt @@ -0,0 +1,139 @@ +cmake_minimum_required(VERSION 3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders + +if(POLICY CMP0157 AND CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + cmake_policy(SET CMP0157 OLD) +endif() + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/../cmake/modules" + "${CMAKE_SOURCE_DIR}/../../cmake/modules") + +include(SwiftProjectVersion) +project(SwiftDifferentiation + LANGUAGES Swift C + VERSION ${SWIFT_RUNTIME_VERSION}) + +if(NOT PROJECT_IS_TOP_LEVEL) + message(SEND_ERROR "Swift Differentiation must build as a standalone project") +endif() + +set(CMAKE_POSITION_INDEPENDENT_CODE YES) +set(CMAKE_Swift_LANGUAGE_VERSION 5) + +set(${PROJECT_NAME}_SWIFTC_SOURCE_DIR + "${PROJECT_SOURCE_DIR}/../../../" + CACHE FILEPATH "Path to the root source directory of the Swift compiler") + +# Hook point for vendor-specific extensions to the build system +# Allowed extension points: +# - DefaultSettings.cmake +# - Settings.cmake +set(${PROJECT_NAME}_VENDOR_MODULE_DIR "${CMAKE_SOURCE_DIR}/../cmake/modules/vendor" + CACHE FILEPATH "Location for private build system extension") + +find_package(SwiftCore REQUIRED) +find_package(SwiftOverlay REQUIRED) + +include(GNUInstallDirs) + +include(AvailabilityMacros) +include(EmitSwiftInterface) +include(InstallSwiftInterface) +include(PlatformInfo) +include(gyb) +include(ResourceEmbedding) +include(CatalystSupport) + +option(${PROJECT_NAME}_INSTALL_NESTED_SUBDIR "Install libraries under a platform and architecture subdirectory" ON) +set(${PROJECT_NAME}_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}>" CACHE STRING "") +set(${PROJECT_NAME}_INSTALL_SWIFTMODULEDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}>" CACHE STRING "") + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/Settings.cmake" OPTIONAL) + +option(${PROJECT_NAME}_ENABLE_LIBRARY_EVOLUTION "Generate ABI resilient runtime libraries" + ${SwiftCore_ENABLE_LIBRARY_EVOLUTION}) + +option(${PROJECT_NAME}_ENABLE_VECTOR_TYPES "Enable vector support" + ${SwiftCore_ENABLE_VECTOR_TYPES}) + + +add_compile_options( + $<$:-explicit-module-build> + $<$:-nostdlibimport> + $<$:-parse-stdlib> + "$<$:SHELL:-library-level api>" + "$<$:SHELL:-enable-experimental-feature NoncopyableGenerics2>" + "$<$:SHELL:-enable-experimental-feature SuppressedAssociatedTypes>" + "$<$:SHELL:-enable-experimental-feature SE427NoInferenceOnExtension>" + "$<$:SHELL:-enable-experimental-feature NonescapableTypes>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature MemberImportVisibility>" + "$<$:SHELL:-Xfrontend -enforce-exclusivity=unchecked>" + "$<$:SHELL:-Xfrontend -target-min-inlining-version -Xfrontend min>" + $<$,$>:-enable-library-evolution>) + +# LNK4049: symbol 'symbol' defined in 'filename.obj' is imported +# LNK4286: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' +# LNK4217: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' in function 'function' +# +# We cannot selectively filter the linker warnings as we do not use the MSVC +# frontned and `clang-cl` (and `clang`) currently do not support `/WX:nnnn`. As +# a compromise, treat all linker warnings as errors. +# FIXME(#83444) - enable this once we fix DLL storage for +# `_fatalErrorForwardModeDifferentiationDisabled` +# add_link_options($<$:LINKER:/WX>) +# Ensure all symbols are fully resolved on Linux +add_link_options($<$:LINKER:-z,defs>) + +if(SwiftDifferentiation_ENABLE_VECTOR_TYPES) + gyb_expand(SIMDDifferentiation.swift.gyb SIMDDifferentiation.swift) +endif() +gyb_expand(TgmathDerivatives.swift.gyb TgmathDerivatives.swift) +gyb_expand(FloatingPointDifferentiation.swift.gyb + FloatingPointDifferentiation.swift) + +add_library(swift_Differentiation + AnyDifferentiable.swift + ArrayDifferentiation.swift + Differentiable.swift + DifferentialOperators.swift + DifferentiationUtilities.swift + OptionalDifferentiation.swift + $<$:SIMDDifferentiation.swift> + TgmathDerivatives.swift + FloatingPointDifferentiation.swift + linker-support/magic-symbols-for-install-name.c) + +if(APPLE AND BUILD_SHARED_LIBS) + target_link_options(swift_Differentiation PRIVATE + "SHELL:-Xlinker -headerpad_max_install_names") +endif() + +set_target_properties(swift_Differentiation PROPERTIES + Swift_MODULE_NAME _Differentiation) + +target_link_libraries(swift_Differentiation PRIVATE + swiftCore + $<$:swiftAndroid> + $<$:swiftCRT>) + + +install(TARGETS swift_Differentiation + EXPORT SwiftSupplementalTargets + COMPONENT ${PROJECT_NAME}_runtime + ARCHIVE DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swift_Differentiation) +install_swift_interface(swift_Differentiation) + +# Configure plist creation for Darwin platforms. +generate_plist(swift_Differentiation "${CMAKE_PROJECT_VERSION}" swift_Differentiation) +embed_manifest(swift_Differentiation) + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/swift_Differentiation.cmake" OPTIONAL) diff --git a/Runtimes/Supplemental/Distributed/CMakeLists.txt b/Runtimes/Supplemental/Distributed/CMakeLists.txt new file mode 100644 index 0000000000000..bded5de9320fa --- /dev/null +++ b/Runtimes/Supplemental/Distributed/CMakeLists.txt @@ -0,0 +1,157 @@ +cmake_minimum_required(VERSION 3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders + +if(POLICY CMP0157 AND CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + cmake_policy(SET CMP0157 OLD) +endif() + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/../cmake/modules" + "${CMAKE_SOURCE_DIR}/../../cmake/modules") + +include(LanguageVersions) +include(SwiftProjectVersion) +project(SwiftDistributed + LANGUAGES C CXX Swift + VERSION ${SWIFT_RUNTIME_VERSION}) + +if(NOT PROJECT_IS_TOP_LEVEL) + message(SEND_ERROR "Swift Distributed must build as a standalone project") +endif() + +set(CMAKE_POSITION_INDEPENDENT_CODE YES) + +set(CMAKE_C_VISIBILITY_PRESET "hidden") +set(CMAKE_CXX_VISIBILITY_PRESET "hidden") +set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) + +set(${PROJECT_NAME}_SWIFTC_SOURCE_DIR + "${PROJECT_SOURCE_DIR}/../../../" + CACHE FILEPATH "Path to the root source directory of the Swift compiler") + +# Hook point for vendor-specific extensions to the build system +# Allowed extension points: +# - DefaultSettings.cmake +# - Settings.cmake +set(${PROJECT_NAME}_VENDOR_MODULE_DIR "${CMAKE_SOURCE_DIR}/../cmake/modules/vendor" + CACHE FILEPATH "Location for private build system extension") + +find_package(SwiftCore REQUIRED) +find_package(SwiftOverlay REQUIRED) + +include(GNUInstallDirs) + +include(AvailabilityMacros) +include(EmitSwiftInterface) +include(InstallSwiftInterface) +include(PlatformInfo) +include(ResourceEmbedding) +include(CatalystSupport) +include(SwiftCallingConventions) + +option(${PROJECT_NAME}_INSTALL_NESTED_SUBDIR "Install libraries under a platform and architecture subdirectory" ON) +set(${PROJECT_NAME}_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}>" CACHE STRING "") +set(${PROJECT_NAME}_INSTALL_SWIFTMODULEDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}>" CACHE STRING "") + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/Settings.cmake" OPTIONAL) + +option(${PROJECT_NAME}_ENABLE_LIBRARY_EVOLUTION "Generate ABI resilient runtime libraries" + ${SwiftCore_ENABLE_LIBRARY_EVOLUTION}) + +option(${PROJECT_NAME}_ENABLE_PRESPECIALIZATION "Enable generic metadata prespecialization" + ${SwiftCore_ENABLE_PRESPECIALIZATION}) + +add_compile_options( + $<$:-explicit-module-build> + $<$:-nostdlibimport> + $<$:-strict-memory-safety> + "$<$:SHELL:-enable-experimental-feature NoncopyableGenerics2>" + "$<$:SHELL:-enable-experimental-feature SuppressedAssociatedTypes>" + "$<$:SHELL:-enable-experimental-feature SE427NoInferenceOnExtension>" + "$<$:SHELL:-enable-experimental-feature NonescapableTypes>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature InoutLifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependenceMutableAccessors>" + "$<$:SHELL:-enable-upcoming-feature MemberImportVisibility>" + "$<$:SHELL:-Xfrontend -enforce-exclusivity=unchecked>" + "$<$:SHELL:-Xfrontend -target-min-inlining-version -Xfrontend min>" + "$<$:SHELL:-Xfrontend -enable-lexical-lifetimes=false>" + "$<$:-warn-implicit-overrides>" + "$<$,$>:-enable-library-evolution>" + "$<$,$>:SHELL:-Xfrontend -prespecialize-generic-metadata>") + +# LNK4049: symbol 'symbol' defined in 'filename.obj' is imported +# LNK4286: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' +# LNK4217: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' in function 'function' +# +# We cannot selectively filter the linker warnings as we do not use the MSVC +# frontned and `clang-cl` (and `clang`) currently do not support `/WX:nnnn`. As +# a compromise, treat all linker warnings as errors. +add_link_options($<$:LINKER:/WX>) +# Ensure all symbols are fully resolved on Linux +add_link_options($<$:LINKER:-z,defs>) + +add_library(swiftDistributed + DistributedActor.cpp + DistributedActor.swift + DistributedActorSystem.swift + DistributedAssertions.swift + DistributedDefaultExecutor.swift + DistributedMacros.swift + DistributedMetadata.swift + LocalTestingDistributedActorSystem.swift) + +set_target_properties(swiftDistributed PROPERTIES + Swift_MODULE_NAME Distributed) +cmake_policy(GET CMP0157 Policy157Enabled) +if(Policy157Enabled STREQUAL NEW) + set_target_properties(swiftDistributed PROPERTIES + LINKER_LANGUAGE CXX) +endif() + +target_compile_definitions(swiftDistributed PRIVATE + $<$:-DSWIFT_TARGET_LIBRARY_NAME=swiftDistributed>) +target_compile_options(swiftDistributed PRIVATE + $<$:-parse-stdlib> + $<$:SHELL:-enable-experimental-feature AllowUnsafeAttribute>) + +if(APPLE AND BUILD_SHARED_LIBS) + target_link_options(swiftDistributed PRIVATE + "SHELL:-Xlinker -headerpad_max_install_names") +endif() + +target_include_directories(swiftDistributed PRIVATE + # FIXME: Use of `swift/Runtime/...`, `swift/ABI/...`, and `swift/Demangling/...` + "${${PROJECT_NAME}_SWIFTC_SOURCE_DIR}/include" + # For `swift/runtime/CMakeConfig.h`, which should be generated + # by the Core build system. + "${${PROJECT_NAME}_PATH_TO_SWIFT_RUNTIME_HEADERS}" + # For llvm/support headers + "${PROJECT_SOURCE_DIR}/include") + +target_link_libraries(swiftDistributed PRIVATE + swiftShims + swiftCore + swift_Concurrency + swift_Builtin_float + $<$:swiftAndroid> + $<$:swiftWinSDK>) + +install(TARGETS swiftDistributed + EXPORT SwiftDistributedTargets + COMPONENT ${PROJECT_NAME}_runtime + ARCHIVE DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swiftDistributed) +install_swift_interface(swiftDistributed) + +# Configure plist creation for Darwin platforms. +generate_plist(swiftDistributed "${CMAKE_PROJECT_VERSION}" swiftDistributed) +embed_manifest(swiftDistributed) + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/swiftDistributed.cmake" OPTIONAL) diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/apple-common.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/apple-common.cmake new file mode 100644 index 0000000000000..dd56f854c2593 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/apple-common.cmake @@ -0,0 +1,7 @@ +set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "") +set(BUILD_SHARED_LIBS YES CACHE BOOL "") +set(SwiftDistributed_INSTALL_NESTED_SUBDIR OFF CACHE BOOL "") + +set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_C_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_Swift_FLAGS_MINSIZEREL "-Osize -g" CACHE STRING "") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..40c066b36d36d --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake new file mode 100644 index 0000000000000..9c88909940f29 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake new file mode 100644 index 0000000000000..7ba1f319b733f --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake @@ -0,0 +1,14 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR freestanding CACHE STRING "") +set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "") +set(SwiftDistributed_SINGLE_THREADED_MODE YES CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake new file mode 100644 index 0000000000000..040412a0a2c0f --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftDistributed_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftDistributed_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftDistributed_COMPILER_VARIANT_TARGET "arm64-apple-ios${SwiftDistributed_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..e631faa114542 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake new file mode 100644 index 0000000000000..659e8edd9fbfc --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..d1db08d6ae896 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake new file mode 100644 index 0000000000000..1dd20403ef33e --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake new file mode 100644 index 0000000000000..6d6cc83c1b25e --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64_32 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake new file mode 100644 index 0000000000000..d570fc0c249e4 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake new file mode 100644 index 0000000000000..bc5bc7f02f308 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftDistributed_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftDistributed_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftDistributed_COMPILER_VARIANT_TARGET "arm64e-apple-ios${SwiftDistributed_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake new file mode 100644 index 0000000000000..e8789681dd0e6 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-XROS.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-XROS.cmake new file mode 100644 index 0000000000000..4bd9e5c555498 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-XROS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR xros CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake new file mode 100644 index 0000000000000..34175778a6c22 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..66208927e0514 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake new file mode 100644 index 0000000000000..ec3cec9d72854 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftDistributed_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftDistributed_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftDistributed_COMPILER_VARIANT_TARGET "x86_64-apple-ios${SwiftDistributed_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..9be3fa6a6d8ec --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake new file mode 100644 index 0000000000000..c215b37455d42 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..15c21b7961e03 --- /dev/null +++ b/Runtimes/Supplemental/Distributed/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftDistributed_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftDistributed_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/CMakeLists.txt b/Runtimes/Supplemental/Observation/CMakeLists.txt new file mode 100644 index 0000000000000..98ce40a292f5e --- /dev/null +++ b/Runtimes/Supplemental/Observation/CMakeLists.txt @@ -0,0 +1,134 @@ +cmake_minimum_required(VERSION 3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders + +if(POLICY CMP0157 AND CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + cmake_policy(SET CMP0157 OLD) +endif() + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/../cmake/modules" + "${CMAKE_SOURCE_DIR}/../../cmake/modules") + +include(LanguageVersions) +include(SwiftProjectVersion) +project(SwiftObservation + LANGUAGES Swift CXX + VERSION ${SWIFT_RUNTIME_VERSION}) + +if(NOT PROJECT_IS_TOP_LEVEL) + message(SEND_ERROR "Swift Observation must build as a standalone project") +endif() + +set(CMAKE_POSITION_INDEPENDENT_CODE YES) + +set(${PROJECT_NAME}_SWIFTC_SOURCE_DIR + "${PROJECT_SOURCE_DIR}/../../../" + CACHE FILEPATH "Path to the root source directory of the Swift compiler") + +# Hook point for vendor-specific extensions to the build system +# Allowed extension points: +# - DefaultSettings.cmake +# - Settings.cmake +set(${PROJECT_NAME}_VENDOR_MODULE_DIR "${CMAKE_SOURCE_DIR}/../cmake/modules/vendor" + CACHE FILEPATH "Location for private build system extension") + +find_package(SwiftCore REQUIRED) +find_package(SwiftOverlay REQUIRED) + +include(GNUInstallDirs) + +include(AvailabilityMacros) +include(EmitSwiftInterface) +include(InstallSwiftInterface) +include(PlatformInfo) +include(gyb) +include(ResourceEmbedding) +include(CatalystSupport) + +option(${PROJECT_NAME}_INSTALL_NESTED_SUBDIR "Install libraries under a platform and architecture subdirectory" ON) +set(${PROJECT_NAME}_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}>" CACHE STRING "") +set(${PROJECT_NAME}_INSTALL_SWIFTMODULEDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}>" CACHE STRING "") + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/Settings.cmake" OPTIONAL) + +option(${PROJECT_NAME}_ENABLE_LIBRARY_EVOLUTION "Generate ABI resilient runtime libraries" + ${SwiftCore_ENABLE_LIBRARY_EVOLUTION}) + +option(${PROJECT_NAME}_ENABLE_PRESPECIALIZATION "Enable generic metadata prespecialization" + ${SwiftCore_ENABLE_PRESPECIALIZATION}) + +add_compile_options( + $<$:-explicit-module-build> + $<$:-nostdlibimport> + "$<$:SHELL:-Xfrontend -enforce-exclusivity=unchecked>" + "$<$:SHELL:-Xfrontend -target-min-inlining-version -Xfrontend min>" + "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>" + "$<$:SHELL:-enable-experimental-feature NoncopyableGenerics2>" + "$<$:SHELL:-enable-experimental-feature SuppressedAssociatedTypes>" + "$<$:SHELL:-enable-experimental-feature SE427NoInferenceOnExtension>" + "$<$:SHELL:-enable-experimental-feature NonescapableTypes>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature InoutLifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependenceMutableAccessors>" + "$<$:SHELL:-enable-upcoming-feature MemberImportVisibility>" + "$<$:SHELL:-enable-experimental-feature Macros>" + "$<$:SHELL:-enable-experimental-feature ExtensionMacros>" + "$<$:SHELL:-Xfrontend -enable-lexical-lifetimes=false>" + "$<$:-warn-implicit-overrides>" + "$<$,$>:-enable-library-evolution>" + "$<$,$>:SHELL:-Xfrontend -prespecialize-generic-metadata>") + +# LNK4049: symbol 'symbol' defined in 'filename.obj' is imported +# LNK4286: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' +# LNK4217: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' in function 'function' +# +# We cannot selectively filter the linker warnings as we do not use the MSVC +# frontned and `clang-cl` (and `clang`) currently do not support `/WX:nnnn`. As +# a compromise, treat all linker warnings as errors. +add_link_options($<$:LINKER:/WX>) +# Ensure all symbols are fully resolved on Linux +add_link_options($<$:LINKER:-z,defs>) + +add_library(swiftObservation + Sources/Observation/Locking.swift + Sources/Observation/Observable.swift + Sources/Observation/ObservationRegistrar.swift + Sources/Observation/ObservationTracking.swift + Sources/Observation/Observations.swift + Sources/Observation/ThreadLocal.swift + Sources/Observation/ThreadLocal.cpp) +set_target_properties(swiftObservation PROPERTIES + Swift_MODULE_NAME Observation) +# FIXME: We should split out the parts that are needed by the runtime to avoid +# pulling in headers from the compiler. +target_include_directories(swiftObservation PRIVATE + "${${PROJECT_NAME}_SWIFTC_SOURCE_DIR}/include" + # For `swift/runtime/CMakeConfig.h`, which should be generated + # by the Core build system. + "${${PROJECT_NAME}_PATH_TO_SWIFT_RUNTIME_HEADERS}") +target_link_libraries(swiftObservation PRIVATE + swiftShims + swiftCore + swift_Concurrency + $<$:swiftAndroid> + $<$:swiftGlibc> + $<$:swiftWinSDK>) + +install(TARGETS swiftObservation + EXPORT SwiftObservationTargets + COMPONENT ${PROJECT_NAME}_runtime + ARCHIVE DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swiftObservation) +install_swift_interface(swiftObservation) + +# Configure plist creation for Darwin platforms. +generate_plist(swiftObservation "${CMAKE_PROJECT_VERSION}" swiftObservation) +embed_manifest(swiftObservation) + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/swiftObservation.cmake" OPTIONAL) diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/apple-common.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/apple-common.cmake new file mode 100644 index 0000000000000..f037c7905ee3e --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/apple-common.cmake @@ -0,0 +1,7 @@ +set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "") +set(BUILD_SHARED_LIBS YES CACHE BOOL "") +set(SwiftObservation_INSTALL_NESTED_SUBDIR OFF CACHE BOOL "") + +set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_C_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_Swift_FLAGS_MINSIZEREL "-Osize -g" CACHE STRING "") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..50fd46931d8e7 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake new file mode 100644 index 0000000000000..2448ed4933479 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake new file mode 100644 index 0000000000000..c8d88765e5610 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake @@ -0,0 +1,14 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR freestanding CACHE STRING "") +set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "") +set(SwiftObservation_SINGLE_THREADED_MODE YES CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake new file mode 100644 index 0000000000000..5506f080048a3 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftObservation_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftObservation_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftObservation_COMPILER_VARIANT_TARGET "arm64-apple-ios${SwiftObservation_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..a191012809e71 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake new file mode 100644 index 0000000000000..72dd460ceb151 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..ac200660e1315 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake new file mode 100644 index 0000000000000..64aa23c220f4c --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake new file mode 100644 index 0000000000000..4d085c3b74d53 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64_32 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake new file mode 100644 index 0000000000000..c46319a503423 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake new file mode 100644 index 0000000000000..2aa67902a3979 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftObservation_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftObservation_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftObservation_COMPILER_VARIANT_TARGET "arm64e-apple-ios${SwiftObservation_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake new file mode 100644 index 0000000000000..40d8ca3e36303 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-XROS.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-XROS.cmake new file mode 100644 index 0000000000000..e5c3feef70c74 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-XROS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR xros CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake new file mode 100644 index 0000000000000..2b84e565919f3 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..d4f7b5bc67a6f --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake new file mode 100644 index 0000000000000..3288b4627c585 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftObservation_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftObservation_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftObservation_COMPILER_VARIANT_TARGET "x86_64-apple-ios${SwiftObservation_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..00ae81dd841b5 --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake new file mode 100644 index 0000000000000..d86c8c003a16c --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..27f23c542db4e --- /dev/null +++ b/Runtimes/Supplemental/Observation/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftObservation_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftObservation_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Readme.md b/Runtimes/Supplemental/Readme.md new file mode 100644 index 0000000000000..693115cf39734 --- /dev/null +++ b/Runtimes/Supplemental/Readme.md @@ -0,0 +1,52 @@ +# Swift Supplemental Libraries + +The supplemental libraries are all libraries that are not one of the Core or +overlay libraries. Each supplemental library builds as an independent project. + +The supplemental libraries are: + - CxxInterop + - Differentiation + - Distributed + - Observation + - StringProcessing + - Runtime + - Synchronization + +The top-level Supplemental CMakeLists supplies a super-build pattern for +configuring and compiling each of the supplemental library projects through a +single CMake invocation. The `Swift_ENABLE_RUNTIMES` CMake option enables the +specified supplemental libraries. All libraries configured this way are built +with the same compilers, against the same sysroot, with the same target triple +and installed into the same location. + +## Super-Build + +Configuring each project independently is tedious. The Supplemental directory +contains a Super-Build CMakeLists that invokes the build of each of the +supplemental libraries in the appropriate order, simplifying the process of +building each library. + +Important configuration variables: + - `Swift_ENABLE_RUNTIMES`: Used to configure which runtime libraries are built. + +The super-build forwards the following variables to each sub-project +unconditionally: + - `BUILD_SHARED_LIBS` + - `CMAKE_BUILD_TYPE` + - `CMAKE_INSTALL_PREFIX` + - `CMAKE_COLOR_DIAGNOSTICS` + - `CMAKE_C_COMPILER` + - `CMAKE_C_COMPILER_TARGET` + - `CMAKE_CXX_COMPILER` + - `CMAKE_CXX_COMPILER_TARGET` + - `CMAKE_Swift_COMPILER` + - `CMAKE_Swift_COMPILER_TARGET` + +If set, the super-build forwards the following values to each sub-project: + + - `SwiftCore_DIR`: Path to the SwiftCore build directory + - `CMAKE_MAKE_PROGRAM`: Path to `ninja` + +The super-build is for convenience. If more fine-grained control is desired for +configuring a specific runtime library, you may configure that library +independently. diff --git a/Runtimes/Supplemental/Runtime/CMakeLists.txt b/Runtimes/Supplemental/Runtime/CMakeLists.txt new file mode 100644 index 0000000000000..099fa4945bea2 --- /dev/null +++ b/Runtimes/Supplemental/Runtime/CMakeLists.txt @@ -0,0 +1,174 @@ +cmake_minimum_required(VERSION 3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders + +if(POLICY CMP0157 AND CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + cmake_policy(SET CMP0157 OLD) +endif() + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/../cmake/modules" + "${CMAKE_SOURCE_DIR}/../../cmake/modules") + +include(SwiftProjectVersion) +project(SwiftRuntime + LANGUAGES Swift CXX + VERSION ${SWIFT_RUNTIME_VERSION}) + +if(NOT PROJECT_IS_TOP_LEVEL) + message(SEND_ERROR "Swift Runtime must build as a standalone project") +endif() + +include(AsmSupport) +enable_asm_language() + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) +set(CMAKE_POSITION_INDEPENDENT_CODE YES) + +set(${PROJECT_NAME}_SWIFTC_SOURCE_DIR + "${PROJECT_SOURCE_DIR}/../../../" + CACHE FILEPATH "Path to the root source directory of the Swift compiler") + +# Hook point for vendor-specific extensions to the build system +# Allowed extension points: +# - DefaultSettings.cmake +# - Settings.cmake +set(${PROJECT_NAME}_VENDOR_MODULE_DIR "${CMAKE_SOURCE_DIR}/../cmake/modules/vendor" + CACHE FILEPATH "Location for private build system extension") + +find_package(SwiftCore REQUIRED) +find_package(SwiftOverlay REQUIRED) +#find_package(SwiftDarwin) + +include(GNUInstallDirs) + +include(AvailabilityMacros) +include(EmitSwiftInterface) +include(InstallSwiftInterface) +include(PlatformInfo) +include(gyb) +include(ResourceEmbedding) +include(CatalystSupport) + +option(${PROJECT_NAME}_INSTALL_NESTED_SUBDIR "Install libraries under a platform and architecture subdirectory" ON) +set(${PROJECT_NAME}_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}>" CACHE STRING "") +set(${PROJECT_NAME}_INSTALL_SWIFTMODULEDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}>" CACHE STRING "") + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/Settings.cmake" OPTIONAL) + +option(${PROJECT_NAME}_ENABLE_LIBRARY_EVOLUTION "Generate ABI resilient runtime libraries" + ${SwiftCore_ENABLE_LIBRARY_EVOLUTION}) + +option(${PROJECT_NAME}_ENABLE_PRESPECIALIZATION "Enable generic metadata prespecialization" + ${SwiftCore_ENABLE_PRESPECIALIZATION}) + +add_compile_options( + $<$:-explicit-module-build> + $<$:-nostdlibimport> + $<$:-parse-stdlib> + "$<$:SHELL:-swift-version 5>" + "$<$:SHELL:-Xfrontend -enforce-exclusivity=unchecked>" + "$<$:SHELL:-Xfrontend -target-min-inlining-version -Xfrontend min>" + "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>" + "$<$:SHELL:-enable-experimental-feature NoncopyableGenerics2>" + "$<$:SHELL:-enable-experimental-feature SuppressedAssociatedTypes>" + "$<$:SHELL:-enable-experimental-feature SE427NoInferenceOnExtension>" + "$<$:SHELL:-enable-experimental-feature NonescapableTypes>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature InoutLifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependenceMutableAccessors>" + "$<$:SHELL:-disable-upcoming-feature MemberImportVisibility>" + "$<$:SHELL:-Xfrontend -enable-lexical-lifetimes=false>" + "$<$:SHELL:-Xfrontend -enable-ossa-modules>" + "$<$:-warn-implicit-overrides>" + "$<$:-cxx-interoperability-mode=default>" + "$<$:SHELL:-Xfrontend -experimental-spi-only-imports>" + "$<$,$>:-enable-library-evolution>" + "$<$,$>:SHELL:-Xfrontend -prespecialize-generic-metadata>") + +# LNK4049: symbol 'symbol' defined in 'filename.obj' is imported +# LNK4286: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' +# LNK4217: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' in function 'function' +# +# We cannot selectively filter the linker warnings as we do not use the MSVC +# frontned and `clang-cl` (and `clang`) currently do not support `/WX:nnnn`. As +# a compromise, treat all linker warnings as errors. +add_link_options($<$:LINKER:/WX>) +# Ensure all symbols are fully resolved on Linux +add_link_options($<$:LINKER:-z,defs>) + +add_library(swiftRuntime + Address.swift + Backtrace.swift + Backtrace+Codable.swift + BacktraceFormatter.swift + Base64.swift + ByteSwapping.swift + CachingMemoryReader.swift + CompactBacktrace.swift + CompactImageMap.swift + Compression.swift + Context.swift + CoreSymbolication.swift + Dwarf.swift + EightByteBuffer.swift + Elf.swift + ElfImageCache.swift + FramePointerUnwinder.swift + Image.swift + ImageMap.swift + ImageMap+Darwin.swift + ImageMap+Linux.swift + ImageSource.swift + Libc.swift + LimitSequence.swift + MemoryReader.swift + OSReleaseScanner.swift + ProcMapsScanner.swift + Registers.swift + Runtime.swift + RichFrame.swift + SymbolicatedBacktrace.swift + Utils.swift + Win32Extras.cpp + get-cpu-context.${SWIFT_ASM_EXT}) + +set_target_properties(swiftRuntime PROPERTIES + Swift_MODULE_NAME Runtime) +target_compile_definitions(swiftRuntime PRIVATE + SWIFT_ASM_AVAILABLE) +target_compile_options(swiftRuntime PRIVATE + "$<$:SHELL:-Xcc -fmodule-map-file=${CMAKE_CURRENT_SOURCE_DIR}/modules/module.modulemap>") +# FIXME: We should split out the parts that are needed by the runtime to avoid +# pulling in headers from the compiler. +target_include_directories(swiftRuntime PRIVATE + "${${PROJECT_NAME}_SWIFTC_SOURCE_DIR}/include") +target_link_libraries(swiftRuntime PRIVATE + swiftShims + swiftCore + swift_Concurrency + swiftCxxStdlib + $<$:swiftAndroid> + #$<$:swiftDarwin> + $<$:swiftGlibc> + $<$:swiftWinSDK>) + +install(TARGETS swiftRuntime + EXPORT SwiftRuntimeTargets + COMPONENT ${PROJECT_NAME}_runtime + ARCHIVE DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swiftRuntime) +install_swift_interface(swiftRuntime) + +# Configure plist creation for Darwin platforms. +generate_plist(swiftRuntime "${CMAKE_PROJECT_VERSION}" swiftRuntime) +embed_manifest(swiftRuntime) + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/SwiftRuntime.cmake" OPTIONAL) diff --git a/Runtimes/Supplemental/StringProcessing/CMakeLists.txt b/Runtimes/Supplemental/StringProcessing/CMakeLists.txt index 43d1e7890c208..709ba16918411 100644 --- a/Runtimes/Supplemental/StringProcessing/CMakeLists.txt +++ b/Runtimes/Supplemental/StringProcessing/CMakeLists.txt @@ -1,33 +1,74 @@ cmake_minimum_required(VERSION 3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders -set(CMAKE_POSITION_INDEPENDENT_CODE YES) +if(POLICY CMP0157 AND CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + cmake_policy(SET CMP0157 OLD) +endif() -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../cmake/modules") -if($ENV{BUILD_NUMBER}) - math(EXPR BUILD_NUMBER "$ENV{BUILD_NUMBER} % 65535") - set(BUILD_NUMBER ".${BUILD_NUMBER}") -endif() +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/../cmake/modules" + "${CMAKE_SOURCE_DIR}/../../cmake/modules") + +include(LanguageVersions) +include(SwiftProjectVersion) project(SwiftStringProcessing LANGUAGES Swift C - VERSION 6.1.0${BUILD_NUMBER}) + VERSION ${SWIFT_RUNTIME_VERSION}) if(NOT PROJECT_IS_TOP_LEVEL) message(FATAL_ERROR "Swift StringProcessing must build as a standalone project") endif() +set(CMAKE_POSITION_INDEPENDENT_CODE YES) + set(${PROJECT_NAME}_SWIFTC_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../../../" CACHE FILEPATH "Path to the root source directory of the Swift compiler") -find_package(SwiftCore) +find_package(SwiftCore REQUIRED) + +include(GNUInstallDirs) include(AvailabilityMacros) +include(EmitSwiftInterface) +include(InstallSwiftInterface) +include(PlatformInfo) +include(ResourceEmbedding) + +option(${PROJECT_NAME}_INSTALL_NESTED_SUBDIR "Install libraries under a platform and architecture subdirectory" ON) +set(${PROJECT_NAME}_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}>") +set(${PROJECT_NAME}_INSTALL_SWIFTMODULEDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}>") + +option(${PROJECT_NAME}_ENABLE_LIBRARY_EVOLUTION "Generate ABI resilient runtime libraries" + ${SwiftCore_ENABLE_LIBRARY_EVOLUTION}) + +option(${PROJECT_NAME}_ENABLE_PRESPECIALIZATION "Enable generic metadata prespecialization" + ${SwiftCore_ENABLE_PRESPECIALIZATION}) add_compile_options( $<$:-explicit-module-build> $<$:-nostdlibimport> - "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>") + "$<$:SHELL:-Xfrontend -disable-implicit-concurrency-module-import>" + "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>" + "$<$:-warn-implicit-overrides>" + "$<$,$>:-enable-library-evolution>" + "$<$,$>:SHELL:-Xfrontend -prespecialize-generic-metadata>") + +# LNK4049: symbol 'symbol' defined in 'filename.obj' is imported +# LNK4286: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' +# LNK4217: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' in function 'function' +# +# We cannot selectively filter the linker warnings as we do not use the MSVC +# frontned and `clang-cl` (and `clang`) currently do not support `/WX:nnnn`. As +# a compromise, treat all linker warnings as errors. +add_link_options($<$:LINKER:/WX>) +# Ensure all symbols are fully resolved on Linux +add_link_options($<$:LINKER:-z,defs>) add_subdirectory(_RegexParser) add_subdirectory(_StringProcessing) diff --git a/Runtimes/Supplemental/StringProcessing/RegexBuilder/CMakeLists.txt b/Runtimes/Supplemental/StringProcessing/RegexBuilder/CMakeLists.txt index c42a60778f9da..a76f8550d2bf5 100644 --- a/Runtimes/Supplemental/StringProcessing/RegexBuilder/CMakeLists.txt +++ b/Runtimes/Supplemental/StringProcessing/RegexBuilder/CMakeLists.txt @@ -13,3 +13,12 @@ target_link_libraries(swiftRegexBuilder PRIVATE swift_RegexParser swift_StringProcessing swiftCore) + +install(TARGETS swiftRegexBuilder + ARCHIVE DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swiftRegexBuilder) +install_swift_interface(swiftRegexBuilder) + +embed_manifest(swiftRegexBuilder) diff --git a/Runtimes/Supplemental/StringProcessing/_RegexParser/CMakeLists.txt b/Runtimes/Supplemental/StringProcessing/_RegexParser/CMakeLists.txt index 0285297831a70..0fed65de6ceb5 100644 --- a/Runtimes/Supplemental/StringProcessing/_RegexParser/CMakeLists.txt +++ b/Runtimes/Supplemental/StringProcessing/_RegexParser/CMakeLists.txt @@ -31,7 +31,17 @@ add_library(swift_RegexParser Utility/Errors.swift Utility/MissingUnicode.swift) -target_link_libraries(swift_RegexParser PRIVATE swiftCore) +target_link_libraries(swift_RegexParser PRIVATE + swiftCore) set_target_properties(swift_RegexParser PROPERTIES Swift_MODULE_NAME _RegexParser) + +install(TARGETS swift_RegexParser + ARCHIVE DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swift_RegexParser) +install_swift_interface(swift_RegexParser) + +embed_manifest(swift_RegexParser) diff --git a/Runtimes/Supplemental/StringProcessing/_StringProcessing/CMakeLists.txt b/Runtimes/Supplemental/StringProcessing/_StringProcessing/CMakeLists.txt index 81b3e408897d9..99a0420852532 100644 --- a/Runtimes/Supplemental/StringProcessing/_StringProcessing/CMakeLists.txt +++ b/Runtimes/Supplemental/StringProcessing/_StringProcessing/CMakeLists.txt @@ -78,3 +78,12 @@ set_target_properties(swift_StringProcessing PROPERTIES target_link_libraries(swift_StringProcessing PRIVATE swift_RegexParser swiftCore) + +install(TARGETS swift_StringProcessing + ARCHIVE DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swift_StringProcessing) +install_swift_interface(swift_StringProcessing) + +embed_manifest(swift_StringProcessing) diff --git a/Runtimes/Supplemental/Synchronization/CMakeLists.txt b/Runtimes/Supplemental/Synchronization/CMakeLists.txt new file mode 100644 index 0000000000000..2ab0500c53d94 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/CMakeLists.txt @@ -0,0 +1,200 @@ +cmake_minimum_required(VERSION 3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders + +if(POLICY CMP0157 AND CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + cmake_policy(SET CMP0157 OLD) +endif() + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/../cmake/modules" + "${CMAKE_SOURCE_DIR}/../../cmake/modules") + +include(LanguageVersions) +include(SwiftProjectVersion) +project(SwiftSynchronization + LANGUAGES Swift + VERSION ${SWIFT_RUNTIME_VERSION}) + +# FIXME(compnerd) this is a workaround for `GNUInstallDirs` which cannot be used +# with a pure Swift project. +enable_language(C) + +if(NOT PROJECT_IS_TOP_LEVEL) + message(SEND_ERROR "Swift Synchronization must build as a standalone project") +endif() + +set(CMAKE_POSITION_INDEPENDENT_CODE YES) + +set(${PROJECT_NAME}_SWIFTC_SOURCE_DIR + "${PROJECT_SOURCE_DIR}/../../../" + CACHE FILEPATH "Path to the root source directory of the Swift compiler") + +# Hook point for vendor-specific extensions to the build system +# Allowed extension points: +# - DefaultSettings.cmake +# - Settings.cmake +set(${PROJECT_NAME}_VENDOR_MODULE_DIR "${CMAKE_SOURCE_DIR}/../cmake/modules/vendor" + CACHE FILEPATH "Location for private build system extension") + +find_package(SwiftCore REQUIRED) +find_package(SwiftOverlay) + +include(GNUInstallDirs) + +include(AvailabilityMacros) +include(EmitSwiftInterface) +include(InstallSwiftInterface) +include(PlatformInfo) +include(gyb) +include(ResourceEmbedding) +include(CatalystSupport) + +option(${PROJECT_NAME}_INSTALL_NESTED_SUBDIR "Install libraries under a platform and architecture subdirectory" ON) +set(${PROJECT_NAME}_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}>" CACHE STRING "") +set(${PROJECT_NAME}_INSTALL_SWIFTMODULEDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}>" CACHE STRING "") + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/Settings.cmake" OPTIONAL) + +option(${PROJECT_NAME}_ENABLE_LIBRARY_EVOLUTION "Generate ABI resilient runtime libraries" + ${SwiftCore_ENABLE_LIBRARY_EVOLUTION}) + +option(${PROJECT_NAME}_ENABLE_PRESPECIALIZATION "Enable generic metadata prespecialization" + ${SwiftCore_ENABLE_PRESPECIALIZATION}) + +option(${PROJECT_NAME}_SINGLE_THREADED_MODE "Build Synchronization assuming it will be used in an environment with only a single thread" OFF) + +add_compile_options( + $<$:-explicit-module-build> + $<$:-nostdlibimport> + $<$:-enable-builtin-module> + $<$:-strict-memory-safety> + "$<$:SHELL:-enable-experimental-feature NoncopyableGenerics2>" + "$<$:SHELL:-enable-experimental-feature SuppressedAssociatedTypes>" + "$<$:SHELL:-enable-experimental-feature SE427NoInferenceOnExtension>" + "$<$:SHELL:-enable-experimental-feature NonescapableTypes>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature InoutLifetimeDependence>" + "$<$:SHELL:-enable-experimental-feature LifetimeDependenceMutableAccessors>" + "$<$:SHELL:-enable-upcoming-feature MemberImportVisibility>" + "$<$:SHELL:-enable-experimental-feature RawLayout>" + "$<$:SHELL:-enable-experimental-feature StaticExclusiveOnly>" + "$<$:SHELL:-enable-experimental-feature Extern>" + "$<$:SHELL:-runtime-compatibility-version none>" + "$<$:SHELL:-Xfrontend -disable-autolinking-runtime-compatibility-dynamic-replacements>" + "$<$:SHELL:-Xfrontend -disable-autolinking-runtime-compatibility-concurrency>" + "$<$:SHELL:-Xfrontend -disable-implicit-concurrency-module-import>" + "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>" + "$<$:SHELL:-Xfrontend -enforce-exclusivity=unchecked>" + "$<$:SHELL:-Xfrontend -target-min-inlining-version -Xfrontend min>" + "$<$:SHELL:-Xfrontend -enable-lexical-lifetimes=false>" + "$<$:-warn-implicit-overrides>" + "$<$,$>:-enable-library-evolution>" + "$<$,$>:SHELL:-Xfrontend -prespecialize-generic-metadata>") + +# LNK4049: symbol 'symbol' defined in 'filename.obj' is imported +# LNK4286: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' +# LNK4217: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' in function 'function' +# +# We cannot selectively filter the linker warnings as we do not use the MSVC +# frontned and `clang-cl` (and `clang`) currently do not support `/WX:nnnn`. As +# a compromise, treat all linker warnings as errors. +add_link_options($<$:LINKER:/WX>) +# Ensure all symbols are fully resolved on Linux +add_link_options($<$:LINKER:-z,defs>) + +gyb_expand(Atomics/AtomicIntegers.swift.gyb Atomics/AtomicIntegers.swift) +gyb_expand(Atomics/AtomicStorage.swift.gyb Atomics/AtomicStorage.swift) + +add_library(swiftSynchronization + Atomics/Atomic.swift + Atomics/AtomicBool.swift + Atomics/AtomicFloats.swift + Atomics/AtomicLazyReference.swift + Atomics/AtomicMemoryOrderings.swift + Atomics/AtomicOptional.swift + Atomics/AtomicPointers.swift + Atomics/AtomicRepresentable.swift + Atomics/WordPair.swift + Atomics/AtomicStorage.swift + Atomics/AtomicIntegers.swift + Cell.swift) + +# Determine Mutex definition +if(${PROJECT_NAME}_SINGLE_THREADED_MODE) + target_sources(swiftSynchronization PRIVATE + Mutex/MutexUnavailable.swift) +else() + target_sources(swiftSynchronization PRIVATE + Mutex/Mutex.swift + $<$:Mutex/DarwinImpl.swift> + $<$:Mutex/LinuxImpl.swift> + $<$:Mutex/SpinLoopHint.swift> + $<$:Mutex/WindowsImpl.swift>) +endif() + +set_target_properties(swiftSynchronization PROPERTIES + Swift_MODULE_NAME Synchronization) + +target_link_libraries(swiftSynchronization PRIVATE + swiftCore + $<$:swiftAndroid> + $<$:swiftDarwin> + $<$:ClangModules>) + +if(WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL i686) + # FIXME(#83765) `-whole-module-optimization` should not be needed. However, + # without this, we will not properly optimize. On Windows i686 in particular, + # we observe the formation of `__atomic_load` calls rather than the expected + # native sequence. + cmake_policy(GET CMP0157 _PolicyCMP0157) + if(_PolicyCMP0157 STREQUAL NEW) + set_target_properties(swiftSynchronization PROPERTIES + Swift_COMPILATION_MODE wholemodule) + else() + target_compile_options(swiftSynchronization PRIVATE + $<$:-whole-module-optimization>) + endif() + + # WMO requires the early-swift-driver to be usable, which is not yet ready on + # Windows. Workaround this by creating an empty stub for swiftCore, removing + # the import library from the command line, and adding the library search path + # associated with it. Due to the autolink, we will still link against the + # library from the correct location but will also use an empty file for the + # additional input allowing us to perform the WMO. + if(CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + get_target_property(_swiftCore_IMPORTED_IMPLIB_RELEASE swiftCore + IMPORTED_IMPLIB_RELEASE) + if(_swiftCore_IMPORTED_IMPLIB_RELEASE) + # Compute the library directory to allow us to find the import library by + # name. + get_filename_component(_swiftCore_IMPORTED_IMPLIB_RELEASE_DIRNAME + ${_swiftCore_IMPORTED_IMPLIB_RELEASE} DIRECTORY) + # Create a (empty) stub library + file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/swiftCoreStub.lib") + # Replace the import library with a stub to bypass the driver and frontend. + # Add the library search path to allow linking via autolinking. + set_target_properties(swiftCore PROPERTIES + IMPORTED_IMPLIB_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/swiftCoreStub.lib" + INTERFACE_LINK_DIRECTORIES ${_swiftCore_IMPORTED_IMPLIB_RELEASE_DIRNAME}) + endif() + endif() +endif() + +install(TARGETS swiftSynchronization + EXPORT SwiftSynchronizationTargets + COMPONENT ${PROJECT_NAME}_runtime + ARCHIVE DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swiftSynchronization) +install_swift_interface(swiftSynchronization) + +# Configure plist creation for Darwin platforms. +generate_plist(swiftSynchronization "${CMAKE_PROJECT_VERSION}" swiftSynchronization) +embed_manifest(swiftSynchronization) + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/swiftSynchronization.cmake" OPTIONAL) diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/apple-common.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/apple-common.cmake new file mode 100644 index 0000000000000..045a720d448c1 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/apple-common.cmake @@ -0,0 +1,7 @@ +set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "") +set(BUILD_SHARED_LIBS YES CACHE BOOL "") +set(SwiftSynchronization_INSTALL_NESTED_SUBDIR OFF CACHE BOOL "") + +set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_C_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_Swift_FLAGS_MINSIZEREL "-Osize -g" CACHE STRING "") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..4ddb5b22db83d --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake new file mode 100644 index 0000000000000..767b7fb22d150 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake new file mode 100644 index 0000000000000..4f8d5724c2fe3 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake @@ -0,0 +1,14 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR freestanding CACHE STRING "") +set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "") +set(SwiftSynchronization_SINGLE_THREADED_MODE YES CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake new file mode 100644 index 0000000000000..22e345648bd07 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftSynchronization_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftSynchronization_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftSynchronization_COMPILER_VARIANT_TARGET "arm64-apple-ios${SwiftSynchronization_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..f9856d3590382 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake new file mode 100644 index 0000000000000..e0c728b134474 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..827858a5faea2 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake new file mode 100644 index 0000000000000..2b5e04dac0611 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake new file mode 100644 index 0000000000000..f50e5881a1fc5 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64_32 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake new file mode 100644 index 0000000000000..4cb83abac73b4 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake new file mode 100644 index 0000000000000..fc55849dde75e --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftSynchronization_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftSynchronization_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftSynchronization_COMPILER_VARIANT_TARGET "arm64e-apple-ios${SwiftSynchronization_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake new file mode 100644 index 0000000000000..ee82d8970b384 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-XROS.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-XROS.cmake new file mode 100644 index 0000000000000..bd57c6bafe351 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-XROS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR xros CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake new file mode 100644 index 0000000000000..93dac83fdf3b3 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..5219f6e6e7571 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake new file mode 100644 index 0000000000000..8f0e265e9a99b --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftSynchronization_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftSynchronization_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftSynchronization_COMPILER_VARIANT_TARGET "x86_64-apple-ios${SwiftSynchronization_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..3c79d9826a06b --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake new file mode 100644 index 0000000000000..23f9cdcbc67e4 --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..8758aa157c97f --- /dev/null +++ b/Runtimes/Supplemental/Synchronization/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftSynchronization_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftSynchronization_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/CMakeLists.txt b/Runtimes/Supplemental/Volatile/CMakeLists.txt new file mode 100644 index 0000000000000..ad8fc2428852a --- /dev/null +++ b/Runtimes/Supplemental/Volatile/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.29) +# TODO before requiring CMake 4.1 or later +# and/or enforcing CMP0195, please check/update +# the implementation of `emit_swift_interface` +# in `EmitSwiftInterface.cmake` +# to ensure it keeps laying down nested swiftmodule folders + +if(POLICY CMP0157 AND CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + cmake_policy(SET CMP0157 OLD) +endif() + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/../cmake/modules" + "${CMAKE_SOURCE_DIR}/../../cmake/modules") + +include(LanguageVersions) +include(SwiftProjectVersion) +project(SwiftVolatile + LANGUAGES Swift + VERSION ${SWIFT_RUNTIME_VERSION}) + +# FIXME(compnerd) this is a workaround for `GNUInstallDirs` which cannot be used +# with a pure Swift project. +enable_language(C) + +if(NOT PROJECT_IS_TOP_LEVEL) + message(SEND_ERROR "Swift Observation must build as a standalone project") +endif() + +set(${PROJECT_NAME}_SWIFTC_SOURCE_DIR + "${PROJECT_SOURCE_DIR}/../../../" + CACHE FILEPATH "Path to the root source directory of the Swift compiler") + +# Hook point for vendor-specific extensions to the build system +# Allowed extension points: +# - DefaultSettings.cmake +# - Settings.cmake +set(${PROJECT_NAME}_VENDOR_MODULE_DIR "${CMAKE_SOURCE_DIR}/../cmake/modules/vendor" + CACHE FILEPATH "Location for private build system extension") + +find_package(SwiftCore REQUIRED) +find_package(SwiftOverlay REQUIRED) + +include(GNUInstallDirs) + +include(AvailabilityMacros) +include(EmitSwiftInterface) +include(InstallSwiftInterface) +include(PlatformInfo) +include(gyb) +include(ResourceEmbedding) +include(CatalystSupport) + +option(${PROJECT_NAME}_INSTALL_NESTED_SUBDIR "Install libraries under a platform and architecture subdirectory" ON) +set(${PROJECT_NAME}_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}>" CACHE STRING "") +set(${PROJECT_NAME}_INSTALL_SWIFTMODULEDIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>$<$:/${${PROJECT_NAME}_PLATFORM_SUBDIR}>" CACHE STRING "") + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/Settings.cmake" OPTIONAL) + +option(${PROJECT_NAME}_ENABLE_LIBRARY_EVOLUTION "Generate ABI resilient runtime libraries" + ${SwiftCore_ENABLE_LIBRARY_EVOLUTION}) + +option(${PROJECT_NAME}_ENABLE_PRESPECIALIZATION "Enable generic metadata prespecialization" + ${SwiftCore_ENABLE_PRESPECIALIZATION}) + +add_compile_options( + $<$:-explicit-module-build> + $<$:-nostdlibimport> + $<$:-strict-memory-safety> + "$<$,$>:-enable-library-evolution>" + "$<$,$>:SHELL:-Xfrontend -prespecialize-generic-metadata>") + +# LNK4049: symbol 'symbol' defined in 'filename.obj' is imported +# LNK4286: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' +# LNK4217: symbol 'symbol' defined in 'filename_1.obj' is imported by 'filename_2.obj' in function 'function' +# +# We cannot selectively filter the linker warnings as we do not use the MSVC +# frontend and `clang-cl` (and `clang`) currently do not support `/WX:nnnn`. As +# a compromise, treat all linker warnings as errors. +add_link_options($<$:LINKER:/WX>) +# Ensure all symbols are fully resolved on Linux +add_link_options($<$:LINKER:-z,defs>) + +add_library(swift_Volatile + Volatile.swift) +set_target_properties(swift_Volatile PROPERTIES + Swift_MODULE_NAME _Volatile) +target_compile_options(swift_Volatile PRIVATE + -parse-stdlib) +target_link_libraries(swift_Volatile PRIVATE + swiftCore) + +install(TARGETS swift_Volatile + EXPORT SwiftVolatileTargets + COMPONENT ${PROJECT_NAME}_runtime + ARCHIVE DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${${PROJECT_NAME}_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +emit_swift_interface(swift_Volatile) +install_swift_interface(swift_Volatile) + +# Configure plist creation for Darwin platforms. +generate_plist(swift_Volatile "${CMAKE_PROJECT_VERSION}" swift_Volatile) +embed_manifest(swift_Volatile) + +include("${${PROJECT_NAME}_VENDOR_MODULE_DIR}/swift_Volatile.cmake" OPTIONAL) diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/apple-common.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/apple-common.cmake new file mode 100644 index 0000000000000..8fdc66eddff83 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/apple-common.cmake @@ -0,0 +1,7 @@ +set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "") +set(BUILD_SHARED_LIBS YES CACHE BOOL "") +set(SwiftVolatile_INSTALL_NESTED_SUBDIR OFF CACHE BOOL "") + +set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_C_FLAGS_MINSIZEREL "-Os -g -DNDEBUG" CACHE STRING "") +set(CMAKE_Swift_FLAGS_MINSIZEREL "-Osize -g" CACHE STRING "") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..8771cb9bf5fe6 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake new file mode 100644 index 0000000000000..d83851da81a44 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake new file mode 100644 index 0000000000000..cb2e76889149a --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-BridgeOS.cmake @@ -0,0 +1,14 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-bridgeos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR freestanding CACHE STRING "") +set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "") +set(SwiftVolatile_SINGLE_THREADED_MODE YES CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake new file mode 100644 index 0000000000000..7ea72c039372d --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftVolatile_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftVolatile_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftVolatile_COMPILER_VARIANT_TARGET "arm64-apple-ios${SwiftVolatile_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..1471461a4c007 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake new file mode 100644 index 0000000000000..8a4a6ab9d9fc4 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..519dc9f6c83ab --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake new file mode 100644 index 0000000000000..651f8f38df426 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake new file mode 100644 index 0000000000000..6cda9a5a0fa61 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64_32-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64_32-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64_32 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake new file mode 100644 index 0000000000000..32da678fcf0de --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-AppleTVOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR appletvos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake new file mode 100644 index 0000000000000..f5439313dcf3e --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftVolatile_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftVolatile_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftVolatile_COMPILER_VARIANT_TARGET "arm64e-apple-ios${SwiftVolatile_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake new file mode 100644 index 0000000000000..defa080f4c3cc --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-WatchOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR watchos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-XROS.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-XROS.cmake new file mode 100644 index 0000000000000..83eb8c7a43670 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-XROS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR xros CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake new file mode 100644 index 0000000000000..424a69d44353e --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/arm64e-iPhoneOS.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "arm64e-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR arm64e CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR iphoneos CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake new file mode 100644 index 0000000000000..88e8ee6f9e664 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-AppleTVOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-tvos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR appletvsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake new file mode 100644 index 0000000000000..b7f9c77ef530a --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-MacOSX.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +if(NOT DEFINED SwiftVolatile_TARGET_VARIANT_DEPLOYMENT_TARGET) + message(SEND_ERROR "SwiftVolatile_TARGET_VARIANT_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-macosx${CMAKE_OSX_DEPLOYMENT_TARGET}" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR macosx CACHE STRING "") + +set(SwiftVolatile_COMPILER_VARIANT_TARGET "x86_64-apple-ios${SwiftVolatile_TARGET_VARIANT_DEPLOYMENT_TARGET}-macabi" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake new file mode 100644 index 0000000000000..65e3ae6791e05 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-WatchOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-watchos${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR watchsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake new file mode 100644 index 0000000000000..4f1ab9dc83d17 --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-XROS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-xros${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR xrsimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake new file mode 100644 index 0000000000000..e17145f1df1ad --- /dev/null +++ b/Runtimes/Supplemental/Volatile/cmake/caches/Vendors/Apple/x86_64-iPhoneOS-simulator.cmake @@ -0,0 +1,12 @@ +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(SEND_ERROR "CMAKE_OSX_DEPLOYMENT_TARGET not defined") +endif() + +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") +set(CMAKE_Swift_COMPILER_TARGET "x86_64-apple-ios${CMAKE_OSX_DEPLOYMENT_TARGET}-simulator" CACHE STRING "") + +set(SwiftVolatile_ARCH_SUBDIR x86_64 CACHE STRING "") +set(SwiftVolatile_PLATFORM_SUBDIR iphonesimulator CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/apple-common.cmake") diff --git a/Runtimes/Supplemental/cmake/modules/AsmSupport.cmake b/Runtimes/Supplemental/cmake/modules/AsmSupport.cmake new file mode 100644 index 0000000000000..b957afb6d2dd8 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/AsmSupport.cmake @@ -0,0 +1,15 @@ +macro(enable_asm_language) + # On Windows, use MASM or MARMASM + set(SWIFT_ASM_DIALECT ASM) + set(SWIFT_ASM_EXT S) + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") + set(SWIFT_ASM_DIALECT ASM_MARMASM) + else() + set(SWIFT_ASM_DIALECT ASM_MASM) + endif() + set(SWIFT_ASM_EXT asm) + endif() + + enable_language(${SWIFT_ASM_DIALECT}) +endmacro() diff --git a/Runtimes/Supplemental/cmake/modules/CatalystSupport.cmake b/Runtimes/Supplemental/cmake/modules/CatalystSupport.cmake new file mode 100644 index 0000000000000..69c341a1d58e6 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/CatalystSupport.cmake @@ -0,0 +1,48 @@ +include(CheckCompilerFlag) + +# Add flags for generating the zippered target variant in the build + +# Initialize `${PROJECT_NAME}_VARIANT_MODULE_TRIPLE` if the driver is able to emit +# modules for the target variant. + +if(${PROJECT_NAME}_COMPILER_VARIANT_TARGET) + add_compile_options( + "$<$:SHELL:-darwin-target-variant ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>" + "$<$:SHELL:-target-variant ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>" + + # TODO: Remove me once we have a driver with + # https://github.com/swiftlang/swift-driver/pull/1803 + "$<$:SHELL:-Xclang-linker -darwin-target-variant -Xclang-linker ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>") + + add_link_options( + "$<$:SHELL:-darwin-target-variant ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>" + "$<$:SHELL:-target-variant ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>" + + # TODO: Remove me once we have a driver with + # https://github.com/swiftlang/swift-driver/pull/1803 + "$<$:SHELL:-Xclang-linker -darwin-target-variant -Xclang-linker ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}>") + + # TODO: Once we are guaranteed to have a driver with the variant module path + # support everywhere, we should integrate this into PlatformInfo.cmake + check_compiler_flag(Swift "-emit-variant-module-path ${CMAKE_CURRENT_BINARY_DIR}/CompilerID/variant.swiftmodule" HAVE_Swift_VARIANT_MODULE_PATH_FLAG) + if(HAVE_Swift_VARIANT_MODULE_PATH_FLAG) + # Get variant module triple + set(module_triple_command "${CMAKE_Swift_COMPILER}" -print-target-info -target ${${PROJECT_NAME}_COMPILER_VARIANT_TARGET}) + execute_process(COMMAND ${module_triple_command} OUTPUT_VARIABLE target_info_json) + message(CONFIGURE_LOG "Swift target variant info: ${target_info_json}") + + + string(JSON module_triple GET "${target_info_json}" "target" "moduleTriple") + set(${PROJECT_NAME}_VARIANT_MODULE_TRIPLE "${module_triple}" CACHE STRING "Triple used for installed swift{module,interface} files for the target variant") + mark_as_advanced(${PROJECT_NAME}_VARIANT_MODULE_TRIPLE) + message(CONFIGURE_LOG "Swift target variant module triple: ${module_triple}") + endif() + + if(${PROJECT_NAME}_EXPERIMENTAL_EMIT_VARIANT_MODULE) + check_compiler_flag(Swift "-experimental-emit-variant-module" HAVE_Swift_EMIT_VARIANT_MODULE_FLAG) + if(HAVE_Swift_EMIT_VARIANT_MODULE_FLAG) + add_compile_options( + "$<$:-experimental-emit-variant-module>") + endif() + endif() +endif() diff --git a/Runtimes/Supplemental/cmake/modules/EmitSwiftInterface.cmake b/Runtimes/Supplemental/cmake/modules/EmitSwiftInterface.cmake new file mode 100644 index 0000000000000..8a3540dd55e07 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/EmitSwiftInterface.cmake @@ -0,0 +1,54 @@ +# Generate and install swift interface files + +# TODO: CMake should learn how to model library evolution and generate this +# stuff automatically. + + +# Generate a swift interface file for the target if library evolution is enabled +function(emit_swift_interface target) + # Generate the target-variant binary swift module when performing zippered + # build + # Clean this up once CMake has nested swiftmodules in the build directory: + # https://gitlab.kitware.com/cmake/cmake/-/merge_requests/10664 + # https://cmake.org/cmake/help/git-stage/policy/CMP0195.html + + # We can't expand the Swift_MODULE_NAME target property in a generator + # expression or it will fail saying that the target doesn't exist. + get_target_property(module_name ${target} Swift_MODULE_NAME) + if(NOT module_name) + set(module_name ${target}) + endif() + # Account for an existing swiftmodule file + # generated with the previous logic + if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule" + AND NOT IS_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule") + message(STATUS "Removing regular file ${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule to support nested swiftmodule generation") + file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule") + endif() + target_compile_options(${target} PRIVATE + "$<$:SHELL:-emit-module-path ${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule/${${PROJECT_NAME}_MODULE_TRIPLE}.swiftmodule>") + if(${PROJECT_NAME}_VARIANT_MODULE_TRIPLE) + target_compile_options(${target} PRIVATE + "$<$:SHELL:-emit-variant-module-path ${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule/${${PROJECT_NAME}_VARIANT_MODULE_TRIPLE}.swiftmodule>") + endif() + add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule/${${PROJECT_NAME}_MODULE_TRIPLE}.swiftmodule" + DEPENDS ${target}) + target_sources(${target} + INTERFACE + $) + + # Generate textual swift interfaces is library-evolution is enabled + if(${PROJECT_NAME}_ENABLE_LIBRARY_EVOLUTION) + target_compile_options(${target} PRIVATE + $<$:-emit-module-interface-path$${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule/${${PROJECT_NAME}_MODULE_TRIPLE}.swiftinterface> + $<$:-emit-private-module-interface-path$${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule/${${PROJECT_NAME}_MODULE_TRIPLE}.private.swiftinterface>) + if(${PROJECT_NAME}_VARIANT_MODULE_TRIPLE) + target_compile_options(${target} PRIVATE + "$<$:SHELL:-emit-variant-module-interface-path ${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule/${${PROJECT_NAME}_VARIANT_MODULE_TRIPLE}.swiftinterface>" + "$<$:SHELL:-emit-variant-private-module-interface-path ${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule/${${PROJECT_NAME}_VARIANT_MODULE_TRIPLE}.private.swiftinterface>") + endif() + target_compile_options(${target} PRIVATE + $<$:-library-level$api> + $<$:-Xfrontend$-require-explicit-availability=ignore>) + endif() +endfunction() diff --git a/Runtimes/Supplemental/cmake/modules/FindSwiftCore.cmake b/Runtimes/Supplemental/cmake/modules/FindSwiftCore.cmake new file mode 100644 index 0000000000000..98bfd4c6b6de3 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/FindSwiftCore.cmake @@ -0,0 +1,304 @@ +#[=======================================================================[.rst: +FindSwiftCore +------------ + +Find SwiftCore, deferring to SwiftCoreConfig.cmake when requested. +This is meant to find the core library to be linked by the Supplemental libraries. + +Imported Targets +^^^^^^^^^^^^^^^^ + +The following :prop_tgt:`IMPORTED` TARGETS may be defined: + + ``swiftCore`` + +Hint Variables +^^^^^^^^^^^^^^ + + ``SDKROOT`` (environment variable) + Set the path to the Swift SDK Root. + This only affects Windows builds. + + ``Swift_SDKROOT`` + Set the path to the Swift SDK installation. + This only affects Linux and Windows builds. + Apple builds always use the library provided by the SDK. + + ``SwiftCore_USE_STATIC_LIBS`` + Set to boolean true to find static libraries. If not + set explicitly this is inferred from the value of + `BUILD_SHARED_LIBS` and the current platform. + + ``SwiftCore_INCLUDE_DIR_HINTS`` + Additional paths to search the swiftmodules into. These + are prepended to the ones searched by this find module + + ``SwiftCore_LIBRARY_HINTS`` + Additional paths to search the libraries into. These + are prepended to the ones searched by this find module + + ``SwiftShims_INCLUDE_DIR_HINTS`` + Additional paths to search the shims into. These + are prepended to the ones searched by this find module + + ``SwiftCore_NAMES`` + Additional names for the Core library + + ``SwiftOnoneSupport_NAMES`` + Additional names for the SwiftOnoneSupport library + + ``SwiftConcurrency_NAMES`` + Additional names for the Swift_Concurrency library + +Result targets +^^^^^^^^^^^^^^ + +If no error is generated, the following targets are available to the users + + * ``swiftCore`` + * ``swiftSwiftOnoneSupport`` + * ``swift_Concurrency`` + * ``swiftShims`` + +Result Variables +^^^^^^^^^^^^^^^^ + +The module may set the following variables if `SwiftCore_DIR` is not set. +(although we suggest relying on the targets above instead) + + ``SwiftCore_FOUND`` + true if core was found + + ``SwiftCore_INCLUDE_DIR`` + the directory containing the Swift.swiftmodule folder + + ``SwiftCore_LIBRARY`` + path to the swiftCore library + + ``SwiftOnoneSupport_INCLUDE_DIR`` + the directory containing the SwiftOnoneSupport.swiftmodule folder + + ``SwiftOnoneSupport_LIBRARY`` + path to the SwiftOnoneSupport library + + ``SwiftConcurrency_INCLUDE_DIR`` + the directory containing the _Concurrency.swiftmodule folder + + ``SwiftConcurrency_LIBRARY`` + path to the swift_Concurrency library + + ``SwiftShims_INCLUDE_DIR`` + the directory containing the Swift shims directory + (i.e. .../usr/lib/swift, not .../usr/lib/swift/shims) + +#]=======================================================================] + +include_guard(GLOBAL) + +# If the SwiftCore_DIR_FLAG is specified, look there instead. The cmake-generated +# config file is more accurate, but requires that the SDK has one available. +if(SwiftCore_DIR) + if(SwiftCore_FIND_REQUIRED) + list(APPEND args REQUIRED) + endif() + if(SwiftCore_FIND_QUIETLY) + list(APPEND args QUIET) + endif() + find_package(SwiftCore NO_MODULE ${args}) + return() +endif() + +include(FindPackageHandleStandardArgs) +include(PlatformInfo) + +# This was loosely modelled after other find modules +# (namely FindGLEW), where the equivalent parameter +# is not stored in cache (possibly because we want +# the project importing it to be able to +# it "immediately") +if(NOT DEFINED SwiftCore_USE_STATIC_LIBS) + set(SwiftCore_USE_STATIC_LIBS OFF) + if(NOT BUILD_SHARED_LIBS AND NOT APPLE) + set(SwiftCore_USE_STATIC_LIBS ON) + endif() +endif() + +if(APPLE) + list(APPEND SwiftCore_INCLUDE_DIR_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift") + list(APPEND SwiftCore_LIBRARY_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift") + list(APPEND SwiftShims_INCLUDE_DIR_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift") + # When building for Apple platforms, SwiftCore always comes from within the + # SDK as a tbd for a shared library in the shared cache. + list(APPEND SwiftCore_NAMES libswiftCore.tbd) + list(APPEND SwiftOnoneSupport_NAMES libswiftSwiftOnoneSupport.tbd) + list(APPEND SwiftConcurrency_NAMES libswift_Concurrency.tbd) +elseif(LINUX) + if (SwiftCore_USE_STATIC_LIBS) + list(APPEND SwiftCore_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/linux-static") + list(APPEND SwiftCore_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/linux-static") + list(APPEND SwiftShims_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static" + "${Swift_SDKROOT}/usr/lib/swift") + list(APPEND SwiftCore_NAMES libswiftCore.a) + list(APPEND SwiftOnoneSupport_NAMES libswiftSwiftOnoneSupport.a) + list(APPEND SwiftConcurrency_NAMES libswift_Concurrency.a) + else() + list(APPEND SwiftCore_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift/linux") + list(APPEND SwiftCore_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift/linux") + list(APPEND SwiftShims_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift") + list(APPEND SwiftCore_NAMES libswiftCore.so) + list(APPEND SwiftOnoneSupport_NAMES libswiftSwiftOnoneSupport.so) + list(APPEND SwiftConcurrency_NAMES libswift_Concurrency.so) + endif() +elseif(WIN32) + list(APPEND SwiftCore_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift/windows" + "$ENV{SDKROOT}/usr/lib/swift/windows") + list(APPEND SwiftCore_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift" + "$ENV{SDKROOT}/usr/lib/swift/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift") + list(APPEND SwiftShims_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift" + "$ENV{SDKROOT}/usr/lib/swift") + if (SwiftCore_USE_STATIC_LIBS) + list(APPEND SwiftCore_NAMES libswiftCore.lib) + list(APPEND SwiftOnoneSupport_NAMES libswiftSwiftOnoneSupport.lib) + list(APPEND SwiftConcurrency_NAMES libswift_Concurrency.lib) + else() + list(APPEND SwiftCore_NAMES swiftCore.lib) + list(APPEND SwiftOnoneSupport_NAMES swiftSwiftOnoneSupport.lib) + list(APPEND SwiftConcurrency_NAMES swift_Concurrency.lib) + endif() +elseif(ANDROID) + if (SwiftCore_USE_STATIC_LIBS) + list(APPEND SwiftCore_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/android" + "$ENV{SDKROOT}/usr/lib/swift_static/android") + list(APPEND SwiftCore_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift_static" + "$ENV{SDKROOT}/usr/lib/swift_static/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift_static") + list(APPEND SwiftShims_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static") + list(APPEND SwiftCore_NAMES libswiftCore.a) + list(APPEND SwiftOnoneSupport_NAMES libswiftSwiftOnoneSupport.a) + list(APPEND SwiftConcurrency_NAMES libswift_Concurrency.a) + else() + list(APPEND SwiftCore_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift/android" + "$ENV{SDKROOT}/usr/lib/swift/android") + list(APPEND SwiftCore_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift" + "$ENV{SDKROOT}/usr/lib/swift/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift") + list(APPEND SwiftShims_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift") + list(APPEND SwiftCore_NAMES libswiftCore.so) + list(APPEND SwiftOnoneSupport_NAMES libswiftSwiftOnoneSupport.so) + list(APPEND SwiftConcurrency_NAMES libswift_Concurrency.so) + endif() +else() + message(FATAL_ERROR "FindSwiftCore.cmake module search not implemented for targeted platform\n" + " Build Core for your platform and set `SwiftCore_DIR` to" + " the directory containing SwiftCoreConfig.cmake\n") +endif() + +find_path(SwiftCore_INCLUDE_DIR + "Swift.swiftmodule" + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${SwiftCore_INCLUDE_DIR_HINTS}) +find_library(SwiftCore_LIBRARY + NAMES + ${SwiftCore_NAMES} + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${SwiftCore_LIBRARY_HINTS}) + +find_path(SwiftOnoneSupport_INCLUDE_DIR + "SwiftOnoneSupport.swiftmodule" + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${SwiftCore_INCLUDE_DIR_HINTS}) +find_library(SwiftOnoneSupport_LIBRARY + NAMES + ${SwiftOnoneSupport_NAMES} + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${SwiftCore_LIBRARY_HINTS}) + +find_path(SwiftConcurrency_INCLUDE_DIR + "_Concurrency.swiftmodule" + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${SwiftCore_INCLUDE_DIR_HINTS}) +find_library(SwiftConcurrency_LIBRARY + NAMES + ${SwiftConcurrency_NAMES} + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${SwiftCore_LIBRARY_HINTS}) + +if(SwiftCore_USE_STATIC_LIBS) + add_library(swiftCore STATIC IMPORTED GLOBAL) + add_library(swiftSwiftOnoneSupport STATIC IMPORTED GLOBAL) + add_library(swift_Concurrency STATIC IMPORTED GLOBAL) +else() + add_library(swiftCore SHARED IMPORTED GLOBAL) + add_library(swiftSwiftOnoneSupport SHARED IMPORTED GLOBAL) + add_library(swift_Concurrency SHARED IMPORTED GLOBAL) +endif() + +set_target_properties(swiftCore PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${SwiftCore_INCLUDE_DIR}") +set_target_properties(swiftSwiftOnoneSupport PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${SwiftOnoneSupport_INCLUDE_DIR}") +set_target_properties(swift_Concurrency PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${SwiftConcurrency_INCLUDE_DIR}") + +if(LINUX OR ANDROID) + set_target_properties(swiftCore PROPERTIES + IMPORTED_LOCATION "${SwiftCore_LIBRARY}") + set_target_properties(swiftSwiftOnoneSupport PROPERTIES + IMPORTED_LOCATION "${SwiftOnoneSupport_LIBRARY}") + set_target_properties(swift_Concurrency PROPERTIES + IMPORTED_LOCATION "${SwiftConcurrency_LIBRARY}") +else() + set_target_properties(swiftCore PROPERTIES + IMPORTED_IMPLIB "${SwiftCore_LIBRARY}") + set_target_properties(swiftSwiftOnoneSupport PROPERTIES + IMPORTED_IMPLIB "${SwiftOnoneSupport_LIBRARY}") + set_target_properties(swift_Concurrency PROPERTIES + IMPORTED_IMPLIB "${SwiftConcurrency_LIBRARY}") +endif() + +find_path(SwiftShims_INCLUDE_DIR "shims/module.modulemap" HINTS + ${SwiftShims_INCLUDE_DIR_HINTS}) +add_library(swiftShims INTERFACE IMPORTED GLOBAL) +target_include_directories(swiftShims INTERFACE +# This is needed for targets that import headers from +# include/swift (e.g. include/swift/ABI/HeapObject.h) +# that assumes the shims are located in `swift/shims` +# relative path + "${SwiftShims_INCLUDE_DIR}/.." + "${SwiftShims_INCLUDE_DIR}/shims") +target_compile_options(swiftShims INTERFACE + "$<$:SHELL:-Xcc -fmodule-map-file=\"${SwiftShims_INCLUDE_DIR}/shims/module.modulemap\">") + +find_package_handle_standard_args(SwiftCore DEFAULT_MSG + SwiftCore_LIBRARY SwiftCore_INCLUDE_DIR + SwiftShims_INCLUDE_DIR + SwiftOnoneSupport_LIBRARY SwiftOnoneSupport_INCLUDE_DIR + SwiftConcurrency_LIBRARY SwiftConcurrency_INCLUDE_DIR) diff --git a/Runtimes/Supplemental/cmake/modules/FindSwiftDarwin.cmake b/Runtimes/Supplemental/cmake/modules/FindSwiftDarwin.cmake new file mode 100644 index 0000000000000..ca0bfbff0e6a6 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/FindSwiftDarwin.cmake @@ -0,0 +1,70 @@ +#[=======================================================================[.rst: +FindSwiftDarwin +------------ + +Find swiftDarwin in the underlying SDK (the only way we obtain such overlay). + +Imported Targets +^^^^^^^^^^^^^^^^ + +The following :prop_tgt:`IMPORTED` TARGETS may be defined: + + ``swiftDarwin`` + +#]=======================================================================] + +include_guard(GLOBAL) + +# This was loosely modelled after other find modules +# (namely FindGLEW), where the equivalent parameter +# is not stored in cache (possibly because we want +# the project importing it to be able to use +# it "immediately") +if(NOT DEFINED SwiftDarwin_USE_STATIC_LIBS) + set(SwiftDarwin_USE_STATIC_LIBS OFF) +endif() + +include(FindPackageHandleStandardArgs) +include(PlatformInfo) + +if(NOT APPLE) + message(WARNING "Darwin is only produced on Apple platforms, so not attempting to search it here.") + return() +endif() + +# Look in the SDK +list(APPEND swiftDarwin_INCLUDE_DIR_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift") +list(APPEND swiftDarwin_LIBRARY_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift") +# When building for Apple platforms, swiftDarwin always comes from within the +# SDK as a tbd for a shared library in the shared cache. +list(APPEND swiftDarwin_NAMES libswiftDarwin.tbd) +set(swiftDarwin_MODULE_NAME "Darwin.swiftmodule") + +find_path(swiftDarwin_INCLUDE_DIR + ${swiftDarwin_MODULE_NAME} + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${swiftDarwin_INCLUDE_DIR_HINTS}) +find_library(swiftDarwin_LIBRARY + NAMES + ${swiftDarwin_NAMES} + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${swiftDarwin_LIBRARY_HINTS}) + +if(SwiftDarwin_USE_STATIC_LIBS) + add_library(swiftDarwin STATIC IMPORTED GLOBAL) +else() + add_library(swiftDarwin SHARED IMPORTED GLOBAL) +endif() + +target_include_directories(swiftDarwin INTERFACE + "${swiftDarwin_INCLUDE_DIR}") + +set_target_properties(swiftDarwin PROPERTIES + IMPORTED_IMPLIB "${swiftDarwin_LIBRARY}") + +find_package_handle_standard_args(SwiftDarwin DEFAULT_MSG + "swiftDarwin_LIBRARY" "swiftDarwin_INCLUDE_DIR") diff --git a/Runtimes/Supplemental/cmake/modules/FindSwiftOverlay.cmake b/Runtimes/Supplemental/cmake/modules/FindSwiftOverlay.cmake new file mode 100644 index 0000000000000..c40b41837ccf7 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/FindSwiftOverlay.cmake @@ -0,0 +1,272 @@ +#[=======================================================================[.rst: +FindSwiftOverlay +------------ + +Find SwiftOverlay, deferring to the associated SwiftOverlayConfig.cmake when requested. +This is meant to find the platform overlays to be linked by the Supplemental libraries. + +Imported Targets +^^^^^^^^^^^^^^^^ + +The following :prop_tgt:`IMPORTED` TARGETS may be defined: + + ``SwiftOverlay`` + +Hint Variables +^^^^^^^^^^^^^^ + + ``SDKROOT`` (environment variable) + Set the path to the Swift SDK Root. + This only affects Windows and Android builds. + + ``Swift_SDKROOT`` + Set the path to the Swift SDK installation. + This affects Linux, Android, and Windows builds. + Apple builds always use the overlay provided by the SDK. + +#]=======================================================================] + +include_guard(GLOBAL) + +# This was loosely modelled after other find modules +# (namely FindGLEW), where the equivalent parameter +# is not stored in cache (possibly because we want +# the project importing it to be able to +# it "immediately") +if(NOT DEFINED SwiftOverlay_USE_STATIC_LIBS) + set(SwiftOverlay_USE_STATIC_LIBS OFF) + if(NOT BUILD_SHARED_LIBS AND NOT APPLE) + set(SwiftOverlay_USE_STATIC_LIBS ON) + endif() +endif() + +if(SwiftOverlay_DIR) + if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED) + list(APPEND args REQUIRED) + endif() + if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) + list(APPEND args QUIET) + endif() + find_package(SwiftOverlay CONFIG ${args}) + return() +endif() + +include(FindPackageHandleStandardArgs) +include(PlatformInfo) + +if(APPLE) + # Darwin has its own dedicated find module, + # so that we can use it together with the CMake config file + # generated by the Overlay build system + + list(APPEND swift_Builtin_float_INCLUDE_DIR_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift") + list(APPEND swift_Builtin_float_LIBRARY_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift") + list(APPEND swift_Builtin_float_NAMES libswift_Builtin_float.tbd) +elseif(LINUX) + #ToDo(swiftlang/swift/issues/83014): Handle the static MUSL SDK case + list(APPEND OVERLAY_TARGET_NAMES "swiftGlibc") + + # Look in the SDK + if (SwiftOverlay_USE_STATIC_LIBS) + list(APPEND swiftGlibc_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/linux") + list(APPEND swiftGlibc_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/linux") + list(APPEND swiftGlibc_NAMES libswiftGlibc.a) + + list(APPEND swift_Builtin_float_INCLUDE_DIR_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift_static/linux") + list(APPEND swift_Builtin_float_LIBRARY_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift_static/linux") + list(APPEND swift_Builtin_float_NAMES libswift_Builtin_float.a) + else() + list(APPEND swiftGlibc_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift/linux/") + list(APPEND swiftGlibc_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift/linux") + list(APPEND swiftGlibc_NAMES libswiftGlibc.so) + + list(APPEND swift_Builtin_float_INCLUDE_DIR_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift/linux") + list(APPEND swift_Builtin_float_LIBRARY_HINTS + "${CMAKE_OSX_SYSROOT}/usr/lib/swift/linux") + list(APPEND swift_Builtin_float_NAMES libswift_Builtin_float.so) + endif() + set(swiftGlibc_MODULE_NAME "Glibc.swiftmodule") +elseif(WIN32) + list(APPEND OVERLAY_TARGET_NAMES "swiftWinSDK") + list(APPEND OVERLAY_TARGET_NAMES "swiftCRT") + + list(APPEND swiftWinSDK_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift/windows" + "$ENV{SDKROOT}/usr/lib/swift/windows") + list(APPEND swiftWinSDK_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift" + "$ENV{SDKROOT}/usr/lib/swift/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift") + + if(SwiftOverlay_USE_STATIC_LIBS) + list(APPEND swiftWinSDK_NAMES libswiftWinSDK.lib) + else() + list(APPEND swiftWinSDK_NAMES swiftWinSDK.lib) + endif() + list(APPEND swiftWinSDK_MODULE_NAME "WinSDK.swiftmodule") + + list(APPEND swiftCRT_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift/windows" + "$ENV{SDKROOT}/usr/lib/swift/windows") + list(APPEND swiftCRT_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift" + "$ENV{SDKROOT}/usr/lib/swift/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift") + + if(SwiftOverlay_USE_STATIC_LIBS) + list(APPEND swiftCRT_NAMES libswiftCRT.lib) + else() + list(APPEND swiftCRT_NAMES swiftCRT.lib) + endif() + set(swiftCRT_MODULE_NAME "CRT.swiftmodule") + + list(APPEND swift_Builtin_float_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift/windows" + "$ENV{SDKROOT}/usr/lib/swift/windows") + list(APPEND swift_Builtin_float_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift" + "$ENV{SDKROOT}/usr/lib/swift/${${PROJECT_NAME}_PLATFORM_SUBDIR}/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift") + + if(SwiftOverlay_USE_STATIC_LIBS) + list(APPEND swift_Builtin_float_NAMES libswift_Builtin_float.lib) + else() + list(APPEND swift_Builtin_float_NAMES swift_Builtin_float.lib) + endif() +elseif(ANDROID) + list(APPEND OVERLAY_TARGET_NAMES "swiftAndroid") + + if (SwiftOverlay_USE_STATIC_LIBS) + list(APPEND swiftAndroid_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/android" + "$ENV{SDKROOT}/usr/lib/swift_static/android") + list(APPEND swiftAndroid_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift_static" + "$ENV{SDKROOT}/usr/lib/swift_static/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift_static") + list(APPEND swiftAndroid_NAMES libswiftAndroid.a) + + list(APPEND swift_Builtin_float_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/android" + "$ENV{SDKROOT}/usr/lib/swift_static/android") + list(APPEND swift_Builtin_float_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift_static/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift_static" + "$ENV{SDKROOT}/usr/lib/swift_static/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift_static") + list(APPEND swift_Builtin_float_NAMES libswift_Builtin_float.a) + else() + list(APPEND swiftAndroid_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift/android" + "$ENV{SDKROOT}/usr/lib/swift/android") + list(APPEND swiftAndroid_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift" + "$ENV{SDKROOT}/usr/lib/swift/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift") + list(APPEND swiftAndroid_NAMES libswiftAndroid.so) + + list(APPEND swift_Builtin_float_INCLUDE_DIR_HINTS + "${Swift_SDKROOT}/usr/lib/swift/android" + "$ENV{SDKROOT}/usr/lib/swift/android") + list(APPEND swift_Builtin_float_LIBRARY_HINTS + "${Swift_SDKROOT}/usr/lib/swift/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "${Swift_SDKROOT}/usr/lib/swift" + "$ENV{SDKROOT}/usr/lib/swift/android/${${PROJECT_NAME}_ARCH_SUBDIR}" + "$ENV{SDKROOT}/usr/lib/swift") + list(APPEND swift_Builtin_float_NAMES libswift_Builtin_float.so) + endif() + set(swiftAndroid_MODULE_NAME "Android.swiftmodule") +else() + message(FATAL_ERROR "FindSwiftOverlay.cmake module search not implemented for targeted platform\n" + " Build the Overlays for your platform and set the appropriate `SwiftOverlay_DIR` variable to" + " the directory containing SwiftOverlayConfig.cmake\n") +endif() + +# Setup SwiftOverlay interface library and link it against the explicit +# overlay targets +add_library(SwiftOverlay INTERFACE) + +foreach(OVERLAY_TARGET ${OVERLAY_TARGET_NAMES}) + find_path(${OVERLAY_TARGET}_INCLUDE_DIR + ${${OVERLAY_TARGET}_MODULE_NAME} + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${${OVERLAY_TARGET}_INCLUDE_DIR_HINTS}) + find_library(${OVERLAY_TARGET}_LIBRARY + NAMES + ${${OVERLAY_TARGET}_NAMES} + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${${OVERLAY_TARGET}_LIBRARY_HINTS}) + + if(SwiftOverlay_USE_STATIC_LIBS) + add_library(${OVERLAY_TARGET} STATIC IMPORTED GLOBAL) + else() + add_library(${OVERLAY_TARGET} SHARED IMPORTED GLOBAL) + endif() + + target_include_directories(${OVERLAY_TARGET} INTERFACE + "${${OVERLAY_TARGET}_INCLUDE_DIR}") + + if(LINUX OR ANDROID) + set_target_properties(${OVERLAY_TARGET} PROPERTIES + IMPORTED_LOCATION "${${OVERLAY_TARGET}_LIBRARY}") + else() + set_target_properties(${OVERLAY_TARGET} PROPERTIES + IMPORTED_IMPLIB "${${OVERLAY_TARGET}_LIBRARY}") + endif() + + target_link_libraries(SwiftOverlay INTERFACE + ${OVERLAY_TARGET}) +endforeach() + +find_path(swift_Builtin_float_INCLUDE_DIR + "_Builtin_float.swiftmodule" + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${swift_Builtin_float_INCLUDE_DIR_HINTS}) +find_library(swift_Builtin_float_LIBRARY + NAMES + ${swift_Builtin_float_NAMES} + NO_CMAKE_FIND_ROOT_PATH + HINTS + ${swift_Builtin_float_LIBRARY_HINTS}) + +if(SwiftOverlay_USE_STATIC_LIBS) + add_library(swift_Builtin_float STATIC IMPORTED GLOBAL) +else() + add_library(swift_Builtin_float SHARED IMPORTED GLOBAL) +endif() + +target_include_directories(swift_Builtin_float INTERFACE + "${swift_Builtin_float_INCLUDE_DIR}") + +if(LINUX OR ANDROID) + set_target_properties(swift_Builtin_float PROPERTIES + IMPORTED_LOCATION "${swift_Builtin_float_LIBRARY}") +else() + set_target_properties(swift_Builtin_float PROPERTIES + IMPORTED_IMPLIB "${swift_Builtin_float_LIBRARY}") +endif() + +foreach(OVERLAY_TARGET ${OVERLAY_TARGET_NAMES}) + list(APPEND vars_to_check "${OVERLAY_TARGET}_LIBRARY" "${OVERLAY_TARGET}_INCLUDE_DIR") +endforeach() +list(APPEND vars_to_check "swift_Builtin_float_LIBRARY" "swift_Builtin_float_INCLUDE_DIR") + +find_package_handle_standard_args(SwiftOverlay DEFAULT_MSG + ${vars_to_check}) diff --git a/Runtimes/Supplemental/cmake/modules/InstallSwiftInterface.cmake b/Runtimes/Supplemental/cmake/modules/InstallSwiftInterface.cmake new file mode 100644 index 0000000000000..e8f5fb2021596 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/InstallSwiftInterface.cmake @@ -0,0 +1,13 @@ + +# Install the generated swift interface files for the target. +function(install_swift_interface target) + # Swiftmodules are already in the directory structure + get_target_property(module_name ${target} Swift_MODULE_NAME) + if(NOT module_name) + set(module_name ${target}) + endif() + + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.swiftmodule" + DESTINATION "${${PROJECT_NAME}_INSTALL_SWIFTMODULEDIR}" + COMPONENT ${PROJECT_NAME}_development) +endfunction() diff --git a/Runtimes/Supplemental/cmake/modules/LanguageVersions.cmake b/Runtimes/Supplemental/cmake/modules/LanguageVersions.cmake new file mode 100644 index 0000000000000..706661f229ac3 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/LanguageVersions.cmake @@ -0,0 +1,5 @@ +set(CMAKE_Swift_LANGUAGE_VERSION 5) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) diff --git a/Runtimes/Supplemental/cmake/modules/PlatformInfo.cmake b/Runtimes/Supplemental/cmake/modules/PlatformInfo.cmake new file mode 100644 index 0000000000000..b62a729bc8ffd --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/PlatformInfo.cmake @@ -0,0 +1,40 @@ +include_guard(GLOBAL) + +if(NOT ${PROJECT_NAME}_SIZEOF_POINTER) + set(${PROJECT_NAME}_SIZEOF_POINTER "${CMAKE_SIZEOF_VOID_P}" CACHE STRING "Size of a pointer in bytes") + message(CONFIGURE_LOG "Stdlib Pointer size: ${CMAKE_SIZEOF_VOID_P}") + mark_as_advanced(${PROJECT_NAME}_SIZEOF_POINTER) +endif() + +# TODO: This logic should migrate to CMake once CMake supports installing swiftmodules +set(module_triple_command "${CMAKE_Swift_COMPILER}" -print-target-info) +if(CMAKE_Swift_COMPILER_TARGET) + list(APPEND module_triple_command -target ${CMAKE_Swift_COMPILER_TARGET}) +endif() +execute_process(COMMAND ${module_triple_command} OUTPUT_VARIABLE target_info_json) +message(CONFIGURE_LOG "Swift target info: ${module_triple_command}\n" +"${target_info_json}") + +if(NOT ${PROJECT_NAME}_MODULE_TRIPLE) + string(JSON module_triple GET "${target_info_json}" "target" "moduleTriple") + set(${PROJECT_NAME}_MODULE_TRIPLE "${module_triple}" CACHE STRING "Triple used for installed swift{doc,module,interface} files") + mark_as_advanced(${PROJECT_NAME}_MODULE_TRIPLE) + + message(CONFIGURE_LOG "Swift module triple: ${module_triple}") +endif() + +if(NOT ${PROJECT_NAME}_PLATFORM_SUBDIR) + string(JSON platform GET "${target_info_json}" "target" "platform") + set(${PROJECT_NAME}_PLATFORM_SUBDIR "${platform}" CACHE STRING "Platform name used for installed swift{doc,module,interface} files") + mark_as_advanced(${PROJECT_NAME}_PLATFORM_SUBDIR) + + message(CONFIGURE_LOG "Swift platform: ${platform}") +endif() + +if(NOT ${PROJECT_NAME}_ARCH_SUBDIR) + string(JSON arch GET "${target_info_json}" "target" "arch") + set(${PROJECT_NAME}_ARCH_SUBDIR "${arch}" CACHE STRING "Architecture used for setting the architecture subdirectory") + mark_as_advanced(${PROJECT_NAME}_ARCH_SUBDIR) + + message(CONFIGURE_LOG "Swift Arch: ${arch}") +endif() diff --git a/Runtimes/Supplemental/cmake/modules/ResourceEmbedding.cmake b/Runtimes/Supplemental/cmake/modules/ResourceEmbedding.cmake new file mode 100644 index 0000000000000..fe118f74da176 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/ResourceEmbedding.cmake @@ -0,0 +1,70 @@ +function(generate_plist project_name project_version target) + set(PLIST_INFO_PLIST "Info.plist") + set(PLIST_INFO_NAME "${project_name}") + + # Underscores aren't permitted in the bundle identifier. + string(REPLACE "_" "" PLIST_INFO_UTI "com.apple.dt.runtime.${PLIST_INFO_NAME}") + set(PLIST_INFO_VERSION "${project_version}") + set(PLIST_INFO_BUILD_VERSION "${project_version}") + + set(PLIST_INFO_PLIST_OUT "${PLIST_INFO_PLIST}") + set(PLIST_INFO_PLIST_IN "${PROJECT_SOURCE_DIR}/${PLIST_INFO_PLIST}.in") + + if(APPLE) + target_link_options(${target} PRIVATE + "SHELL:-Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${PLIST_INFO_PLIST_OUT}") + endif() + + configure_file( + "${PLIST_INFO_PLIST_IN}" + "${PLIST_INFO_PLIST_OUT}" + @ONLY + NEWLINE_STYLE UNIX) + + set_property(TARGET ${target} APPEND PROPERTY LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${PLIST_INFO_PLIST_OUT}") + + # If Application Extensions are enabled, pass the linker flag marking + # the dylib as safe. + if (CXX_SUPPORTS_FAPPLICATION_EXTENSION AND (NOT DISABLE_APPLICATION_EXTENSION)) + list(APPEND link_flags "-Wl,-application_extension") + endif() +endfunction() + +# FIXME: it appears that `CMAKE_MT` evaluates to an empty string which prevents +# the use of the variable. This aliases `MT` to `CMAKE_MT` and tries to fallback +# to known spellings for the tool. +if(WIN32 AND BUILD_SHARED_LIBS) + find_program(MT HINTS ${CMAKE_MT} NAMES mt llvm-mt REQUIRED) +endif() + +function(embed_manifest target) + get_target_property(_EM_TARGET_TYPE ${target} TYPE) + if(NOT "${_EM_TARGET_TYPE}" MATCHES "SHARED_LIBRARY|EXECUTABLE") + return() + endif() + + get_target_property(_EM_BINARY_DIR ${target} BINARY_DIR) + get_target_property(_EM_NAME ${target} NAME) + + # Evaluate variables + file(CONFIGURE + OUTPUT ${_EM_BINARY_DIR}/${_EM_NAME}-${PROJECT_VERSION}.1.manifest.in + CONTENT [[ + + + +]]) + # Evaluate generator expression + file(GENERATE + OUTPUT ${_EM_BINARY_DIR}/${_EM_NAME}-${PROJECT_VERSION}.1.manifest + INPUT ${_EM_BINARY_DIR}/${_EM_NAME}-${PROJECT_VERSION}.1.manifest.in) + + if(WIN32) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${MT}" -nologo -manifest "${_EM_BINARY_DIR}/${_EM_NAME}-${PROJECT_VERSION}.1.manifest" "-outputresource:$;#1") + endif() +endfunction() diff --git a/Runtimes/Supplemental/cmake/modules/SwiftCallingConventions.cmake b/Runtimes/Supplemental/cmake/modules/SwiftCallingConventions.cmake new file mode 100644 index 0000000000000..ceb3145e10d41 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/SwiftCallingConventions.cmake @@ -0,0 +1,28 @@ +include(CheckSourceCompiles) +include(CheckCompilerFlag) + +check_source_compiles(CXX +"#if !(__has_attribute(swiftcall) && \ + __has_attribute(swift_context) && \ + __has_attribute(swift_error_result) && \ + __has_attribute(swift_indirect_result)) +#error CXX compiler must support Swift calling conventions +#endif +int main(void) { return 0; }" +HAVE_SWIFTCALL) + +if(NOT HAVE_SWIFTCALL) + message(SEND_ERROR "CXX Compiler must support Swift calling conventions") +endif() + +check_source_compiles(CXX +"#if !(__has_attribute(swiftasynccall) && \ + __has_attribute(swift_async_context)) +#error CXX compiler must support Swift async calling conventions +#endif +int main(void) { return 0; }" +HAVE_SWIFT_ASYNC_CALL) + +if(NOT HAVE_SWIFT_ASYNC_CALL) + message(SEND_ERROR "CXX Compiler must support Swift async calling conventions") +endif() diff --git a/Runtimes/Supplemental/cmake/modules/gyb.cmake b/Runtimes/Supplemental/cmake/modules/gyb.cmake new file mode 100644 index 0000000000000..c7d487d8f4e14 --- /dev/null +++ b/Runtimes/Supplemental/cmake/modules/gyb.cmake @@ -0,0 +1,47 @@ +find_package(Python3 REQUIRED COMPONENTS Interpreter) + +# Create a target to expand a gyb source +# target_name: Name of the target +# FLAGS list of flags passed to gyb +# DEPENDS list of dependencies +# COMMENT Custom comment +function(gyb_expand source output) + set(flags) + set(arguments) + set(multival_arguments FLAGS DEPENDS) + cmake_parse_arguments(GYB "${flags}" "${arguments}" "${multival_arguments}" ${ARGN}) + + get_filename_component(full_output_path ${output} ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") + get_filename_component(dir "${full_output_path}" DIRECTORY) + get_filename_component(fname "${full_output_path}" NAME) + + file(READ "${source}" gyb_src) + string(REGEX MATCHALL "\\\$\{[\r\n\t ]*gyb.expand\\\([\r\n\t ]*[\'\"]([^\'\"]*)[\'\"]" gyb_expand_matches "${gyb_src}") + foreach(match ${gyb_expand_matches}) + string(REGEX MATCH "[\'\"]\([^\'\"]*\)[\'\"]" gyb_dep "${match}") + list(APPEND gyb_expand_deps "${CMAKE_MATCH_1}") + endforeach() + list(REMOVE_DUPLICATES gyb_expand_deps) + + set(utils_dir "${${PROJECT_NAME}_SWIFTC_SOURCE_DIR}/utils/") + set(gyb_tool "${utils_dir}/gyb") + + # All the tidbits to track for changes + list(APPEND GYB_DEPENDS + "${source}" + "${utils_dir}/GYBUnicodeDataUtils.py" + "${utils_dir}/SwiftIntTypes.py" + "${utils_dir}/SwiftFloatingPointTypes.py" + "${utils_dir}/UnicodeData/GraphemeBreakProperty.txt" + "${utils_dir}/UnicodeData/GraphemeBreakTest.txt" + "${utils_dir}/gyb_stdlib_support.py") + add_custom_command( + OUTPUT "${full_output_path}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${dir}" + COMMAND "${CMAKE_COMMAND}" -E env "$" "${gyb_tool}" ${GYB_FLAGS} -o "${full_output_path}.tmp" "${source}" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${full_output_path}.tmp" "${full_output_path}" + COMMAND "${CMAKE_COMMAND}" -E remove "${full_output_path}.tmp" + DEPENDS ${gyb_tool} ${gyb_tool}.py ${GYB_DEPENDS} ${gyb_expand_deps} + COMMENT "Generating GYB source ${fname} from ${source}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") +endfunction() diff --git a/Runtimes/cmake/modules/SwiftProjectVersion.cmake b/Runtimes/cmake/modules/SwiftProjectVersion.cmake new file mode 100644 index 0000000000000..36b7ae6b2db63 --- /dev/null +++ b/Runtimes/cmake/modules/SwiftProjectVersion.cmake @@ -0,0 +1,23 @@ +# This module sets the Swift version number variable consistently across the +# Swift runtime projects. + +## Result Variable +# +# ``SWIFT_RUNTIME_VERSION`` +# The computed version number applied to apply to the project. +# If ``SWIFT_RUNTIME_VERSION`` is set prior to entering the module, the version +# is not modified. + +block(PROPAGATE SWIFT_RUNTIME_VERSION) + if(SWIFT_RUNTIME_VERSION) + return() + endif() + + if($ENV{BUILD_NUMBER}) + # Microsoft build numbers limit each version number component to [0 - 65535] + # https://learn.microsoft.com/en-us/windows/win32/sbscs/assembly-versions + math(EXPR BUILD_NUMBER "$ENV{BUILD_NUMBER} % 65535") + set(BUILD_NUMBER ".${BUILD_NUMBER}") + endif() + set(SWIFT_RUNTIME_VERSION 6.3.0${BUILD_NUMBER}) +endblock() diff --git a/Runtimes/cmake/toolchains/Darwin.toolchain.cmake b/Runtimes/cmake/toolchains/Darwin.toolchain.cmake new file mode 100644 index 0000000000000..1cc1ddea00f33 --- /dev/null +++ b/Runtimes/cmake/toolchains/Darwin.toolchain.cmake @@ -0,0 +1,106 @@ +# Toolchain configuration when compiling for Darwin Platforms +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_Swift_COMPILER_WORKS YES) +set(CMAKE_C_COMPILER_WORKS YES) +set(CMAKE_CXX_COMPILER_WORKS YES) + +find_program(XCRUN_EXECUTABLE NAMES "xcrun" REQUIRED) + +if(NOT CMAKE_OSX_SYSROOT) + if(NOT DEFINED ENV{SDKROOT}) + message(FATAL_ERROR "`CMAKE_OSX_SYSROOT` and the 'SDKROOT' environment variable are not set") + endif() + + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --show-sdk-path -sdk $ENV{SDKROOT} + OUTPUT_VARIABLE SDKROOT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(NOT EXISTS ${SDKROOT}) + message(FATAL_ERROR "SDKROOT could not be detected!") + endif() + + message(STATUS "Using SDKROOT: ${SDKROOT}") + set(CMAKE_OSX_SYSROOT "${SDKROOT}" CACHE FILEPATH "") +endif() + +if(NOT CMAKE_C_COMPILER) + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find clang + OUTPUT_VARIABLE CMAKE_C_COMPILER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using C compiler ${CMAKE_C_COMPILER}") +endif() + +if(NOT CMAKE_CXX_COMPILER) + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find clang++ + OUTPUT_VARIABLE CMAKE_CXX_COMPILER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using CXX compiler ${CMAKE_CXX_COMPILER}") +endif() + +if(NOT CMAKE_Swift_COMPILER) + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find swiftc + OUTPUT_VARIABLE CMAKE_Swift_COMPILER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using Swift compiler ${CMAKE_Swift_COMPILER}") +endif() + +if(NOT CMAKE_AR) + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find ar + OUTPUT_VARIABLE CMAKE_AR + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using ar ${CMAKE_AR}") +endif() + +if(NOT CMAKE_RANLIB) + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find ranlib + OUTPUT_VARIABLE CMAKE_RANLIB + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using ranlib ${CMAKE_RANLIB}") +endif() + +if(NOT CMAKE_STRIP) + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find strip + OUTPUT_VARIABLE CMAKE_STRIP + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using strip ${CMAKE_STRIP}") +endif() + +if(NOT CMAKE_DSYMUTIL) + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find dsymutil + OUTPUT_VARIABLE CMAKE_DSYMUTIL + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using dsymutil ${CMAKE_DSYMUTIL}") +endif() + +if(NOT CMAKE_LIBTOOL) + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find libtool + OUTPUT_VARIABLE CMAKE_LIBTOOL + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using libtool ${CMAKE_LIBTOOL}") +endif() + +if(NOT CMAKE_CODESIGN) + execute_process(COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find codesign + OUTPUT_VARIABLE CMAKE_CODESIGN + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using codesign ${CMAKE_CODESIGN}") +endif() + +if(NOT CMAKE_CODESIGN_ALLOCATE) + execute_process( + COMMAND "${XCRUN_EXECUTABLE}" --sdk ${CMAKE_OSX_SYSROOT} --find codesign_allocate + OUTPUT_VARIABLE CMAKE_CODESIGN_ALLOCATE + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using codesign_allocate ${CMAKE_CODESIGN_ALLOCATE}") +endif() diff --git a/SwiftCompilerSources/CMakeLists.txt b/SwiftCompilerSources/CMakeLists.txt index f38418922440c..f559638880537 100644 --- a/SwiftCompilerSources/CMakeLists.txt +++ b/SwiftCompilerSources/CMakeLists.txt @@ -6,6 +6,8 @@ # See http://swift.org/LICENSE.txt for license information # See http://swift.org/CONTRIBUTORS.txt for Swift project authors +include(SwiftUtils) + # Following function are needed as a workaround until it's possible to compile # swift code with cmake's builtin swift support. @@ -101,19 +103,12 @@ function(add_swift_compiler_modules_library name) set(swift_compile_options "-color-diagnostics" "-Xfrontend" "-validate-tbd-against-ir=none" - "${cxx_interop_flag}" + "-cxx-interoperability-mode=default" "-Xfrontend" "-disable-target-os-checking" "-Xcc" "-std=c++17" "-Xcc" "-DCOMPILED_WITH_SWIFT" "-Xcc" "-DSWIFT_TARGET" "-Xcc" "-UIBOutlet" "-Xcc" "-UIBAction" "-Xcc" "-UIBInspectable") - # Prior to 5.9, we have to use the experimental flag for C++ interop. - if (CMAKE_Swift_COMPILER_VERSION VERSION_LESS 5.9) - list(APPEND swift_compile_options "-Xfrontend" "-enable-experimental-cxx-interop") - else() - list(APPEND swift_compile_options "-cxx-interoperability-mode=default") - endif() - if (NOT BOOTSTRAPPING_MODE STREQUAL "HOSTTOOLS") if(SWIFT_MIN_RUNTIME_VERSION) list(APPEND swift_compile_options @@ -128,10 +123,19 @@ function(add_swift_compiler_modules_library name) list(APPEND swift_compile_options "-Xfrontend" "-cxx-interop-use-opaque-pointer-for-moveonly") endif() - if(CMAKE_BUILD_TYPE STREQUAL "Debug") + is_build_type_with_debuginfo("${CMAKE_BUILD_TYPE}" debug) + if (debug) list(APPEND swift_compile_options "-g") - else() - list(APPEND swift_compile_options "-O" "-cross-module-optimization") + endif() + + is_build_type_optimized("${CMAKE_BUILD_TYPE}" optimized) + if (optimized) + if("${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel") + list(APPEND swift_compile_options "-Osize") + else() + list(APPEND swift_compile_options "-O") + endif() + list(APPEND swift_compile_options "-cross-module-optimization") endif() if(LLVM_ENABLE_ASSERTIONS) @@ -202,6 +206,18 @@ function(add_swift_compiler_modules_library name) # Workaround for https://github.com/swiftlang/llvm-project/issues/7172 list(APPEND swift_compile_options "-Xcc" "-Xclang" "-Xcc" "-fmodule-format=raw") + + elseif(swift_exec_bin_dir MATCHES ".*/swiftly/bin") + # Detect and handle swiftly-managed hosts. + execute_process(COMMAND swiftly use --print-location + OUTPUT_VARIABLE swiftly_dir + ERROR_VARIABLE err) + if(err) + message(SEND_ERROR "Failed to find swiftly Swift compiler") + endif() + string(STRIP "${swiftly_dir}" swiftly_dir) + + list(APPEND sdk_option "-I" "${swiftly_dir}/usr/lib" "-I" "${swiftly_dir}/usr/lib") else() list(APPEND sdk_option "-I" "${swift_exec_bin_dir}/../lib" "-I" "${sdk_path}/usr/lib") endif() @@ -286,12 +302,6 @@ function(add_swift_compiler_modules_library name) endforeach() # Create a static library containing all module object files. - if (XCODE) - # Xcode does not compile libraries that contain only object files. - # Therefore, it fails to create the static library. As a workaround, - # we add an empty source file force_lib.c to the target. - set(all_obj_files force_lib.c ${all_obj_files}) - endif() add_library(${name} STATIC ${all_obj_files}) add_dependencies(${name} ${all_module_targets}) set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX) @@ -341,7 +351,7 @@ else() message(FATAL_ERROR "The Swift compiler (${CMAKE_Swift_COMPILER}) differs from the Swift compiler in SWIFT_NATIVE_SWIFT_TOOLS_PATH (${SWIFT_NATIVE_SWIFT_TOOLS_PATH}/swiftc).") endif() - set(min_supported_swift_version 5.8) + set(min_supported_swift_version 5.9) if(CMAKE_Swift_COMPILER_VERSION VERSION_LESS "${min_supported_swift_version}") message(FATAL_ERROR "Outdated Swift compiler: building with host tools requires Swift ${min_supported_swift_version} or newer. " diff --git a/SwiftCompilerSources/Sources/AST/CMakeLists.txt b/SwiftCompilerSources/Sources/AST/CMakeLists.txt index 63a8ec912e1d9..303857f3f98d7 100644 --- a/SwiftCompilerSources/Sources/AST/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/AST/CMakeLists.txt @@ -12,6 +12,7 @@ add_swift_compiler_module(AST SOURCES Declarations.swift Conformance.swift + DiagnosticEngine.swift GenericSignature.swift Registration.swift SubstitutionMap.swift diff --git a/SwiftCompilerSources/Sources/AST/Conformance.swift b/SwiftCompilerSources/Sources/AST/Conformance.swift index 1d7510d9d217e..f67c942924c3d 100644 --- a/SwiftCompilerSources/Sources/AST/Conformance.swift +++ b/SwiftCompilerSources/Sources/AST/Conformance.swift @@ -17,7 +17,7 @@ import ASTBridging /// members to the type (or extension) members that provide the functionality for the concrete type. /// /// TODO: Ideally, `Conformance` should be an enum -public struct Conformance: CustomStringConvertible, NoReflectionChildren { +public struct Conformance: CustomStringConvertible, Hashable, NoReflectionChildren { public let bridged: BridgedConformance public init(bridged: BridgedConformance) { @@ -28,6 +28,14 @@ public struct Conformance: CustomStringConvertible, NoReflectionChildren { return String(taking: bridged.getDebugDescription()) } + public func hash(into hasher: inout Hasher) { + hasher.combine(bridged.opaqueValue) + } + + public static func ==(lhs: Conformance, rhs: Conformance) -> Bool { + lhs.bridged.opaqueValue == rhs.bridged.opaqueValue + } + public var isConcrete: Bool { bridged.isConcrete() } public var isValid: Bool { bridged.isValid() } @@ -37,7 +45,7 @@ public struct Conformance: CustomStringConvertible, NoReflectionChildren { return Type(bridged: bridged.getType()) } - public var proto: ProtocolDecl { + public var `protocol`: ProtocolDecl { return bridged.getRequirement().getAs(ProtocolDecl.self) } public var isSpecialized: Bool { diff --git a/SwiftCompilerSources/Sources/AST/Declarations.swift b/SwiftCompilerSources/Sources/AST/Declarations.swift index b20349e6cde15..2041d32fd22de 100644 --- a/SwiftCompilerSources/Sources/AST/Declarations.swift +++ b/SwiftCompilerSources/Sources/AST/Declarations.swift @@ -23,6 +23,9 @@ public class Decl: CustomStringConvertible, Hashable { /// The module in which this declaration resides. public var parentModule: ModuleDecl { bridged.getModuleContext().getAs(ModuleDecl.self) } + /// The parent DeclContext if it is a Decl. + public var parent: Decl? { bridged.getParent().decl } + // True if this declaration is imported from C/C++/ObjC. public var hasClangNode: Bool { bridged.hasClangNode() } @@ -55,7 +58,9 @@ public class NominalTypeDecl: GenericTypeDecl { } } -final public class EnumDecl: NominalTypeDecl {} +final public class EnumDecl: NominalTypeDecl { + public var hasRawType: Bool { bridged.Enum_hasRawType() } +} final public class StructDecl: NominalTypeDecl { public var hasUnreferenceableStorage: Bool { bridged.Struct_hasUnreferenceableStorage() } @@ -69,7 +74,9 @@ final public class ClassDecl: NominalTypeDecl { } } -final public class ProtocolDecl: NominalTypeDecl {} +final public class ProtocolDecl: NominalTypeDecl { + public var requiresClass: Bool { bridged.ProtocolDecl_requiresClass() } +} final public class BuiltinTupleDecl: NominalTypeDecl {} @@ -109,7 +116,9 @@ final public class AccessorDecl: FuncDecl {} final public class MacroDecl: ValueDecl {} -final public class EnumElementDecl: ValueDecl {} +final public class EnumElementDecl: ValueDecl { + public var hasAssociatedValues: Bool { bridged.EnumElementDecl_hasAssociatedValues() } +} final public class ExtensionDecl: Decl {} @@ -117,6 +126,8 @@ final public class TopLevelCodeDecl: Decl {} final public class ImportDecl: Decl {} +final public class UsingDecl: Decl {} + final public class PrecedenceGroupDecl: Decl {} final public class MissingDecl: Decl {} @@ -148,3 +159,9 @@ extension OptionalBridgedDeclObj { public var decl: Decl? { obj.getAs(Decl.self) } public func getAs(_ declType: T.Type) -> T? { obj.getAs(T.self) } } + +extension Optional where Wrapped == Decl { + public var bridged: OptionalBridgedDeclObj { + OptionalBridgedDeclObj(self?.bridged.obj) + } +} diff --git a/SwiftCompilerSources/Sources/AST/DiagnosticEngine.swift b/SwiftCompilerSources/Sources/AST/DiagnosticEngine.swift new file mode 100644 index 0000000000000..08ae4a83cafc6 --- /dev/null +++ b/SwiftCompilerSources/Sources/AST/DiagnosticEngine.swift @@ -0,0 +1,186 @@ +//===--- DiagnosticEngine.swift -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 - 2025 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 ASTBridging +import Basic + +public typealias DiagID = swift.DiagID + +public protocol DiagnosticArgument { + func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) +} +extension String: DiagnosticArgument { + public func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { + _withBridgedStringRef { fn(BridgedDiagnosticArgument($0)) } + } +} +extension StringRef: DiagnosticArgument { + public func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { + fn(BridgedDiagnosticArgument(_bridged)) + } +} +extension Int: DiagnosticArgument { + public func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { + fn(BridgedDiagnosticArgument(self)) + } +} +extension Type: DiagnosticArgument { + public func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { + fn(bridged.asDiagnosticArgument()) + } +} + +public struct DiagnosticFixIt { + let start: SourceLoc + let byteLength: Int + let text: String + + init(start: SourceLoc, byteLength: Int, replacement text: String) { + self.start = start + self.byteLength = byteLength + self.text = text + } + + func withBridgedDiagnosticFixIt(_ fn: (BridgedDiagnosticFixIt) -> Void) { + text._withBridgedStringRef { bridgedTextRef in + let bridgedDiagnosticFixIt = BridgedDiagnosticFixIt( + start.bridged, UInt32(byteLength), + bridgedTextRef) + fn(bridgedDiagnosticFixIt) + } + } +} + +public struct DiagnosticEngine { + private let bridged: BridgedDiagnosticEngine + + public init(bridged: BridgedDiagnosticEngine) { + self.bridged = bridged + } + init?(bridged: BridgedNullableDiagnosticEngine) { + guard let raw = bridged.raw else { + return nil + } + self.bridged = BridgedDiagnosticEngine(raw: raw) + } + + public func diagnose(_ id: DiagID, + _ args: [DiagnosticArgument], + at position: SourceLoc?, + highlight: CharSourceRange? = nil, + fixIts: [DiagnosticFixIt] = []) { + let bridgedSourceLoc = position.bridgedLocation + let highlightStart: swift.SourceLoc + let highlightLength: UInt32 + if let highlight = highlight { + highlightStart = highlight.start.bridged + highlightLength = highlight.byteLength + } else { + highlightStart = .init() + highlightLength = 0 + } + var bridgedArgs: [BridgedDiagnosticArgument] = [] + var bridgedFixIts: [BridgedDiagnosticFixIt] = [] + + // Build a higher-order function to wrap every 'withBridgedXXX { ... }' + // calls, so we don't escape anything from the closure. 'bridgedArgs' and + // 'bridgedFixIts' are temporary storage to store bridged values. So they + // should not be used after the closure is executed. + + var closure: () -> Void = { + bridgedArgs.withBridgedArrayRef { bridgedArgsRef in + bridgedFixIts.withBridgedArrayRef { bridgedFixItsRef in + bridged.diagnose(at: bridgedSourceLoc, id, bridgedArgsRef, + highlightAt: highlightStart, + highlightLength: highlightLength, + fixIts: bridgedFixItsRef) + } + } + } + // 'reversed()' because the closure should be wrapped in that order. + for arg in args.reversed() { + closure = { [closure, arg] in + arg._withBridgedDiagnosticArgument { bridgedArg in + bridgedArgs.append(bridgedArg) + closure() + } + } + } + // 'reversed()' because the closure should be wrapped in that order. + for fixIt in fixIts.reversed() { + closure = { [closure, fixIt] in + fixIt.withBridgedDiagnosticFixIt { bridgedFixIt in + bridgedFixIts.append(bridgedFixIt) + closure() + } + } + } + + closure() + } + + // FIXME: Remove this overload once https://github.com/swiftlang/swift/issues/82318 is fixed. + public func diagnose( + _ id: DiagID, + arguments args: [DiagnosticArgument], + at position: SourceLoc?, + highlight: CharSourceRange? = nil, + fixIts: [DiagnosticFixIt] = [] + ) { + diagnose(id, args, at: position, highlight: highlight, fixIts: fixIts) + } + + public func diagnose(_ id: DiagID, + _ args: DiagnosticArgument..., + at position: SourceLoc?, + highlight: CharSourceRange? = nil, + fixIts: DiagnosticFixIt...) { + diagnose(id, args, at: position, highlight: highlight, fixIts: fixIts) + } + + public func diagnose(_ diagnostic: Diagnostic) { + let loc = diagnostic.location.getSourceLocation(diagnosticEngine: self) + diagnose(diagnostic.id, diagnostic.arguments, at: loc) + } + + /// Loads the file at `path` and returns a `SourceLoc` pointing to `line` and `column` in the file. + /// Returns nil if the file cannot be loaded. + public func getLocationFromExternalSource(path: StringRef, line: Int, column: Int) -> SourceLoc? { + return SourceLoc(bridged: bridged.getLocationFromExternalSource(path: path._bridged, line: line, column: column)) + } +} + +/// Something which can provide a `SourceLoc` for diagnostics. +public protocol ProvidingSourceLocation { + func getSourceLocation(diagnosticEngine: DiagnosticEngine) -> SourceLoc? +} + +extension SourceLoc: ProvidingSourceLocation { + public func getSourceLocation(diagnosticEngine: DiagnosticEngine) -> SourceLoc? { self } +} + +/// A utility struct which allows throwing a Diagnostic. +public struct Diagnostic : Error { + public let id: DiagID + public let arguments: [DiagnosticArgument] + public let location: SourceLocation + + public init(_ id: DiagID, _ arguments: DiagnosticArgument..., at location: SourceLocation) { + self.init(id, arguments, at: location) + } + + public init(_ id: DiagID, _ arguments: [DiagnosticArgument], at location: SourceLocation) { + self.id = id + self.arguments = arguments + self.location = location + } +} diff --git a/SwiftCompilerSources/Sources/AST/GenericSignature.swift b/SwiftCompilerSources/Sources/AST/GenericSignature.swift index e490a7ee6e828..64b858e9b4bd2 100644 --- a/SwiftCompilerSources/Sources/AST/GenericSignature.swift +++ b/SwiftCompilerSources/Sources/AST/GenericSignature.swift @@ -29,4 +29,10 @@ public struct GenericSignature: CustomStringConvertible, NoReflectionChildren { public var genericParameters: TypeArray { TypeArray(bridged: bridged.getGenericParams()) } + + public func mapTypeIntoContext(_ type: Type) -> Type { + Type(bridged: bridged.mapTypeIntoContext(type.bridged)) + } + + public var isEmpty: Bool { bridged.impl == nil } } diff --git a/SwiftCompilerSources/Sources/AST/Registration.swift b/SwiftCompilerSources/Sources/AST/Registration.swift index f1d09ebf4190d..6c4f62bf69cb7 100644 --- a/SwiftCompilerSources/Sources/AST/Registration.swift +++ b/SwiftCompilerSources/Sources/AST/Registration.swift @@ -36,6 +36,7 @@ public func registerAST() { registerDecl(ExtensionDecl.self) registerDecl(TopLevelCodeDecl.self) registerDecl(ImportDecl.self) + registerDecl(UsingDecl.self) registerDecl(PrecedenceGroupDecl.self) registerDecl(MissingDecl.self) registerDecl(MissingMemberDecl.self) diff --git a/SwiftCompilerSources/Sources/AST/SubstitutionMap.swift b/SwiftCompilerSources/Sources/AST/SubstitutionMap.swift index 494a55e35749d..c967fe543b1d6 100644 --- a/SwiftCompilerSources/Sources/AST/SubstitutionMap.swift +++ b/SwiftCompilerSources/Sources/AST/SubstitutionMap.swift @@ -68,7 +68,7 @@ public struct SubstitutionMap: CustomStringConvertible, NoReflectionChildren { TypeArray(bridged: bridged.getReplacementTypes()) } - /// The single replacement type if it's guarnateed that the substitution map has a single replacement type. + /// The single replacement type if it's guaranteed that the substitution map has a single replacement type. public var replacementType: Type { assert(replacementTypes.count == 1) return replacementTypes[0] diff --git a/SwiftCompilerSources/Sources/AST/Type.swift b/SwiftCompilerSources/Sources/AST/Type.swift index 77d8646f9529c..c193387800f1a 100644 --- a/SwiftCompilerSources/Sources/AST/Type.swift +++ b/SwiftCompilerSources/Sources/AST/Type.swift @@ -14,7 +14,7 @@ import Basic import ASTBridging /// A Swift type. -/// It is not necessarily canoncial, e.g. typealiases are not resolved. +/// It is not necessarily canonical, e.g. typealiases are not resolved. public struct Type: TypeProperties, CustomStringConvertible, NoReflectionChildren { public enum TraitResult { case isNot @@ -48,6 +48,10 @@ public struct Type: TypeProperties, CustomStringConvertible, NoReflectionChildre public var instanceTypeOfMetatype: Type { Type(bridged: bridged.getInstanceTypeOfMetatype()) } + public var staticTypeOfDynamicSelf: Type { Type(bridged: bridged.getStaticTypeOfDynamicSelf()) } + + public var interfaceTypeOfArchetype: Type { Type(bridged: bridged.getInterfaceTypeOfArchetype()) } + public var superClassType: Type? { precondition(isClass) let bridgedSuperClassTy = bridged.getSuperClassType() @@ -62,10 +66,6 @@ public struct Type: TypeProperties, CustomStringConvertible, NoReflectionChildre public func subst(with substitutionMap: SubstitutionMap) -> Type { return Type(bridged: bridged.subst(substitutionMap.bridged)) } - - public func subst(type: Type, with targetType: Type) -> Type { - return Type(bridged: bridged.subst(type.bridged, targetType.bridged)) - } } /// A Type that is statically known to be canonical. @@ -86,10 +86,6 @@ public struct CanonicalType: TypeProperties, CustomStringConvertible, NoReflecti public func subst(with substitutionMap: SubstitutionMap) -> CanonicalType { return rawType.subst(with: substitutionMap).canonical } - - public func subst(type: CanonicalType, with targetType: CanonicalType) -> CanonicalType { - return self.rawType.subst(type: type.rawType, with: targetType.rawType).canonical - } } /// Implements the common members of `AST.Type`, `AST.CanonicalType` and `SIL.Type`. @@ -112,6 +108,7 @@ extension TypeProperties { public var isBuiltinFloat: Bool { rawType.bridged.isBuiltinFloat() } public var isBuiltinVector: Bool { rawType.bridged.isBuiltinVector() } + public var isBuiltinFixedArray: Bool { rawType.bridged.isBuiltinFixedArray() } public var isClass: Bool { if let nominal = nominal, nominal is ClassDecl { @@ -136,14 +133,19 @@ extension TypeProperties { public var isTuple: Bool { rawType.bridged.isTuple() } public var isFunction: Bool { rawType.bridged.isFunction() } + public var isArchetype: Bool { rawType.bridged.isArchetype() } public var isExistentialArchetype: Bool { rawType.bridged.isExistentialArchetype() } public var isExistentialArchetypeWithError: Bool { rawType.bridged.isExistentialArchetypeWithError() } + public var isRootArchetype: Bool { rawType.interfaceTypeOfArchetype.isGenericTypeParameter } + public var isRootExistentialArchetype: Bool { isExistentialArchetype && isRootArchetype } public var isExistential: Bool { rawType.bridged.isExistential() } public var isClassExistential: Bool { rawType.bridged.isClassExistential() } + public var isGenericTypeParameter: Bool { rawType.bridged.isGenericTypeParam() } public var isUnownedStorageType: Bool { return rawType.bridged.isUnownedStorageType() } public var isMetatype: Bool { rawType.bridged.isMetatypeType() } public var isExistentialMetatype: Bool { rawType.bridged.isExistentialMetatypeType() } public var isDynamicSelf: Bool { rawType.bridged.isDynamicSelf()} + public var isBox: Bool { rawType.bridged.isBox() } /// True if this is the type which represents an integer literal used in a type position. /// For example `N` in `struct T {}` @@ -184,13 +186,33 @@ extension TypeProperties { public var hasArchetype: Bool { rawType.bridged.hasArchetype() } public var hasTypeParameter: Bool { rawType.bridged.hasTypeParameter() } public var hasLocalArchetype: Bool { rawType.bridged.hasLocalArchetype() } + public var hasDynamicSelf: Bool { rawType.bridged.hasDynamicSelf() } public var isEscapable: Bool { rawType.bridged.isEscapable() } public var isNoEscape: Bool { rawType.bridged.isNoEscape() } + public var isBuiltinType: Bool { rawType.bridged.isBuiltinType() } + public var archetypeRequiresClass: Bool { rawType.bridged.archetypeRequiresClass() } public var representationOfMetatype: AST.`Type`.MetatypeRepresentation { rawType.bridged.getRepresentationOfMetatype().representation } + public var builtinFixedArrayElementType: CanonicalType { + CanonicalType(bridged: rawType.bridged.getBuiltinFixedArrayElementType()) + } + public var builtinFixedArraySizeType: CanonicalType { + CanonicalType(bridged: rawType.bridged.getBuiltinFixedArraySizeType()) + } + + /// Returns the value of an integer value type (see `isInteger`). + /// Returns nil if the value is not representable in an `Int`. + public var valueOfInteger: Int? { + let optionalInt = rawType.bridged.getValueOfIntegerType() + if optionalInt.hasValue { + return optionalInt.value + } + return nil + } + /// Assumes this is a nominal type. Returns a substitution map that sends each /// generic parameter of the declaration's generic signature to the corresponding /// generic argument of this nominal type. @@ -207,7 +229,7 @@ extension TypeProperties { rawType.bridged.getNominalOrBoundGenericNominal().getAs(NominalTypeDecl.self) } - /// Performas a global conformance lookup for this type for `protocol`. + /// Performs a global conformance lookup for this type for `protocol`. /// It checks conditional requirements. /// /// This type must be a contextualized type. It must not contain type parameters. diff --git a/SwiftCompilerSources/Sources/Basic/SourceLoc.swift b/SwiftCompilerSources/Sources/Basic/SourceLoc.swift index 7a631452b98a6..7eb37a720becb 100644 --- a/SwiftCompilerSources/Sources/Basic/SourceLoc.swift +++ b/SwiftCompilerSources/Sources/Basic/SourceLoc.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2025 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,26 +12,25 @@ import BasicBridging +/// Represents a location in source code. +/// It is basically a pointer into a buffer of the loaded source file (managed by `DiagnosticEngine`). +/// In contrast to just having a filename+line+column, this allows displaying the context around +/// the location when printing diagnostics. public struct SourceLoc { - /// Points into a source file. - public let bridged: BridgedSourceLoc + public let bridged: swift.SourceLoc - public init?(bridged: BridgedSourceLoc) { - guard bridged.isValid else { + public init?(bridged: swift.SourceLoc) { + guard bridged.isValid() else { return nil } self.bridged = bridged } } -extension SourceLoc { - public func advanced(by n: Int) -> SourceLoc { - SourceLoc(bridged: bridged.advanced(by: n))! - } -} - -extension Optional where Wrapped == SourceLoc { - public var bridged: BridgedSourceLoc { +extension Optional { + // TODO: This can go back to being 'bridged' once we upgrade to a toolchain + // where https://github.com/swiftlang/swift/issues/82609 is fixed. + public var bridgedLocation: swift.SourceLoc { self?.bridged ?? .init() } } @@ -45,7 +44,7 @@ public struct CharSourceRange { self.byteLength = byteLength } - public init?(bridgedStart: BridgedSourceLoc, byteLength: UInt32) { + public init?(bridgedStart: swift.SourceLoc, byteLength: UInt32) { guard let start = SourceLoc(bridged: bridgedStart) else { return nil } diff --git a/SwiftCompilerSources/Sources/Basic/Utils.swift b/SwiftCompilerSources/Sources/Basic/Utils.swift index c68354e06c6b0..cff63254d5a32 100644 --- a/SwiftCompilerSources/Sources/Basic/Utils.swift +++ b/SwiftCompilerSources/Sources/Basic/Utils.swift @@ -79,11 +79,15 @@ public extension NoReflectionChildren { // StringRef //===----------------------------------------------------------------------===// -public struct StringRef : CustomStringConvertible, NoReflectionChildren { +public struct StringRef : CustomStringConvertible, NoReflectionChildren, ExpressibleByStringLiteral { public let _bridged: BridgedStringRef public init(bridged: BridgedStringRef) { self._bridged = bridged } + public init(stringLiteral: StaticString) { + self._bridged = BridgedStringRef(data: stringLiteral.utf8Start, count: stringLiteral.utf8CodeUnitCount) + } + public var string: String { String(_bridged) } public var description: String { string } @@ -96,6 +100,20 @@ public struct StringRef : CustomStringConvertible, NoReflectionChildren { return buffer[index] } + public func startsWith(_ prefix: StaticString) -> Bool { + return prefix.withUTF8Buffer { (prefixBuffer: UnsafeBufferPointer) in + if count < prefixBuffer.count { + return false + } + let buffer = UnsafeBufferPointer(start: _bridged.data, count: prefixBuffer.count) + return buffer.elementsEqual(prefixBuffer, by: ==) + } + } + + /// This overload is disfavored to make sure that it's only used for cases that don't involve literals, for that + /// `==(StringRef, StaticString) -> Bool` is preferred. Otherwise these overloads are + /// going to be ambiguous because both StringRef, StaticString conform to `ExpressibleByStringLiteral`. + @_disfavoredOverload public static func ==(lhs: StringRef, rhs: StringRef) -> Bool { let lhsBuffer = UnsafeBufferPointer(start: lhs._bridged.data, count: lhs.count) let rhsBuffer = UnsafeBufferPointer(start: rhs._bridged.data, count: rhs.count) diff --git a/SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift b/SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift index b238b39087af9..1f74f1d8b93ac 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift @@ -15,7 +15,7 @@ import SIL extension FunctionPassContext { var aliasAnalysis: AliasAnalysis { - let bridgedAA = _bridged.getAliasAnalysis() + let bridgedAA = bridgedPassContext.getAliasAnalysis() return AliasAnalysis(bridged: bridgedAA, context: self) } } @@ -97,8 +97,8 @@ struct AliasAnalysis { return false } } - // Finaly use escape info to check if one address "escapes" to the other address. - return v1.allContainedAddresss.canAddressAlias(with: v2.allContainedAddresss, context) + // Finally use escape info to check if one address "escapes" to the other address. + return v1.allContainedAddresses.canAddressAlias(with: v2.allContainedAddresses, context) } static func register() { @@ -113,7 +113,7 @@ struct AliasAnalysis { bridgedAliasAnalysis.mutableCachePointer.assumingMemoryBound(to: Cache.self).deinitialize(count: 1) }, // getMemEffectsFn - { (bridgedCtxt: BridgedPassContext, + { (bridgedCtxt: BridgedContext, bridgedAliasAnalysis: BridgedAliasAnalysis, bridgedAddr: BridgedValue, bridgedInst: BridgedInstruction) -> BridgedMemoryBehavior in @@ -121,7 +121,7 @@ struct AliasAnalysis { return aa.getMemoryEffect(of: bridgedInst.instruction, on: bridgedAddr.value).bridged }, // isObjReleasedFn - { (bridgedCtxt: BridgedPassContext, + { (bridgedCtxt: BridgedContext, bridgedAliasAnalysis: BridgedAliasAnalysis, bridgedObj: BridgedValue, bridgedInst: BridgedInstruction) -> Bool in @@ -142,13 +142,13 @@ struct AliasAnalysis { }, // isAddrVisibleFromObj - { (bridgedCtxt: BridgedPassContext, + { (bridgedCtxt: BridgedContext, bridgedAliasAnalysis: BridgedAliasAnalysis, bridgedAddr: BridgedValue, bridgedObj: BridgedValue) -> Bool in let context = FunctionPassContext(_bridged: bridgedCtxt) let aa = AliasAnalysis(bridged: bridgedAliasAnalysis, context: context) - let addr = bridgedAddr.value.allContainedAddresss + let addr = bridgedAddr.value.allContainedAddresses // This is similar to `canReferenceSameFieldFn`, except that all addresses of all objects are // considered which are transitively visible from `bridgedObj`. @@ -159,7 +159,7 @@ struct AliasAnalysis { }, // mayAliasFn - { (bridgedCtxt: BridgedPassContext, + { (bridgedCtxt: BridgedContext, bridgedAliasAnalysis: BridgedAliasAnalysis, bridgedLhs: BridgedValue, bridgedRhs: BridgedValue) -> Bool in @@ -253,17 +253,26 @@ struct AliasAnalysis { case let storeBorrow as StoreBorrowInst: return memLoc.mayAlias(with: storeBorrow.destination, self) ? .init(write: true) : .noEffects - case let mdi as MarkDependenceInstruction: + case let mdi as MarkDependenceInst: if mdi.base.type.isAddress && memLoc.mayAlias(with: mdi.base, self) { return .init(read: true) } return .noEffects + case let mdai as MarkDependenceAddrInst: + if memLoc.mayAlias(with: mdai.address, self) { + return .init(read: true, write: true) + } + if mdai.base.type.isAddress && memLoc.mayAlias(with: mdai.base, self) { + return .init(read: true) + } + return .noEffects + case let copy as SourceDestAddrInstruction: let mayRead = memLoc.mayAlias(with: copy.source, self) let mayWrite = memLoc.mayAlias(with: copy.destination, self) - var effects = SideEffects.Memory(read: mayRead, write: mayWrite || (mayRead && copy.isTakeOfSrc)) - if !copy.isInitializationOfDest { + var effects = SideEffects.Memory(read: mayRead, write: mayWrite || (mayRead && copy.isTakeOfSource)) + if !copy.isInitializationOfDestination { effects.merge(with: defaultEffects(of: copy, on: memLoc)) } return effects @@ -318,7 +327,8 @@ struct AliasAnalysis { return defaultEffects(of: endBorrow, on: memLoc) case let debugValue as DebugValueInst: - if debugValue.operand.value.type.isAddress && memLoc.mayAlias(with: debugValue.operand.value, self) { + let v = debugValue.operand.value + if v.type.isAddress, !(v is Undef), memLoc.mayAlias(with: v, self) { return .init(read: true) } else { return .noEffects @@ -329,7 +339,7 @@ struct AliasAnalysis { return .noEffects } if destroy.isDeadEnd { - // We don't have to take deinit effects into acount for a `destroy_value [dead_end]`. + // We don't have to take deinit effects into account for a `destroy_value [dead_end]`. // Such destroys are lowered to no-ops and will not call any deinit. return .noEffects } @@ -358,7 +368,7 @@ struct AliasAnalysis { return defaultEffects(of: endBorrow, on: memLoc) case .box, .class, .tail: // Check if the memLoc is "derived" from the begin_borrow, i.e. is an interior pointer. - var walker = FindBeginBorrowWalker(beginBorrow: endBorrow.borrow as! BorrowIntroducingInstruction) + var walker = FindBeginBorrowWalker(beginBorrow: endBorrow.borrow as! BeginBorrowInstruction) return walker.visitAccessStorageRoots(of: accessPath) ? .noEffects : .worstEffects } } @@ -425,6 +435,11 @@ struct AliasAnalysis { } let callee = builtin.operands[1].value return context.calleeAnalysis.getSideEffects(ofCallee: callee).memory + case .PrepareInitialization, .ZeroInitializer: + if builtin.arguments.count == 1, memLoc.mayAlias(with: builtin.arguments[0], self) { + return .init(write: true) + } + return .noEffects default: return defaultEffects(of: builtin, on: memLoc) } @@ -456,7 +471,7 @@ struct AliasAnalysis { } // To avoid quadratic complexity for large functions, we limit the amount of work that the EscapeUtils are - // allowed to to. This keeps the complexity linear. + // allowed to do. This keeps the complexity linear. // // This arbitrary limit is good enough for almost all functions. It lets // the EscapeUtils do several hundred up/down walks which is much more than needed in most cases. @@ -466,14 +481,14 @@ struct AliasAnalysis { for _ in function.instructions { numInsts += 1 } cache.estimatedFunctionSize = numInsts } - return 1000000 / cache.estimatedFunctionSize! + return 1_000_000 / cache.estimatedFunctionSize! } /// Returns true if the `instruction` (which in general writes to memory) is immutable in a certain scope, /// defined by `address`. /// /// That means that even if we don't know anything about `instruction`, we can be sure - /// that `instruction` cannot write to `address`, if it's inside the addresse's scope. + /// that `instruction` cannot write to `address`, if it's inside the address's scope. /// An immutable scope is for example a read-only `begin_access`/`end_access` scope. /// Another example is a borrow scope of an immutable copy-on-write buffer. private func isImmutable(instruction: Instruction, inScopeOf address: Value) -> Bool { @@ -613,6 +628,14 @@ private enum ImmutableScope { if beginAccess.isUnsafe { return nil } + + // This is a workaround for a bug in the move-only checker: rdar://151841926. + // The move-only checker sometimes inserts destroy_addr within read-only static access scopes. + // TODO: remove this once the bug is fixed. + if beginAccess.isStatic { + return nil + } + switch beginAccess.accessKind { case .read: self = .readAccess(beginAccess) @@ -704,7 +727,7 @@ private enum ImmutableScope { } private struct FindBeginBorrowWalker : ValueUseDefWalker { - let beginBorrow: BorrowIntroducingInstruction + let beginBorrow: BeginBorrowInstruction var walkUpCache = WalkerCache() mutating func walkUp(value: Value, path: SmallProjectionPath) -> WalkResult { @@ -774,7 +797,7 @@ private struct FullApplyEffectsVisitor : EscapeVisitorWithResult { // In contrast to a full apply, the effects of a partial_apply don't depend on the callee // (a partial_apply doesn't call anything, it just creates a thick function pointer). -// The only effects come from capturing the arguments (either consuming or guaranteeed). +// The only effects come from capturing the arguments (either consuming or guaranteed). private struct PartialApplyEffectsVisitor : EscapeVisitorWithResult { let partialApply: PartialApplyInst var result = SideEffects.Memory.noEffects diff --git a/SwiftCompilerSources/Sources/Optimizer/Analysis/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/Analysis/CMakeLists.txt index bedf3296a61df..4f95a146351ba 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Analysis/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/Analysis/CMakeLists.txt @@ -9,6 +9,7 @@ swift_compiler_sources(Optimizer AliasAnalysis.swift CalleeAnalysis.swift + LoopTree.swift DeadEndBlocksAnalysis.swift DominatorTree.swift PostDominatorTree.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/Analysis/DominatorTree.swift b/SwiftCompilerSources/Sources/Optimizer/Analysis/DominatorTree.swift index c7c106d879c08..132434c5eb028 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Analysis/DominatorTree.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Analysis/DominatorTree.swift @@ -15,6 +15,31 @@ import OptimizerBridging struct DominatorTree { let bridged: BridgedDomTree + + func getChildren(of block: BasicBlock) -> DomChildren { + return DomChildren(bridgedDomTree: bridged, bb: block) + } +} + +struct DomChildren: BridgedRandomAccessCollection { + private let bridgedDomTree: BridgedDomTree + private let block: BasicBlock + + let count: Int + + var startIndex: Int { return 0 } + var endIndex: Int { return count } + + init(bridgedDomTree: BridgedDomTree, bb: BasicBlock) { + self.bridgedDomTree = bridgedDomTree + self.block = bb + self.count = bridgedDomTree.getNumberOfChildren(bb.bridged) + } + + subscript(_ index: Int) -> BasicBlock { + assert(index >= startIndex && index < endIndex) + return bridgedDomTree.getChildAt(block.bridged, index).block + } } extension BasicBlock { diff --git a/SwiftCompilerSources/Sources/Optimizer/Analysis/LoopTree.swift b/SwiftCompilerSources/Sources/Optimizer/Analysis/LoopTree.swift new file mode 100644 index 0000000000000..aab4605926c3b --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/Analysis/LoopTree.swift @@ -0,0 +1,213 @@ +//===--- LoopTree.swift ---------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL +import OptimizerBridging + +/// Describes top level loops. +struct LoopTree { + fileprivate let bridged: BridgedLoopTree + + let loops: TopLevelLoopArray + + init(bridged: BridgedLoopTree, context: FunctionPassContext) { + self.bridged = bridged + self.loops = TopLevelLoopArray(bridged) + } +} + +/// Describes a loop with its children. +struct Loop { + private let bridged: BridgedLoop + + let innerLoops: LoopArray + let loopBlocks: LoopBlocks + + var exitingAndLatchBlocks: some Sequence { + return header.predecessors.lazy + .filter { predecessor in + contains(block: predecessor) && !isLoopExiting(loopBlock: predecessor) + } + exitingBlocks + } + + /// Exit blocks of the loop. + /// + /// - Note: Some exit blocks will be duplicated if the loop has critical edges. + var exitBlocks: some Sequence { + return loopBlocks.lazy + .flatMap(\.successors) + .filter { !contains(block: $0) } + } + + var exitingBlocks: some Sequence { + return loopBlocks.lazy + .filter { isLoopExiting(loopBlock: $0) } + } + + init(bridged: BridgedLoop) { + self.bridged = bridged + self.innerLoops = LoopArray(bridged) + self.loopBlocks = LoopBlocks(bridged) + } + + var preheader: BasicBlock? { + bridged.getPreheader().block + } + + var header: BasicBlock { + bridged.getHeader().block + } + + /// Returns `true` if the loop has exactly one exit block. + var hasSingleExitBlock: Bool { + return exitBlocks.singleElement != nil + } + + var hasNoExitBlocks: Bool { + return exitBlocks.isEmpty + } + + private func isLoopExiting(loopBlock: BasicBlock) -> Bool { + return loopBlock.successors.contains { !contains(block: $0) } + } + + func getBlocksThatDominateAllExitingAndLatchBlocks(_ context: FunctionPassContext) -> [BasicBlock] { + var result: [BasicBlock] = [] + var cachedExitingAndLatchBlocks = Stack(context) + var workList = BasicBlockWorklist(context) + defer { + cachedExitingAndLatchBlocks.deinitialize() + workList.deinitialize() + } + + cachedExitingAndLatchBlocks.append(contentsOf: exitingAndLatchBlocks) + workList.pushIfNotVisited(header) + + while let block = workList.pop() { + guard cachedExitingAndLatchBlocks.allSatisfy({ exitBlock in + return block.dominates(exitBlock, context.dominatorTree) + }) else { + continue + } + + result.append(block) + + workList.pushIfNotVisited(contentsOf: context.dominatorTree.getChildren(of: block)) + } + + return result + } + + func contains(block: BasicBlock) -> Bool { + return bridged.contains(block.bridged) + } + + func splitCriticalExitingAndBackEdges(_ context: FunctionPassContext) { + for exitingOrLatchBlock in exitingAndLatchBlocks { + for (index, succesor) in exitingOrLatchBlock.successors.enumerated() where !contains(block: succesor) { + splitCriticalEdge( + from: exitingOrLatchBlock.terminator.parentBlock, + toEdgeIndex: index, + dominatorTree: context.dominatorTree, + loopTree: context.loopTree, + context + ) + } + } + } +} + +struct TopLevelLoopArray: BridgedRandomAccessCollection { + private let bridgedLoopTree: BridgedLoopTree + + let count: Int + + var startIndex: Int { return 0 } + var endIndex: Int { return count } + + init(_ bridgedLoopTree: BridgedLoopTree) { + self.bridgedLoopTree = bridgedLoopTree + self.count = bridgedLoopTree.getTopLevelLoopCount() + } + + subscript(_ index: Int) -> Loop { + assert(index >= startIndex && index < endIndex) + return Loop(bridged: bridgedLoopTree.getLoop(index)) + } +} + +struct LoopArray: BridgedRandomAccessCollection { + private let bridgedLoop: BridgedLoop + + let count: Int + + var startIndex: Int { return 0 } + var endIndex: Int { return count } + + init(_ bridgedLoop: BridgedLoop) { + self.bridgedLoop = bridgedLoop + self.count = bridgedLoop.getInnerLoopCount() + } + + subscript(_ index: Int) -> Loop { + assert(index >= startIndex && index < endIndex) + return Loop(bridged: bridgedLoop.getInnerLoop(index)) + } +} + +struct LoopBlocks: BridgedRandomAccessCollection { + private let bridgedLoop: BridgedLoop + + let count: Int + + var startIndex: Int { return 0 } + var endIndex: Int { return count } + + init(_ bridgedLoop: BridgedLoop) { + self.bridgedLoop = bridgedLoop + self.count = bridgedLoop.getBasicBlockCount() + } + + subscript(_ index: Int) -> BasicBlock { + assert(index >= startIndex && index < endIndex) + return bridgedLoop.getBasicBlock(index).block + } +} + +func splitEdge( + from block: BasicBlock, + toEdgeIndex: Int, + dominatorTree: DominatorTree, + loopTree: LoopTree, + _ context: some MutatingContext +) -> BasicBlock { + let result = loopTree.bridged.splitEdge(block.bridged, toEdgeIndex, dominatorTree.bridged).block + + context.notifyBranchesChanged() + return result +} + +/// If the specified edge is critical, the function returns inserted block. Otherwise returns `nil`. +@discardableResult +func splitCriticalEdge( + from block: BasicBlock, + toEdgeIndex: Int, + dominatorTree: DominatorTree, + loopTree: LoopTree, + _ context: some MutatingContext +) -> BasicBlock? { + guard block.isCriticalEdge(edgeIndex: toEdgeIndex) else { + return nil + } + + return splitEdge(from: block, toEdgeIndex: toEdgeIndex, dominatorTree: dominatorTree, loopTree: loopTree, context) +} diff --git a/SwiftCompilerSources/Sources/Optimizer/DataStructures/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/DataStructures/CMakeLists.txt index 9cda577b95a84..f5e7afa317c82 100644 --- a/SwiftCompilerSources/Sources/Optimizer/DataStructures/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/DataStructures/CMakeLists.txt @@ -7,11 +7,6 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors swift_compiler_sources(Optimizer - BasicBlockRange.swift DeadEndBlocks.swift FunctionUses.swift - InstructionRange.swift - ReachableBlocks.swift - Set.swift - Stack.swift - Worklist.swift) + ReachableBlocks.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift b/SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift deleted file mode 100644 index 13fdedd4d18a6..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift +++ /dev/null @@ -1,183 +0,0 @@ -//===--- InstructionRange.swift - a range of instructions -----------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2022 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 SIL - -/// A range of instructions. -/// -/// The `InstructionRange` defines a range from a dominating "begin" instruction to one or more "end" instructions. -/// The range is "exclusive", which means that the "end" instructions are not part of the range. -/// -/// One or more "potential" end instructions can be inserted. -/// Though, not all inserted instructions end up as "end" instructions. -/// -/// `InstructionRange` is useful for calculating the liverange of values. -/// -/// The `InstructionRange` is similar to a `BasicBlockRange`, but defines the range -/// in a "finer" granularity, i.e. on instructions instead of blocks. -/// `InstructionRange` uses an underlying `BasicBlockRange` to compute the -/// involved blocks of the instruction range. -/// -/// There are several kind of instructions: -/// * begin: it is a single instruction which dominates all instructions of the range -/// * ends: all inserted instruction which are at the end of the range -/// * exits: the first instructions of the exit blocks -/// * interiors: all inserted instructions which are not end instructions. -/// -/// See also `BasicBlockRange` for more information. -/// -/// This type should be a move-only type, but unfortunately we don't have move-only -/// types yet. Therefore it's needed to call `deinitialize()` explicitly to -/// destruct this data structure, e.g. in a `defer {}` block. -struct InstructionRange : CustomStringConvertible, NoReflectionChildren { - - /// The underlying block range. - private(set) var blockRange: BasicBlockRange - - private var insertedInsts: InstructionSet - - // For efficiency, this set does not include instructions in blocks which are not the begin or any end block. - private var inExclusiveRange: InstructionSet - - init(begin beginInst: Instruction, _ context: some Context) { - self = InstructionRange(beginBlock: beginInst.parentBlock, context) - self.inExclusiveRange.insert(beginInst) - } - - // Note: 'ends' are simply the instructions to insert in the range. 'self.ends' might not return the same sequence - // as this 'ends' argument because 'self.ends' will not include block exits. - init(begin beginInst: Instruction, ends: S, _ context: some Context) where S.Element: Instruction { - self = InstructionRange(begin: beginInst, context) - insert(contentsOf: ends) - } - - init(for value: Value, _ context: some Context) { - if let inst = value.definingInstruction { - self = InstructionRange(begin: inst, context) - } else if let arg = value as? Argument { - self = InstructionRange(beginBlock: arg.parentBlock, context) - } else { - fatalError("cannot build an instruction range for \(value)") - } - } - - private init(beginBlock: BasicBlock, _ context: some Context) { - self.blockRange = BasicBlockRange(begin: beginBlock, context) - self.insertedInsts = InstructionSet(context) - self.inExclusiveRange = InstructionSet(context) - } - - /// Insert a potential end instruction. - mutating func insert(_ inst: Instruction) { - insertedInsts.insert(inst) - insertIntoRange(instructions: ReverseInstructionList(first: inst.previous)) - blockRange.insert(inst.parentBlock) - if inst.parentBlock != blockRange.begin { - // The first time an instruction is inserted in another block than the begin-block we need to insert - // instructions from the begin instruction to the end of the begin block. - // For subsequent insertions this is a no-op: `insertIntoRange` will return immediately because those - // instruction are already inserted. - insertIntoRange(instructions: blockRange.begin.instructions.reversed()) - } - } - - /// Insert a sequence of potential end instructions. - mutating func insert(contentsOf other: S) where S.Element: Instruction { - for inst in other { - insert(inst) - } - } - - /// Returns true if the exclusive range contains `inst`. - func contains(_ inst: Instruction) -> Bool { - if inExclusiveRange.contains(inst) { - return true - } - let block = inst.parentBlock - return block != blockRange.begin && blockRange.contains(block) - } - - /// Returns true if the inclusive range contains `inst`. - func inclusiveRangeContains (_ inst: Instruction) -> Bool { - contains(inst) || insertedInsts.contains(inst) - } - - /// Returns the end instructions. - /// - /// Warning: this returns `begin` if no instructions were inserted. - var ends: LazyMapSequence>, Instruction> { - blockRange.ends.map { - $0.instructions.reversed().first(where: { insertedInsts.contains($0)})! - } - } - - // Returns the exit blocks. - var exitBlocks: LazySequence>, - LazyFilterSequence>>> { - blockRange.exits - } - - /// Returns the exit instructions. - var exits: LazyMapSequence>, - LazyFilterSequence>>>, - Instruction> { - blockRange.exits.lazy.map { $0.instructions.first! } - } - - /// Returns the interior instructions. - var interiors: LazySequence, - LazyFilterSequence>>> { - blockRange.inserted.lazy.flatMap { - var include = blockRange.contains($0) - return $0.instructions.reversed().lazy.filter { - if insertedInsts.contains($0) { - let isInterior = include - include = true - return isInterior - } - return false - } - } - } - - var begin: Instruction? { - blockRange.begin.instructions.first(where: inExclusiveRange.contains) - } - - private mutating func insertIntoRange(instructions: ReverseInstructionList) { - for inst in instructions { - if !inExclusiveRange.insert(inst) { - return - } - } - } - - var description: String { - return (blockRange.isValid ? "" : "\n") + - """ - begin: \(begin?.description ?? blockRange.begin.name) - ends: \(ends.map { $0.description }.joined(separator: "\n ")) - exits: \(exits.map { $0.description }.joined(separator: "\n ")) - interiors:\(interiors.map { $0.description }.joined(separator: "\n ")) - """ - } - - /// TODO: once we have move-only types, make this a real deinit. - mutating func deinitialize() { - inExclusiveRange.deinitialize() - insertedInsts.deinitialize() - blockRange.deinitialize() - } -} diff --git a/SwiftCompilerSources/Sources/Optimizer/DataStructures/Set.swift b/SwiftCompilerSources/Sources/Optimizer/DataStructures/Set.swift deleted file mode 100644 index 2b95ad273cb4e..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/DataStructures/Set.swift +++ /dev/null @@ -1,245 +0,0 @@ -//===--- Set.swift - sets for basic blocks, values and instructions -------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2022 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 SIL -import OptimizerBridging - -protocol IntrusiveSet : CustomStringConvertible, NoReflectionChildren { - associatedtype Element - - init(_ context: some Context) - mutating func insert(_ element: Element) -> Bool - mutating func erase(_ element: Element) - func contains(_ element: Element) -> Bool - mutating func deinitialize() -} - -/// A set of basic blocks. -/// -/// This is an extremely efficient implementation which does not need memory -/// allocations or hash lookups. -/// -/// This type should be a move-only type, but unfortunately we don't have move-only -/// types yet. Therefore it's needed to call `deinitialize()` explicitly to -/// destruct this data structure, e.g. in a `defer {}` block. -struct BasicBlockSet : IntrusiveSet { - - private let context: BridgedPassContext - private let bridged: BridgedBasicBlockSet - - init(_ context: some Context) { - self.context = context._bridged - self.bridged = self.context.allocBasicBlockSet() - } - - func contains(_ block: BasicBlock) -> Bool { - bridged.contains(block.bridged) - } - - /// Returns true if `block` was not contained in the set before inserting. - @discardableResult - mutating func insert(_ block: BasicBlock) -> Bool { - bridged.insert(block.bridged) - } - - mutating func erase(_ block: BasicBlock) { - bridged.erase(block.bridged) - } - - var description: String { - let function = bridged.getFunction().function - let blockNames = function.blocks.enumerated().filter { contains($0.1) } - .map { "bb\($0.0)"} - return "{" + blockNames.joined(separator: ", ") + "}" - } - - /// TODO: once we have move-only types, make this a real deinit. - mutating func deinitialize() { - context.freeBasicBlockSet(bridged) - } -} - -/// A set of values. -/// -/// This is an extremely efficient implementation which does not need memory -/// allocations or hash lookups. -/// -/// This type should be a move-only type, but unfortunately we don't have move-only -/// types yet. Therefore it's needed to call `deinitialize()` explicitly to -/// destruct this data structure, e.g. in a `defer {}` block. -struct ValueSet : IntrusiveSet { - - private let context: BridgedPassContext - private let bridged: BridgedNodeSet - - init(_ context: some Context) { - self.context = context._bridged - self.bridged = self.context.allocNodeSet() - } - - func contains(_ value: Value) -> Bool { - bridged.containsValue(value.bridged) - } - - /// Returns true if `value` was not contained in the set before inserting. - @discardableResult - mutating func insert(_ value: Value) -> Bool { - bridged.insertValue(value.bridged) - } - - mutating func erase(_ value: Value) { - bridged.eraseValue(value.bridged) - } - - var description: String { - let function = bridged.getFunction().function - var d = "{\n" - for block in function.blocks { - for arg in block.arguments { - if contains(arg) { - d += arg.description - } - } - for inst in block.instructions { - for result in inst.results { - if contains(result) { - d += result.description - } - } - } - } - d += "}\n" - return d - } - - /// TODO: once we have move-only types, make this a real deinit. - mutating func deinitialize() { - context.freeNodeSet(bridged) - } -} - -/// A set of instructions. -/// -/// This is an extremely efficient implementation which does not need memory -/// allocations or hash lookups. -/// -/// This type should be a move-only type, but unfortunately we don't have move-only -/// types yet. Therefore it's needed to call `deinitialize()` explicitly to -/// destruct this data structure, e.g. in a `defer {}` block. -struct SpecificInstructionSet : IntrusiveSet { - - private let context: BridgedPassContext - private let bridged: BridgedNodeSet - - init(_ context: some Context) { - self.context = context._bridged - self.bridged = self.context.allocNodeSet() - } - - func contains(_ inst: InstType) -> Bool { - bridged.containsInstruction(inst.bridged) - } - - /// Returns true if `inst` was not contained in the set before inserting. - @discardableResult - mutating func insert(_ inst: InstType) -> Bool { - bridged.insertInstruction(inst.bridged) - } - - mutating func erase(_ inst: InstType) { - bridged.eraseInstruction(inst.bridged) - } - - var description: String { - let function = bridged.getFunction().function - var d = "{\n" - for i in function.instructions { - if let inst = i as? InstType, contains(inst) { - d += inst.description + "\n" - } - } - d += "}\n" - return d - } - - /// TODO: once we have move-only types, make this a real deinit. - mutating func deinitialize() { - context.freeNodeSet(bridged) - } -} - -typealias InstructionSet = SpecificInstructionSet - -/// A set of operands. -/// -/// This is an extremely efficient implementation which does not need memory -/// allocations or hash lookups. -/// -/// This type should be a move-only type, but unfortunately we don't have move-only -/// types yet. Therefore it's needed to call `deinitialize()` explicitly to -/// destruct this data structure, e.g. in a `defer {}` block. -struct OperandSet : IntrusiveSet { - - private let context: BridgedPassContext - private let bridged: BridgedOperandSet - - init(_ context: some Context) { - self.context = context._bridged - self.bridged = self.context.allocOperandSet() - } - - func contains(_ operand: Operand) -> Bool { - bridged.contains(operand.bridged) - } - - /// Returns true if `inst` was not contained in the set before inserting. - @discardableResult - mutating func insert(_ operand: Operand) -> Bool { - bridged.insert(operand.bridged) - } - - mutating func erase(_ operand: Operand) { - bridged.erase(operand.bridged) - } - - var description: String { - let function = bridged.getFunction().function - var d = "{\n" - for inst in function.instructions { - for op in inst.operands { - if contains(op) { - d += op.description - } - } - } - d += "}\n" - return d - } - - /// TODO: once we have move-only types, make this a real deinit. - mutating func deinitialize() { - context.freeOperandSet(bridged) - } -} - -extension IntrusiveSet { - mutating func insert(contentsOf source: some Sequence) { - for element in source { - _ = insert(element) - } - } - - init(insertContentsOf source: some Sequence, _ context: some Context) { - self.init(context) - insert(contentsOf: source) - } -} diff --git a/SwiftCompilerSources/Sources/Optimizer/DataStructures/Stack.swift b/SwiftCompilerSources/Sources/Optimizer/DataStructures/Stack.swift deleted file mode 100644 index c38e8da5404f5..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/DataStructures/Stack.swift +++ /dev/null @@ -1,222 +0,0 @@ -//===--- Stack.swift - defines the Stack data structure -------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2022 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 OptimizerBridging -import SIL - -/// A very efficient implementation of a stack, which can also be iterated over. -/// -/// A Stack is the best choice for things like worklists, etc., if no random -/// access is needed. -/// Compared to Array, it does not require any memory allocations, because it -/// uses a recycling bump pointer allocator for allocating the slabs. -/// All operations have (almost) zero cost. -/// -/// This type should be a move-only type, but unfortunately we don't have move-only -/// types yet. Therefore it's needed to call `deinitialize()` explicitly to -/// destruct this data structure, e.g. in a `defer {}` block. -struct Stack : CollectionLikeSequence { - - private let bridgedContext: BridgedPassContext - private var firstSlab = BridgedPassContext.Slab(nil) - private var lastSlab = BridgedPassContext.Slab(nil) - private var endIndex: Int = 0 - - private static var slabCapacity: Int { - BridgedPassContext.Slab.getCapacity() / MemoryLayout.stride - } - - private func allocate(after lastSlab: BridgedPassContext.Slab? = nil) -> BridgedPassContext.Slab { - let lastSlab = lastSlab ?? BridgedPassContext.Slab(nil) - let newSlab = bridgedContext.allocSlab(lastSlab) - UnsafeMutableRawPointer(newSlab.data!).bindMemory(to: Element.self, capacity: Stack.slabCapacity) - return newSlab - } - - private static func element(in slab: BridgedPassContext.Slab, at index: Int) -> Element { - return pointer(in: slab, at: index).pointee - } - - private static func pointer(in slab: BridgedPassContext.Slab, at index: Int) -> UnsafeMutablePointer { - return UnsafeMutableRawPointer(slab.data!).assumingMemoryBound(to: Element.self) + index - } - - struct Iterator : IteratorProtocol { - var slab: BridgedPassContext.Slab - var index: Int - let lastSlab: BridgedPassContext.Slab - let endIndex: Int - - mutating func next() -> Element? { - let end = (slab.data == lastSlab.data ? endIndex : slabCapacity) - - guard index < end else { return nil } - - let elem = Stack.element(in: slab, at: index) - index += 1 - - if index >= end && slab.data != lastSlab.data { - slab = slab.getNext() - index = 0 - } - return elem - } - } - - init(_ context: some Context) { self.bridgedContext = context._bridged } - - func makeIterator() -> Iterator { - return Iterator(slab: firstSlab, index: 0, lastSlab: lastSlab, endIndex: endIndex) - } - - var first: Element? { - isEmpty ? nil : Stack.element(in: firstSlab, at: 0) - } - - var last: Element? { - isEmpty ? nil : Stack.element(in: lastSlab, at: endIndex &- 1) - } - - mutating func push(_ element: Element) { - if endIndex >= Stack.slabCapacity { - lastSlab = allocate(after: lastSlab) - endIndex = 0 - } else if firstSlab.data == nil { - assert(endIndex == 0) - firstSlab = allocate() - lastSlab = firstSlab - } - Stack.pointer(in: lastSlab, at: endIndex).initialize(to: element) - endIndex += 1 - } - - /// The same as `push` to provide an Array-like append API. - mutating func append(_ element: Element) { push(element) } - - mutating func append(contentsOf other: S) where S.Element == Element { - for elem in other { - append(elem) - } - } - - var isEmpty: Bool { return endIndex == 0 } - - mutating func pop() -> Element? { - if isEmpty { - return nil - } - assert(endIndex > 0) - endIndex -= 1 - let elem = Stack.pointer(in: lastSlab, at: endIndex).move() - - if endIndex == 0 { - if lastSlab.data == firstSlab.data { - _ = bridgedContext.freeSlab(lastSlab) - firstSlab.data = nil - lastSlab.data = nil - endIndex = 0 - } else { - lastSlab = bridgedContext.freeSlab(lastSlab) - endIndex = Stack.slabCapacity - } - } - - return elem - } - - mutating func removeAll() { - while pop() != nil { } - } - - /// TODO: once we have move-only types, make this a real deinit. - mutating func deinitialize() { removeAll() } -} - -extension Stack { - /// Mark a stack location for future iteration. - /// - /// TODO: Marker should be ~Escapable. - struct Marker { - let slab: BridgedPassContext.Slab - let index: Int - } - - var top: Marker { Marker(slab: lastSlab, index: endIndex) } - - struct Segment : CollectionLikeSequence { - let low: Marker - let high: Marker - - init(in stack: Stack, low: Marker, high: Marker) { - if low.slab.data == nil { - assert(low.index == 0, "invalid empty stack marker") - // `low == nil` and `high == nil` is a valid empty segment, - // even though `assertValid(marker:)` would return false. - if high.slab.data != nil { - stack.assertValid(marker: high) - } - self.low = Marker(slab: stack.firstSlab, index: 0) - self.high = high - return - } - stack.assertValid(marker: low) - stack.assertValid(marker: high) - self.low = low - self.high = high - } - - func makeIterator() -> Stack.Iterator { - return Iterator(slab: low.slab, index: low.index, - lastSlab: high.slab, endIndex: high.index) - } - } - - /// Assert that `marker` is valid based on the current `top`. - /// - /// This is an assert rather than a query because slabs can reuse - /// memory leading to a stale marker that appears valid. - func assertValid(marker: Marker) { - var currentSlab = lastSlab - var currentIndex = endIndex - while currentSlab.data != marker.slab.data { - assert(currentSlab.data != firstSlab.data, "Invalid stack marker") - currentSlab = currentSlab.getPrevious() - currentIndex = Stack.slabCapacity - } - assert(marker.index <= currentIndex, "Invalid stack marker") - } - - /// Execute the `body` closure, passing it `self` for further - /// mutation of the stack and passing `marker` to mark the stack - /// position prior to executing `body`. `marker` must not escape the - /// `body` closure. - mutating func withMarker( - _ body: (inout Stack, Marker) throws -> R) rethrows -> R { - return try body(&self, top) - } - - /// Record a stack marker, execute a `body` closure, then execute a - /// `handleNewElements` closure with the Segment that contains all - /// elements that remain on the stack after being pushed on the - /// stack while executing `body`. `body` must push more elements - /// than it pops. - mutating func withMarker( - pushElements body: (inout Stack) throws -> R, - withNewElements handleNewElements: ((Segment) -> ()) - ) rethrows -> R { - return try withMarker { (stack: inout Stack, marker: Marker) in - let result = try body(&stack) - handleNewElements(Segment(in: stack, low: marker, high: stack.top)) - return result - } - } -} diff --git a/SwiftCompilerSources/Sources/Optimizer/DataStructures/Worklist.swift b/SwiftCompilerSources/Sources/Optimizer/DataStructures/Worklist.swift deleted file mode 100644 index ec1336b064d3d..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/DataStructures/Worklist.swift +++ /dev/null @@ -1,97 +0,0 @@ -//===--- Worklist.swift ---------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 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 SIL - -/// A utility for processing entities in a worklist. -/// -/// A `Worklist` is basically a combination of a stack and a set. -/// It can be used for typical worklist-processing algorithms. -/// -/// This type should be a move-only type, but unfortunately we don't have move-only -/// types yet. Therefore it's needed to call `deinitialize()` explicitly to -/// destruct this data structure, e.g. in a `defer {}` block. -struct Worklist : CustomStringConvertible, NoReflectionChildren { - typealias Element = Set.Element - private var worklist: Stack - private var pushedElements: Set - - init(_ context: some Context) { - self.worklist = Stack(context) - self.pushedElements = Set(context) - } - - mutating func pop() -> Element? { return worklist.pop() } - - /// Pop and allow the popped element to be pushed again to the worklist. - mutating func popAndForget() -> Element? { - if let element = worklist.pop() { - pushedElements.erase(element) - return element - } - return nil - } - - mutating func pushIfNotVisited(_ element: Element) { - if pushedElements.insert(element) { - worklist.append(element) - } - } - - mutating func pushIfNotVisited(contentsOf other: S) where S.Element == Element { - for element in other { - pushIfNotVisited(element) - } - } - - /// Returns true if \p element was pushed to the worklist, regardless if it's already popped or not. - func hasBeenPushed(_ element: Element) -> Bool { pushedElements.contains(element) } - - var isEmpty: Bool { worklist.isEmpty } - - var description: String { - """ - worklist: \(worklist) - pushed: \(pushedElements) - """ - } - - /// TODO: once we have move-only types, make this a real deinit. - mutating func deinitialize() { - pushedElements.deinitialize() - worklist.deinitialize() - } -} - -typealias BasicBlockWorklist = Worklist -typealias InstructionWorklist = Worklist -typealias SpecificInstructionWorklist = Worklist> -typealias ValueWorklist = Worklist -typealias OperandWorklist = Worklist - -extension InstructionWorklist { - mutating func pushPredecessors(of inst: Instruction, ignoring ignoreInst: Instruction) { - if let prev = inst.previous { - if prev != ignoreInst { - pushIfNotVisited(prev) - } - } else { - for predBlock in inst.parentBlock.predecessors { - let termInst = predBlock.terminator - if termInst != ignoreInst { - pushIfNotVisited(termInst) - } - } - } - } -} - diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift new file mode 100644 index 0000000000000..6956c1078ec8d --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift @@ -0,0 +1,516 @@ +//===--- AllocBoxToStack.swift --------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL + +/// Replaces `alloc_box` with `alloc_stack` if the box is not escaping. +/// +/// ``` +/// %1 = alloc_box ${ var T } +/// %2 = project_box %1, 0 +/// ... +/// store %3 to %2 // uses of the box field +/// ... +/// destroy_value %1 // end of lifetime of the box +/// ``` +/// -> +/// ``` +/// %2 = alloc_stack $T +/// ... +/// store %3 to %2 // uses of the stack location +/// ... +/// destroy_addr %2 // end of lifetime +/// dealloc_stack %2 +/// ``` +/// +/// This transformation also works inter-procedurally. If the box is passed to a callee, +/// the callee is specialized: instead of the box argument the stack address is passed +/// as `@inout_aliasable` parameter: +/// +/// ``` +/// sil @closure : $(@guaranteed var { T }) -> () { +/// bb0(%0 : @guaranteed ${ var T }): +/// %1 = project_box %0 +/// %2 = load %1 +/// ... +/// ``` +/// -> +/// ``` +/// sil @specialized_closure : $(@inout_aliasable T) -> () { +/// bb0(%0 : $*T): +/// %2 = load %0 +/// ... +/// ``` +/// +let allocBoxToStack = FunctionPass(name: "allocbox-to-stack") { + (function: Function, context: FunctionPassContext) in + + _ = tryConvertBoxesToStack(in: function, isMandatory: false, context) +} + +/// The "mandatory" version of the pass, which runs in the mandatory pipeline. +/// In contrast to the regular version, it is a module pass because it deletes the originals +/// of specialized closures, so that successive diagnostic passes don't report errors for +/// the unspecialized closures. +/// +let mandatoryAllocBoxToStack = ModulePass(name: "mandatory-allocbox-to-stack") { + (moduleContext: ModulePassContext) in + + var worklist = FunctionWorklist() + worklist.pushIfNotVisited(contentsOf: moduleContext.functions) + + var originalsOfSpecializedFunctions = FunctionWorklist() + + while let function = worklist.pop() { + moduleContext.transform(function: function) { context in + let specFns = tryConvertBoxesToStack(in: function, isMandatory: true, context) + worklist.pushIfNotVisited(contentsOf: specFns.specializedFunctions) + originalsOfSpecializedFunctions.pushIfNotVisited(contentsOf: specFns.originalFunctions) + } + } + + /// This is needed because box-promotion is mandatory for non-copyable types. Without removing the + /// original, un-specialized versions of closures we risk getting false errors in diagnostic passes. + eraseIfDead(functions: originalsOfSpecializedFunctions.functions, moduleContext) +} + +/// Converts all non-escaping `alloc_box` to `alloc_stack` and specializes called functions if a +/// box is passed to a function. +/// Returns the list of original functions for which a specialization has been created. +private func tryConvertBoxesToStack(in function: Function, isMandatory: Bool, + _ context: FunctionPassContext +) -> FunctionSpecializations { + var promotableBoxes = Array<(AllocBoxInst, Flags)>() + var functionsToSpecialize = FunctionSpecializations(isMandatory: isMandatory) + + findPromotableBoxes(in: function, &promotableBoxes, &functionsToSpecialize) + + functionsToSpecialize.createSpecializedFunctions(context) + + for (box, flags) in promotableBoxes { + let stack = createAllocStack(for: box, flags: flags, context) + functionsToSpecialize.rewriteUses(of: box, with: stack, context) + context.erase(instruction: box) + + hoistMarkUnresolvedInsts(stackAddress: stack, checkKind: .consumableAndAssignable, context) + } + if !promotableBoxes.isEmpty { + context.fixStackNesting(in: function) + } + + return functionsToSpecialize +} + +private func findPromotableBoxes(in function: Function, + _ promotableBoxes: inout Array<(AllocBoxInst, Flags)>, + _ functionsToSpecialize: inout FunctionSpecializations +) { + for inst in function.instructions { + if let allocBox = inst as? AllocBoxInst { + if let (promotableArgs, flags) = canPromote(allocBox: allocBox) { + promotableBoxes.append((allocBox, flags)) + functionsToSpecialize.add(promotableArguments: promotableArgs) + } + } + } +} + +private typealias Flags = (isLexical: Bool, isFromVarDecl: Bool) + +private func canPromote(allocBox: AllocBoxInst) -> (promotableArguments: [FunctionArgument], flags: Flags)? { + // For simplicity we only support boxes with a single field. This is okay because SILGen only generates + // such kind of boxes. + guard allocBox.type.getBoxFields(in: allocBox.parentFunction).count == 1 else { + return nil + } + + var argumentsToPromote = Array() + var flags = (isLexical: false, isFromVarDecl: false) + + // Contains all visited box _and_ closure values (`partial_apply`) of the current function and + // all callees, which need to be specialized. + var worklist = CrossFunctionValueWorklist() + worklist.pushIfNotVisited(allocBox) + + while let value = worklist.pop() { + for use in value.uses { + // Note: all instructions which are handled here must also be handled in `FunctionSpecializations.rewriteUses`! + switch use.instruction { + case is StrongRetainInst, is StrongReleaseInst, is ProjectBoxInst, is DestroyValueInst, + is EndBorrowInst, is DebugValueInst, is DeallocStackInst: + break + case let deallocBox as DeallocBoxInst where deallocBox.parentFunction == allocBox.parentFunction: + break + case let beginBorrow as BeginBorrowInst: + flags.isLexical = flags.isLexical || beginBorrow.isLexical + flags.isFromVarDecl = flags.isFromVarDecl || beginBorrow.isFromVarDecl + fallthrough + case is MarkUninitializedInst, is CopyValueInst, is MoveValueInst: + worklist.pushIfNotVisited(use.instruction as! SingleValueInstruction) + case let apply as ApplySite: + if apply.isCallee(operand: use) { + // Calling the closure does not escape the closure value. + break + } + guard let callee = apply.getSpecializableCallee() else { + return nil + } + let calleeArg = apply.calleeArgument(of: use, in: callee)! + // Continue checking the box (or closure) value in the callee. + worklist.pushIfNotVisited(calleeArg) + + if value.type.isBox { + // We need to specialize this function by replacing the box argument with an address. + argumentsToPromote.append(calleeArg) + } + if let partialApply = apply as? PartialApplyInst { + // We need to check if the captured argument is escaping via the partial_apply. + worklist.pushIfNotVisited(partialApply) + } + default: + return nil + } + } + } + return (argumentsToPromote, flags) +} + +/// Utility for specializing functions by promoting box arguments to `@inout_aliasable` address arguments. +private struct FunctionSpecializations { + + // All box arguments (in all functions) which are promoted from box to address. + private var promotableArguments = CrossFunctionValueWorklist() + private var originals = FunctionWorklist() + private var originalToSpecialized = Dictionary() + private let isMandatory: Bool + + init(isMandatory: Bool) { self.isMandatory = isMandatory } + + var originalFunctions: [Function] { originals.functions } + var specializedFunctions: [Function] { originals.functions.lazy.map { originalToSpecialized[$0]! } } + + mutating func add(promotableArguments: [FunctionArgument]) { + for arg in promotableArguments { + self.promotableArguments.pushIfNotVisited(arg) + self.originals.pushIfNotVisited(arg.parentFunction) + } + } + + mutating func createSpecializedFunctions(_ context: FunctionPassContext) { + // It's important to first create _all_ declarations before creating the function bodies, because + // a function body may reference another specialized declaration - in any order. + for f in originals.functions { + originalToSpecialized[f] = createSpecializedDeclaration(for: f, context) + } + for f in originals.functions { + createSpecializedBody(for: f, context) + } + } + + /// Rewrites all uses of `box` with the `stack` address where `box` is either an `alloc_box` in + /// the original function or a promoted box argument in a specialized function. + func rewriteUses(of box: Value, with stack: Value, _ context: FunctionPassContext) { + while let use = box.uses.first { + let user = use.instruction + switch user { + case is StrongRetainInst, is StrongReleaseInst, is DestroyValueInst, is EndBorrowInst, is DeallocBoxInst: + context.erase(instruction: user) + case let projectBox as ProjectBoxInst: + assert(projectBox.fieldIndex == 0, "only single-field boxes are handled") + if isMandatory { + // Once we have promoted the box to stack, access violations can be detected statically by the + // DiagnoseStaticExclusivity pass (which runs after MandatoryAllocBoxToStack). + // Therefore we can convert dynamic accesses to static accesses. + makeAccessesStatic(of: projectBox, context) + } + projectBox.replace(with: stack, context) + case is MarkUninitializedInst, is CopyValueInst, is BeginBorrowInst, is MoveValueInst: + // First, replace the instruction with the original `box`, which adds more uses to `box`. + // In a later iteration those additional uses will be handled. + (user as! SingleValueInstruction).replace(with: box, context) + case let apply as ApplySite: + specialize(apply: apply, context) + default: + fatalError("unhandled box user") + } + } + } + + /// Replaces `apply` with a new apply of the specialized callee. + private func specialize(apply: ApplySite, _ context: FunctionPassContext) { + let fri = apply.callee as! FunctionRefInst + let callee = fri.referencedFunction + let builder = Builder(before: apply, context) + let newArgs = apply.argumentOperands.map { (argOp) -> Value in + if promotableArguments.hasBeenPushed(apply.calleeArgument(of: argOp, in: callee)!) { + return builder.createProjectBox(box: argOp.value, fieldIndex: 0) + } else { + return argOp.value + } + } + let specializedCallee = builder.createFunctionRef(originalToSpecialized[callee]!) + + switch apply { + case let applyInst as ApplyInst: + let newApply = builder.createApply(function: specializedCallee, applyInst.substitutionMap, arguments: newArgs, isNonThrowing: applyInst.isNonThrowing) + applyInst.replace(with: newApply, context) + case let partialAp as PartialApplyInst: + let newApply = builder.createPartialApply(function: specializedCallee, substitutionMap: + partialAp.substitutionMap, + capturedArguments: newArgs, + calleeConvention: partialAp.calleeConvention, + hasUnknownResultIsolation: partialAp.hasUnknownResultIsolation, + isOnStack: partialAp.isOnStack) + partialAp.replace(with: newApply, context) + case let tryApply as TryApplyInst: + builder.createTryApply(function: specializedCallee, tryApply.substitutionMap, arguments: newArgs, + normalBlock: tryApply.normalBlock, errorBlock: tryApply.errorBlock) + context.erase(instruction: tryApply) + case let beginApply as BeginApplyInst: + let newApply = builder.createBeginApply(function: specializedCallee, beginApply.substitutionMap, + arguments: newArgs) + beginApply.replace(with: newApply, context) + default: + fatalError("unknown apply") + } + // It is important to delete the dead `function_ref`. Otherwise it will still reference the original + // function which prevents deleting it in the mandatory-allocbox-to-stack pass. + if fri.uses.isEmpty { + context.erase(instruction: fri) + } + } + + private func createSpecializedDeclaration(for function: Function, _ context: FunctionPassContext) -> Function + { + let argIndices = function.arguments.enumerated().filter { + promotableArguments.hasBeenPushed($0.element) + }.map { $0.offset } + let name = context.mangle(withBoxToStackPromotedArguments: argIndices, from: function) + + if let existingSpecialization = context.lookupFunction(name: name) { + // This can happen if a previous run of the pass already created this specialization. + return existingSpecialization + } + + let params = function.convention.parameters.enumerated().map { (paramIdx, param) in + let arg = function.arguments[function.convention.indirectSILResultCount + paramIdx] + if promotableArguments.hasBeenPushed(arg) { + return ParameterInfo(type: param.type.getBoxFields(in: function).singleElement!.canonicalType, + convention: .indirectInoutAliasable, + options: 0, + hasLoweredAddresses: param.hasLoweredAddresses) + } else { + return param + } + } + return context.createSpecializedFunctionDeclaration(from: function, withName: name, withParams: params) + } + + private func createSpecializedBody(for original: Function, _ context: FunctionPassContext) + { + let specializedFunc = originalToSpecialized[original]! + if specializedFunc.isDefinition { + // This can happen if a previous run of the pass already created this specialization. + return + } + context.buildSpecializedFunction(specializedFunction: specializedFunc) { (specializedFunc, specContext) in + cloneFunction(from: original, toEmpty: specializedFunc, specContext) + + replaceBoxWithStackArguments(in: specializedFunc, original: original, specContext) + } + context.notifyNewFunction(function: specializedFunc, derivedFrom: original) + } + + private func replaceBoxWithStackArguments(in specializedFunc: Function, original: Function, + _ context: FunctionPassContext + ) { + for (argIdx, (origBoxArg, boxArg)) in zip(original.arguments, specializedFunc.arguments).enumerated() { + if promotableArguments.hasBeenPushed(origBoxArg) { + let boxFields = boxArg.type.getBoxFields(in: specializedFunc) + let stackArg = specializedFunc.entryBlock.insertFunctionArgument( + atPosition: argIdx, type: boxFields[0], ownership: .none, decl: boxArg.decl, context) + stackArg.copyFlags(from: boxArg, context) + + rewriteUses(of: boxArg, with: stackArg, context) + specializedFunc.entryBlock.eraseArgument(at: argIdx + 1, context) + + hoistMarkUnresolvedInsts( + stackAddress: stackArg, + checkKind: boxFields.isMutable(fieldIndex: 0) ? .consumableAndAssignable : .noConsumeOrAssign, + context) + } + } + } +} + +/// Replaces an `alloc_box` with an `alloc_stack` and inserts `destroy_addr` and `dealloc_stack` +/// at the end of the lifetime. +private func createAllocStack(for allocBox: AllocBoxInst, flags: Flags, _ context: FunctionPassContext) -> Value { + let builder = Builder(before: allocBox, context) + let unboxedType = allocBox.type.getBoxFields(in: allocBox.parentFunction)[0] + let asi = builder.createAllocStack(unboxedType, + debugVariable: allocBox.debugVariable, + hasDynamicLifetime: allocBox.hasDynamicLifetime, + isLexical: flags.isLexical, + isFromVarDecl: flags.isFromVarDecl) + let stackLocation: Value + if let mu = allocBox.uses.getSingleUser(ofType: MarkUninitializedInst.self) { + stackLocation = builder.createMarkUninitialized(value: asi, kind: mu.kind) + } else { + stackLocation = asi + } + + for destroy in getFinalDestroys(of: allocBox, context) { + let loc = allocBox.location.asCleanup.withScope(of: destroy.location) + Builder.insert(after: destroy, location: loc, context) { builder in + if !(destroy is DeallocBoxInst), + context.deadEndBlocks.isDeadEnd(destroy.parentBlock), + !isInLoop(block: destroy.parentBlock, context) { + // "Last" releases in dead-end regions may not actually destroy the box + // and consequently may not actually release the stored value. That's + // because values (including boxes) may be leaked along paths into + // dead-end regions. Thus it is invalid to lower such final releases of + // the box to destroy_addr's/dealloc_box's of the stack-promoted storage. + // + // There is one exception: if the alloc_box is in a dead-end loop. In + // that case SIL invariants require that the final releases actually + // destroy the box; otherwise, a box would leak once per loop. To check + // for this, it is sufficient check that the LastRelease is in a dead-end + // loop: if the alloc_box is not in that loop, then the entire loop is in + // the live range, so no release within the loop would be a "final + // release". + // + // None of this applies to dealloc_box instructions which always destroy + // the box. + return + } + if !unboxedType.isTrivial(in: allocBox.parentFunction), !(destroy is DeallocBoxInst) { + builder.createDestroyAddr(address: stackLocation) + } + if let dbi = destroy as? DeallocBoxInst, dbi.isDeadEnd { + // Don't bother to create dealloc_stack instructions in dead-ends. + return + } + builder.createDeallocStack(asi) + } + } + return stackLocation +} + +/// Returns the list of final destroy instructions of `allocBox`. +/// In case the box is copied, not all `destroy_value`s are final destroys, e.g. +/// ``` +/// %1 = alloc_box ${ var T } +/// %2 = copy_value %1 +/// destroy_value %1 // not a final destroy +/// destroy_value %2 // a final destroy +/// ``` +private func getFinalDestroys(of allocBox: AllocBoxInst, _ context: FunctionPassContext) -> [Instruction] { + var liverange = InstructionRange(for: allocBox, context) + defer { liverange.deinitialize() } + + var worklist = ValueWorklist(context) + defer { worklist.deinitialize() } + worklist.pushIfNotVisited(allocBox) + + var destroys = Stack(context) + defer { destroys.deinitialize() } + + while let value = worklist.pop() { + for use in value.uses { + let user = use.instruction + liverange.insert(user) + switch user { + case is MarkUninitializedInst, is CopyValueInst, is MoveValueInst, is PartialApplyInst, is BeginBorrowInst: + worklist.pushIfNotVisited(user as! SingleValueInstruction) + case is StrongReleaseInst, is DestroyValueInst, is DeallocBoxInst: + destroys.push(user) + case let apply as FullApplySite: + if apply.convention(of: use) == .directOwned { + destroys.push(user) + } + default: + break + } + } + } + return destroys.filter { !liverange.contains($0) } +} + +/// Hoists `mark_unresolved_non_copyable_value` instructions from inside the def-use chain of `stackAddress` +/// right after the `stackAddress`. +/// ``` +/// %1 = alloc_stack $T %1 = alloc_stack $T +/// %2 = begin_access [read] %1 --> %2 = mark_unresolved_non_copyable_value %1 +/// %3 = mark_unresolved_non_copyable_value %2 %3 = begin_access [read] %2 +/// ``` +private func hoistMarkUnresolvedInsts(stackAddress: Value, + checkKind: MarkUnresolvedNonCopyableValueInst.CheckKind, + _ context: FunctionPassContext +) { + var worklist = ValueWorklist(context) + defer { worklist.deinitialize() } + worklist.pushIfNotVisited(stackAddress) + var foundMarkUninit = false + while let addr = worklist.pop() { + for use in addr.uses { + switch use.instruction { + case is BeginAccessInst, is MarkUninitializedInst: + worklist.pushIfNotVisited(use.instruction as! SingleValueInstruction) + case let mu as MarkUnresolvedNonCopyableValueInst: + mu.replace(with: mu.operand.value, context) + foundMarkUninit = true + default: + break + } + } + } + guard foundMarkUninit else { + return + } + let builder: Builder + if let inst = stackAddress as? SingleValueInstruction { + builder = Builder(after: inst, context) + } else { + builder = Builder(atBeginOf: stackAddress.parentBlock, context) + } + let mu = builder.createMarkUnresolvedNonCopyableValue(value: stackAddress, checkKind: checkKind, isStrict: false) + stackAddress.uses.ignore(user: mu).ignoreDebugUses.ignoreUses(ofType: DeallocStackInst.self) + .replaceAll(with: mu, context) +} + +private func makeAccessesStatic(of address: Value, _ context: FunctionPassContext) { + for beginAccess in address.uses.users(ofType: BeginAccessInst.self) { + if beginAccess.enforcement == .dynamic { + beginAccess.set(enforcement: .static, context: context) + } + } +} + +private extension ApplySite { + func getSpecializableCallee() -> Function? { + if let callee = referencedFunction, + callee.isDefinition, + callee.canBeInlinedIntoCaller(withSerializedKind: parentFunction.serializedKind) + { + if self is FullApplySite, + // If the function is inlined later, there is no point in specializing it. + !callee.shouldOptimize || callee.inlineStrategy == .heuristicAlways || + callee.inlineStrategy == .always + { + return nil + } + return callee + } + return nil + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt index 66aae9c7ad84c..54962dfbcf2cc 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt @@ -7,9 +7,11 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors swift_compiler_sources(Optimizer + AllocBoxToStack.swift AssumeSingleThreaded.swift AsyncDemotion.swift BooleanLiteralFolding.swift + ConstantCapturePropagation.swift CleanupDebugSteps.swift ClosureSpecialization.swift ComputeEscapeEffects.swift @@ -24,6 +26,7 @@ swift_compiler_sources(Optimizer LifetimeDependenceDiagnostics.swift LifetimeDependenceInsertion.swift LifetimeDependenceScopeFixup.swift + LoopInvariantCodeMotion.swift ObjectOutliner.swift ObjCBridgingOptimization.swift MergeCondFails.swift @@ -33,4 +36,6 @@ swift_compiler_sources(Optimizer SimplificationPasses.swift StackPromotion.swift StripObjectHeaders.swift + TempLValueElimination.swift + TempRValueElimination.swift ) diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift index ae4a34a852455..b8dbafcb12afa 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift @@ -10,58 +10,106 @@ // //===-----------------------------------------------------------------------===// -/// This file contains the closure-specialization optimizations for general and differentiable Swift. +import AST +import SIL -/// General Closure Specialization -/// ------------------------------------ -/// TODO: Add description when the functionality is added. +/// Closure Specialization +/// ---------------------- +/// Specializes functions which take a closure (a `partial_apply` or `thin_to_thick_function` as argument. +/// The closure is created directly in the specialized function whereas the captured arguments are passed +/// as additional arguments to the specialized function. As a heuristic, this is only done if the closure +/// is actually called in the function because after specialization, the closure and its call can be +/// further optimized, e.g. the closure can be inlined. +/// +/// ``` +/// %3 = function_ref @closure +/// %4 = partial_apply %3(%1, %2) : (Float, Int, Bool) -> () +/// %5 = function_ref @closure_user +/// apply %5(%4) +/// ... +/// +/// sil @closure_user : ((Float) -> (), Float) -> () { +/// bb0(%0 : $(Float) -> (), %1 : $Float): +/// apply %0(%1) +/// ... +/// ``` +/// -> +/// ``` +/// %5 = function_ref @specialized_closure_user +/// apply %5(%1, %2) +/// ... +/// +/// sil @specialized_closure_user : (Float, Int, Bool) -> () { +/// bb0(%0 : $Float, %1 : $Int, %2 : $Bool): +/// %3 = function_ref @closure +/// %4 = partial_apply %3(%1, %2) +/// apply %3(%0) +/// ... +/// ``` +/// +let closureSpecialization = FunctionPass(name: "closure-specialization") { + (function: Function, context: FunctionPassContext) in + + guard function.hasOwnership else { + return + } + + for inst in function.instructions { + if let apply = inst as? FullApplySite { + _ = trySpecialize(apply: apply, context) + } + } + if context.needFixStackNesting { + context.fixStackNesting(in: function) + } +} /// AutoDiff Closure Specialization /// ------------------------------- /// This optimization performs closure specialization tailored for the patterns seen in Swift Autodiff. In principle, -/// the optimization does the same thing as the existing closure specialization pass. However, it is tailored to the +/// the optimization does the same thing as the general closure specialization pass. However, it is tailored to the /// patterns of Swift Autodiff. /// /// The compiler performs reverse-mode differentiation on functions marked with `@differentiable(reverse)`. In doing so, /// it generates corresponding VJP and Pullback functions, which perform the forward and reverse pass respectively. You /// can think of VJPs as functions that "differentiate" an original function and Pullbacks as the calculated -/// "derivative" of the original function. -/// -/// VJPs always return a tuple of 2 values -- the original result and the Pullback. Pullbacks are essentially a chain +/// "derivative" of the original function. +/// +/// VJPs always return a tuple of 2 values -- the original result and the Pullback. Pullbacks are essentially a chain /// of closures, where the closure-contexts are implicitly used as the so-called "tape" during the reverse /// differentiation process. It is this chain of closures contained within the Pullbacks that this optimization aims /// to optimize via closure specialization. /// /// The code patterns that this optimization targets, look similar to the one below: /// ``` swift -/// +/// /// // Since `foo` is marked with the `differentiable(reverse)` attribute the compiler /// // will generate corresponding VJP and Pullback functions in SIL. Let's assume that /// // these functions are called `vjp_foo` and `pb_foo` respectively. -/// @differentiable(reverse) -/// func foo(_ x: Float) -> Float { +/// @differentiable(reverse) +/// func foo(_ x: Float) -> Float { /// return sin(x) /// } /// -/// //============== Before closure specialization ==============// +/// //============== Before closure specialization ==============// /// // VJP of `foo`. Returns the original result and the Pullback of `foo`. -/// sil @vjp_foo: $(Float) -> (originalResult: Float, pullback: (Float) -> Float) { -/// bb0(%0: $Float): -/// // __Inlined__ `vjp_sin`: It is important for all intermediate VJPs to have +/// sil @vjp_foo: $(Float) -> (originalResult: Float, pullback: (Float) -> Float) { +/// bb0(%0: $Float): +/// // __Inlined__ `vjp_sin`: It is important for all intermediate VJPs to have /// // been inlined in `vjp_foo`, otherwise `vjp_foo` will not be able to determine /// // that `pb_foo` is closing over other closures and no specialization will happen. -/// \ +/// \ /// %originalResult = apply @sin(%0): $(Float) -> Float \__ Inlined `vjp_sin` /// %partially_applied_pb_sin = partial_apply pb_sin(%0): $(Float) -> Float / -/// / +/// / /// /// %pb_foo = function_ref @pb_foo: $@convention(thin) (Float, (Float) -> Float) -> Float /// %partially_applied_pb_foo = partial_apply %pb_foo(%partially_applied_pb_sin): $(Float, (Float) -> Float) -> Float -/// +/// /// return (%originalResult, %partially_applied_pb_foo) /// } /// -/// // Pullback of `foo`. +/// // Pullback of `foo`. /// // /// // It receives what are called as intermediate closures that represent /// // the calculations that the Pullback needs to perform to calculate a function's @@ -70,1040 +118,629 @@ /// // The intermediate closures may themselves contain intermediate closures and /// // that is why the Pullback for a function differentiated at the "top" level /// // may end up being a "chain" of closures. -/// sil @pb_foo: $(Float, (Float) -> Float) -> Float { -/// bb0(%0: $Float, %pb_sin: $(Float) -> Float): -/// %derivative_of_sin = apply %pb_sin(%0): $(Float) -> Float +/// sil @pb_foo: $(Float, (Float) -> Float) -> Float { +/// bb0(%0: $Float, %pb_sin: $(Float) -> Float): +/// %derivative_of_sin = apply %pb_sin(%0): $(Float) -> Float /// return %derivative_of_sin: Float /// } /// -/// //============== After closure specialization ==============// -/// sil @vjp_foo: $(Float) -> (originalResult: Float, pullback: (Float) -> Float) { -/// bb0(%0: $Float): -/// %originalResult = apply @sin(%0): $(Float) -> Float -/// +/// //============== After closure specialization ==============// +/// sil @vjp_foo: $(Float) -> (originalResult: Float, pullback: (Float) -> Float) { +/// bb0(%0: $Float): +/// %originalResult = apply @sin(%0): $(Float) -> Float +/// /// // Before the optimization, pullback of `foo` used to take a closure for computing /// // pullback of `sin`. Now, the specialized pullback of `foo` takes the arguments that /// // pullback of `sin` used to close over and pullback of `sin` is instead copied over /// // inside pullback of `foo`. /// %specialized_pb_foo = function_ref @specialized_pb_foo: $@convention(thin) (Float, Float) -> Float -/// %partially_applied_pb_foo = partial_apply %specialized_pb_foo(%0): $(Float, Float) -> Float -/// +/// %partially_applied_pb_foo = partial_apply %specialized_pb_foo(%0): $(Float, Float) -> Float +/// /// return (%originalResult, %partially_applied_pb_foo) /// } -/// -/// sil @specialized_pb_foo: $(Float, Float) -> Float { -/// bb0(%0: $Float, %1: $Float): -/// %2 = partial_apply @pb_sin(%1): $(Float) -> Float -/// %3 = apply %2(): $() -> Float +/// +/// sil @specialized_pb_foo: $(Float, Float) -> Float { +/// bb0(%0: $Float, %1: $Float): +/// %2 = partial_apply @pb_sin(%1): $(Float) -> Float +/// %3 = apply %2(): $() -> Float /// return %3: $Float /// } /// ``` - -import AST -import SIL -import SILBridging - -private let verbose = false - -private func log(_ message: @autoclosure () -> String) { - if verbose { - print("### \(message())") - } -} - -// =========== Entry point =========== // -let generalClosureSpecialization = FunctionPass(name: "experimental-swift-based-closure-specialization") { - (function: Function, context: FunctionPassContext) in - // TODO: Implement general closure specialization optimization - print("NOT IMPLEMENTED") -} - +/// let autodiffClosureSpecialization = FunctionPass(name: "autodiff-closure-specialization") { (function: Function, context: FunctionPassContext) in + guard function.hasOwnership else { + return + } + guard !function.isDefinedExternally, - function.isAutodiffVJP, - function.blocks.singleElement != nil else { + function.isAutodiffVJP + else { return } - + var remainingSpecializationRounds = 5 - var callerModified = false repeat { - var callSites = gatherCallSites(in: function, context) - - if !callSites.isEmpty { - for callSite in callSites { - var (specializedFunction, alreadyExists) = getOrCreateSpecializedFunction(basedOn: callSite, context) - - if !alreadyExists { - context.notifyNewFunction(function: specializedFunction, derivedFrom: callSite.applyCallee) - } - - rewriteApplyInstruction(using: specializedFunction, callSite: callSite, context) - } - - var deadClosures: InstructionWorklist = callSites.reduce(into: InstructionWorklist(context)) { deadClosures, callSite in - callSite.closureArgDescriptors - .map { $0.closure } - .forEach { deadClosures.pushIfNotVisited($0) } - } - - defer { - deadClosures.deinitialize() - } - - while let deadClosure = deadClosures.pop() { - let isDeleted = context.tryDeleteDeadClosure(closure: deadClosure as! SingleValueInstruction) - if isDeleted { - context.notifyInvalidatedStackNesting() + var changed = false + for inst in function.instructions { + if let partialApply = inst as? PartialApplyInst, + partialApply.isPullbackInResultOfAutodiffVJP + { + if trySpecialize(apply: partialApply, context) { + changed = true } } - - if context.needFixStackNesting { - function.fixStackNesting(context) - } + } + if context.needFixStackNesting { + context.fixStackNesting(in: function) + } + if !changed { + break } - callerModified = callSites.count > 0 remainingSpecializationRounds -= 1 - } while callerModified && remainingSpecializationRounds > 0 + } while remainingSpecializationRounds > 0 } -// =========== Top-level functions ========== // - -private let specializationLevelLimit = 2 - -private func gatherCallSites(in caller: Function, _ context: FunctionPassContext) -> [CallSite] { - /// __Root__ closures created via `partial_apply` or `thin_to_thick_function` may be converted and reabstracted - /// before finally being used at an apply site. We do not want to handle these intermediate closures separately - /// as they are handled and cloned into the specialized function as part of the root closures. Therefore, we keep - /// track of these intermediate closures in a set. - /// - /// This set is populated via the `markConvertedAndReabstractedClosuresAsUsed` function which is called when we're - /// handling the different uses of our root closures. - /// - /// Below SIL example illustrates the above point. - /// ``` - /// // The below set of a "root" closure and its reabstractions/conversions - /// // will be handled as a unit and the entire set will be copied over - /// // in the specialized version of `takesClosure` if we determine that we - /// // can specialize `takesClosure` against its closure argument. - /// __ - /// %someFunction = function_ref @someFunction: $@convention(thin) (Int, Int) -> Int \ - /// %rootClosure = partial_apply [callee_guaranteed] %someFunction (%someInt): $(Int, Int) -> Int \ - /// %thunk = function_ref @reabstractionThunk : $@convention(thin) (@callee_guaranteed (Int) -> Int) -> @out Int / - /// %reabstractedClosure = partial_apply [callee_guaranteed] %thunk(%rootClosure) : / - /// $@convention(thin) (@callee_guaranteed (Int) -> Int) -> @out Int __/ - /// - /// %takesClosure = function_ref @takesClosure : $@convention(thin) (@owned @callee_guaranteed (Int) -> @out Int) -> Int - /// %result = partial_apply %takesClosure(%reabstractedClosure) : $@convention(thin) (@owned @callee_guaranteed () -> @out Int) -> Int - /// ret %result - /// ``` - var convertedAndReabstractedClosures = InstructionSet(context) - - defer { - convertedAndReabstractedClosures.deinitialize() - } - - var callSiteMap = CallSiteMap() +// ===================== Utility functions and extensions ===================== // - for inst in caller.instructions { - if !convertedAndReabstractedClosures.contains(inst), - let rootClosure = inst.asSupportedClosure - { - updateCallSites(for: rootClosure, in: &callSiteMap, - convertedAndReabstractedClosures: &convertedAndReabstractedClosures, context) - } +private func trySpecialize(apply: ApplySite, _ context: FunctionPassContext) -> Bool { + guard isCalleeSpecializable(of: apply), + let specialization = analyzeArguments(of: apply, context) + else { + return false } - return callSiteMap.callSites -} + // We need to make each captured argument of all closures a unique Value. Otherwise the Cloner would + // wrongly map added capture arguments to the re-generated closure in the specialized function: + // + // %4 = partial_apply %3(%1, %1) : (Int, Int) -> () // %1 is captured twice + // + // sil @specialized_closure_user : (Int, Int) -> () { + // bb0(%0 : $Int, %1 : $Int): + // %3 = function_ref @closure + // %4 = partial_apply %3(%0, %0) <- instead of an argument list of `(%0, %1)`! + // + // This wouldn't be a problem per se - the code is correct. However, this is not reflected in the mangling. + // And the same specialized function could be re-used at a call-site where not two identical values are + // passed to the closure. + // + specialization.uniqueCaptureArguments(context) -private func getOrCreateSpecializedFunction(basedOn callSite: CallSite, _ context: FunctionPassContext) - -> (function: Function, alreadyExists: Bool) -{ - let specializedFunctionName = callSite.specializedCalleeName(context) - if let specializedFunction = context.lookupFunction(name: specializedFunctionName) { - return (specializedFunction, true) - } + let specializedFunction = specialization.getOrCreateSpecializedFunction(context) - let applySiteCallee = callSite.applyCallee - let specializedParameters = applySiteCallee.convention.getSpecializedParameters(basedOn: callSite) + specialization.unUniqueCaptureArguments(context) - let specializedFunction = - context.createFunctionForClosureSpecialization(from: applySiteCallee, withName: specializedFunctionName, - withParams: specializedParameters, - withSerialization: applySiteCallee.isSerialized) + specialization.rewriteApply(for: specializedFunction, context) - context.buildSpecializedFunction(specializedFunction: specializedFunction, - buildFn: { (emptySpecializedFunction, functionPassContext) in - let closureSpecCloner = SpecializationCloner(emptySpecializedFunction: emptySpecializedFunction, functionPassContext) - closureSpecCloner.cloneAndSpecializeFunctionBody(using: callSite) - }) + specialization.deleteDeadClosures(context) - return (specializedFunction, false) + return true } -private func rewriteApplyInstruction(using specializedCallee: Function, callSite: CallSite, - _ context: FunctionPassContext) { - let newApplyArgs = callSite.getArgumentsForSpecializedApply(of: specializedCallee) +private func isCalleeSpecializable(of apply: ApplySite) -> Bool { + if let callee = apply.referencedFunction, + callee.isDefinition, - for newApplyArg in newApplyArgs { - if case let .PreviouslyCaptured(capturedArg, needsRetain, parentClosureArgIndex) = newApplyArg, - needsRetain - { - let closureArgDesc = callSite.closureArgDesc(at: parentClosureArgIndex)! - var builder = Builder(before: closureArgDesc.closure, context) - - // TODO: Support only OSSA instructions once the OSSA elimination pass is moved after all function optimization - // passes. - if callSite.applySite.parentBlock != closureArgDesc.closure.parentBlock { - // Emit the retain and release that keeps the argument live across the callee using the closure. - builder.createRetainValue(operand: capturedArg) - - for instr in closureArgDesc.lifetimeFrontier { - builder = Builder(before: instr, context) - builder.createReleaseValue(operand: capturedArg) - } + // We don't support generic functions (yet) + !apply.hasSubstitutions, - // Emit the retain that matches the captured argument by the partial_apply in the callee that is consumed by - // the partial_apply. - builder = Builder(before: callSite.applySite, context) - builder.createRetainValue(operand: capturedArg) - } else { - builder.createRetainValue(operand: capturedArg) - } - } - } + // Don't specialize non-fragile (read as non-serialized) callees if the caller is fragile; the + // specialized callee will have shared linkage, and thus cannot be referenced from the fragile caller. + !(apply.parentFunction.isSerialized && !callee.isSerialized), - // Rewrite apply instruction - var builder = Builder(before: callSite.applySite, context) - let oldApply = callSite.applySite as! PartialApplyInst - let funcRef = builder.createFunctionRef(specializedCallee) - let capturedArgs = Array(newApplyArgs.map { $0.value }) - - let newApply = builder.createPartialApply(function: funcRef, substitutionMap: SubstitutionMap(), - capturedArguments: capturedArgs, calleeConvention: oldApply.calleeConvention, - hasUnknownResultIsolation: oldApply.hasUnknownResultIsolation, - isOnStack: oldApply.isOnStack) - - builder = Builder(before: callSite.applySite.next!, context) - // TODO: Support only OSSA instructions once the OSSA elimination pass is moved after all function optimization - // passes. - for closureArgDesc in callSite.closureArgDescriptors { - if closureArgDesc.isClosureConsumed, - !closureArgDesc.isPartialApplyOnStack, - !closureArgDesc.parameterInfo.isTrivialNoescapeClosure - { - builder.createReleaseValue(operand: closureArgDesc.closure) - } + // If the callee uses a dynamic Self, we cannot specialize it, since the resulting specialization + // might no longer have 'self' as the last parameter. + // + // TODO: Keep the self argument the last when appending arguments. + !callee.mayBindDynamicSelf + { + return true } - - oldApply.replace(with: newApply, context) + return false } -// ===================== Utility functions and extensions ===================== // - -private func updateCallSites(for rootClosure: SingleValueInstruction, in callSiteMap: inout CallSiteMap, - convertedAndReabstractedClosures: inout InstructionSet, _ context: FunctionPassContext) { - var rootClosurePossibleLiveRange = InstructionRange(begin: rootClosure, context) - defer { - rootClosurePossibleLiveRange.deinitialize() - } +private func analyzeArguments(of apply: ApplySite, _ context: FunctionPassContext) -> SpecializationInfo? { + var argumentsToSpecialize = [(Operand, Closure)]() + var rootClosures = [PartialApplyInst]() + var rootClosuresAdded = InstructionSet(context) + defer { rootClosuresAdded.deinitialize() } - var rootClosureApplies = OperandWorklist(context) - defer { - rootClosureApplies.deinitialize() + for argOp in apply.argumentOperands { + var visited = ValueSet(context) + defer { visited.deinitialize() } + if let closure = findSpecializableClosure(of: argOp.value, &visited), + // Ok, we know that we can perform the optimization but not whether or not the optimization + // is profitable. Check if the closure is actually called in the callee (or in a function + // called by the callee). This opens optimization opportunities, like inlining. + isClosureApplied(apply.calleeArgument(of: argOp, in: apply.referencedFunction!)!) + { + argumentsToSpecialize.append((argOp, closure)) + if let partialApply = closure as? PartialApplyInst, + rootClosuresAdded.insert(partialApply) + { + rootClosures.append(partialApply) + } + } } - - // A "root" closure undergoing conversions and/or reabstractions has additional restrictions placed upon it, in order - // for a call site to be specialized against it. We handle conversion/reabstraction uses before we handle apply uses - // to gather the parameters required to evaluate these restrictions or to skip call site uses of "unsupported" - // closures altogether. - // - // There are currently 2 restrictions that are evaluated prior to specializing a callsite against a converted and/or - // reabstracted closure - - // 1. A reabstracted root closure can only be specialized against, if the reabstracted closure is ultimately passed - // trivially (as a noescape+thick function) into the call site. - // - // 2. A root closure may be a partial_apply [stack], in which case we need to make sure that all mark_dependence - // bases for it will be available in the specialized callee in case the call site is specialized against this root - // closure. - - let (foundUnexpectedUse, haveUsedReabstraction) = - handleNonApplies(for: rootClosure, rootClosureApplies: &rootClosureApplies, - rootClosurePossibleLiveRange: &rootClosurePossibleLiveRange, context); - - - if foundUnexpectedUse { - return + if argumentsToSpecialize.isEmpty { + return nil } - - let intermediateClosureArgDescriptorData = - handleApplies(for: rootClosure, callSiteMap: &callSiteMap, rootClosureApplies: &rootClosureApplies, - rootClosurePossibleLiveRange: &rootClosurePossibleLiveRange, - convertedAndReabstractedClosures: &convertedAndReabstractedClosures, - haveUsedReabstraction: haveUsedReabstraction, context) - - finalizeCallSites(for: rootClosure, in: &callSiteMap, - rootClosurePossibleLiveRange: rootClosurePossibleLiveRange, - intermediateClosureArgDescriptorData: intermediateClosureArgDescriptorData, context) + return SpecializationInfo(apply: apply, closureArguments: argumentsToSpecialize, rootClosures: rootClosures) } -/// Handles all non-apply direct and transitive uses of `rootClosure`. -/// -/// Returns: -/// haveUsedReabstraction - whether the root closure is reabstracted via a thunk -/// foundUnexpectedUse - whether the root closure is directly or transitively used in an instruction that we don't know -/// how to handle. If true, then `rootClosure` should not be specialized against. -private func handleNonApplies(for rootClosure: SingleValueInstruction, - rootClosureApplies: inout OperandWorklist, - rootClosurePossibleLiveRange: inout InstructionRange, - _ context: FunctionPassContext) - -> (foundUnexpectedUse: Bool, haveUsedReabstraction: Bool) -{ - var foundUnexpectedUse = false - var haveUsedReabstraction = false - - /// The root closure or an intermediate closure created by reabstracting the root closure may be a `partial_apply - /// [stack]` and we need to make sure that all `mark_dependence` bases for this `onStack` closure will be available in - /// the specialized callee, in case the call site is specialized against this root closure. - /// - /// `possibleMarkDependenceBases` keeps track of all potential values that may be used as bases for creating - /// `mark_dependence`s for our `onStack` root/reabstracted closures. For root closures these values are non-trivial - /// closure captures (which are always available as function arguments in the specialized callee). For reabstracted - /// closures these values may be the root closure or its conversions (below is a short SIL example representing this - /// case). - /// ``` - /// %someFunction = function_ref @someFunction : $@convention(thin) (Int) -> Int - /// %rootClosure = partial_apply [callee_guaranteed] %someFunction(%someInt) : $@convention(thin) (Int) -> Int - /// %noescapeRootClosure = convert_escape_to_noescape %rootClosure : $@callee_guaranteed () -> Int to $@noescape @callee_guaranteed () -> Int - /// %thunk = function_ref @reabstractionThunk : $@convention(thin) (@noescape @callee_guaranteed () -> Int) -> @out Int - /// %thunkedRootClosure = partial_apply [callee_guaranteed] [on_stack] %thunk(%noescapeRootClosure) : $@convention(thin) (@noescape @callee_guaranteed () -> Int) -> @out Int - /// %dependency = mark_dependence %thunkedRootClosure : $@noescape @callee_guaranteed () -> @out Int on %noescapeClosure : $@noescape @callee_guaranteed () -> Int - /// %takesClosure = function_ref @takesClosure : $@convention(thin) (@owned @noescape @callee_guaranteed () -> @out Int) - /// %ret = apply %takesClosure(%dependency) : $@convention(thin) (@owned @noescape @callee_guaranteed () -> @out Int) - /// ``` - /// - /// Any value outside of the aforementioned values is not going to be available in the specialized callee and a - /// `mark_dependence` of the root closure on such a value means that we cannot specialize the call site against it. - var possibleMarkDependenceBases = ValueSet(context) - defer { - possibleMarkDependenceBases.deinitialize() - } +// Walks down the use-def chain of a function argument, recursively, to find a rootClosure. +private func findSpecializableClosure(of value: Value, _ visited: inout ValueSet) -> Closure? { + visited.insert(value) - var rootClosureConversionsAndReabstractions = OperandWorklist(context) - rootClosureConversionsAndReabstractions.pushIfNotVisited(contentsOf: rootClosure.uses) - defer { - rootClosureConversionsAndReabstractions.deinitialize() - } + let specializationLevelLimit = 2 - if let pai = rootClosure as? PartialApplyInst { - for arg in pai.arguments { - possibleMarkDependenceBases.insert(arg) - } - } - - while let use = rootClosureConversionsAndReabstractions.pop() { - switch use.instruction { - case let cfi as ConvertFunctionInst: - rootClosureConversionsAndReabstractions.pushIfNotVisited(contentsOf: cfi.uses) - possibleMarkDependenceBases.insert(cfi) - rootClosurePossibleLiveRange.insert(use.instruction) - - case let cvt as ConvertEscapeToNoEscapeInst: - rootClosureConversionsAndReabstractions.pushIfNotVisited(contentsOf: cvt.uses) - possibleMarkDependenceBases.insert(cvt) - rootClosurePossibleLiveRange.insert(use.instruction) - - case let pai as PartialApplyInst: - if !pai.isPullbackInResultOfAutodiffVJP, - pai.isSupportedClosure, - pai.isPartialApplyOfThunk, - // Argument must be a closure - pai.arguments[0].type.isThickFunction - { - rootClosureConversionsAndReabstractions.pushIfNotVisited(contentsOf: pai.uses) - possibleMarkDependenceBases.insert(pai) - rootClosurePossibleLiveRange.insert(use.instruction) - haveUsedReabstraction = true - } else if pai.isPullbackInResultOfAutodiffVJP { - rootClosureApplies.pushIfNotVisited(use) - } - - case let mv as MoveValueInst: - rootClosureConversionsAndReabstractions.pushIfNotVisited(contentsOf: mv.uses) - possibleMarkDependenceBases.insert(mv) - rootClosurePossibleLiveRange.insert(use.instruction) + switch value { + case is ConvertFunctionInst, + is ConvertEscapeToNoEscapeInst, + is MoveValueInst, + is CopyValueInst: + return findSpecializableClosure(of: (value as! UnaryInstruction).operand.value, &visited) - case let mdi as MarkDependenceInst: - if possibleMarkDependenceBases.contains(mdi.base), - mdi.value == use.value, - mdi.value.type.isNoEscapeFunction, - mdi.value.type.isThickFunction - { - rootClosureConversionsAndReabstractions.pushIfNotVisited(contentsOf: mdi.uses) - rootClosurePossibleLiveRange.insert(use.instruction) - } - - case is CopyValueInst, - is DestroyValueInst, - is RetainValueInst, - is ReleaseValueInst, - is StrongRetainInst, - is StrongReleaseInst: - rootClosurePossibleLiveRange.insert(use.instruction) - - case let ti as TupleInst: - if ti.parentFunction.isAutodiffVJP, - let returnInst = ti.parentFunction.returnInstruction, - ti == returnInst.returnedValue - { - // This is the pullback closure returned from an Autodiff VJP and we don't need to handle it. - } else { - fallthrough - } + case let mdi as MarkDependenceInst: + guard mdi.value.type.isNoEscapeFunction, mdi.value.type.isThickFunction else { + return nil + } + guard let operandClosure = findSpecializableClosure(of: mdi.value, &visited) else { + return nil + } + // Make sure that the mark_dependence's base is part of the use-def chain and will therefore be cloned as well. + if !visited.contains(mdi.base) { + return nil + } + return operandClosure + + case let partialApply as PartialApplyInst: + // Don't specialize for re-abstractions via a partial_apply, but treat such re-abstractions like + // closure "conversions". E.g. + // ``` + // %1 = partial_apply // root closure + // %2 = function_ref @thunk + // %3 = partial_apply %2(%1) // re-abstraction + // apply %f(%3) + // ``` + if partialApply.isPartialApplyOfThunk, + let argumentClosure = findSpecializableClosure(of: partialApply.arguments[0], &visited) + { + return argumentClosure + } + guard let callee = partialApply.referencedFunction, + !partialApply.hasSubstitutions, + + // Avoid an infinite specialization loop caused by repeated runs of ClosureSpecialization and + // ConstantCapturePropagation. + // ConstantCapturePropagation propagates constant function-literals. Such function specializations + // can then be optimized again by ClosureSpecialization and so on. This happens if a closure argument + // is called _and_ referenced in another closure, which is passed to a recursive call. E.g. + // + // func foo(_ c: @escaping () -> ()) { + // c() foo({ c() }) + // } + // + // A limit of 2 is good enough and will not be exceed in "regular" optimization scenarios. + callee.specializationLevel <= specializationLevelLimit, + + // Functions with a readnone, readonly or releasenone effect and a consumed captures cannot be + // specialized because the captured arguments are passed as owned arguments to the specialized + // function. Destroy for such arguments in the specialized function violates the effect. + (partialApply.isOnStack || callee.effectAllowsSpecialization), + + // TODO: handle other kind of indirect arguments + partialApply.hasOnlyInoutIndirectArguments, + + (partialApply.isOnStack || partialApply.allArgumentsCanBeCopied) + else { + return nil + } + return partialApply - default: - foundUnexpectedUse = true - log("Found unexpected direct or transitive user of root closure: \(use.instruction)") - return (foundUnexpectedUse, haveUsedReabstraction) + case let tttfi as ThinToThickFunctionInst: + guard let callee = tttfi.referencedFunction, + callee.specializationLevel <= specializationLevelLimit + else { + return nil } - } + return tttfi - return (foundUnexpectedUse, haveUsedReabstraction) + default: + return nil + } } -private typealias IntermediateClosureArgDescriptorDatum = (applySite: SingleValueInstruction, closureArgIndex: Int, paramInfo: ParameterInfo) - -private func handleApplies(for rootClosure: SingleValueInstruction, callSiteMap: inout CallSiteMap, - rootClosureApplies: inout OperandWorklist, - rootClosurePossibleLiveRange: inout InstructionRange, - convertedAndReabstractedClosures: inout InstructionSet, haveUsedReabstraction: Bool, - _ context: FunctionPassContext) -> [IntermediateClosureArgDescriptorDatum] -{ - var intermediateClosureArgDescriptorData: [IntermediateClosureArgDescriptorDatum] = [] - - while let use = rootClosureApplies.pop() { - rootClosurePossibleLiveRange.insert(use.instruction) - - // TODO [extend to general swift]: Handle full apply sites - guard let pai = use.instruction as? PartialApplyInst else { - continue - } +/// Either a `partial_apply` or a `thin_to_thick_function` +private typealias Closure = SingleValueInstruction - // TODO: Handling generic closures may be possible but is not yet implemented - if pai.hasSubstitutions || !pai.calleeIsDynamicFunctionRef || !pai.isPullbackInResultOfAutodiffVJP { - continue - } +/// Information about the function to be specialized and for which closure arguments. +private struct SpecializationInfo { - guard let callee = pai.referencedFunction else { - continue - } + // The apply which we want to specialize + let apply: ApplySite - // Workaround for a problem with OSSA: https://github.com/swiftlang/swift/issues/78847 - // TODO: remove this if-statement once the underlying problem is fixed. - if callee.hasOwnership { - continue - } + // All closure arguments of the apply which we want to replace (usually there is one, but there can be + // multiple). + // Note that the `Closure` is not necessarily the value of the `Operand`. There can be function conventions + // and re-abstractions (via a thunk) in-between: + // + // %4 = partial_apply %3(%1) : (Int) -> () // %4 = rootClosure + // %5 = convert_function %4 + // %6 = copy_value %5 + // apply %5(%5) // %5 = closureArgument + // + let closureArguments: [(closureArgument: Operand, rootClosure: Closure)] - if callee.isDefinedExternally { - continue - } + // All rootClosures of `closureArguments` which are `partial_apply`s, and uniqued: if a rootClosure + // appears multiple times in `closureArguments`, it's only added a single time here. + let rootClosures: [PartialApplyInst] - // Don't specialize non-fragile (read as non-serialized) callees if the caller is fragile; the specialized callee - // will have shared linkage, and thus cannot be referenced from the fragile caller. - let caller = rootClosure.parentFunction - if caller.isSerialized && !callee.isSerialized { - continue - } + // The function to specialize + var callee: Function { apply.referencedFunction! } - // If the callee uses a dynamic Self, we cannot specialize it, since the resulting specialization might no longer - // have 'self' as the last parameter. - // - // TODO: We could fix this by inserting new arguments more carefully, or changing how we model dynamic Self - // altogether. - if callee.mayBindDynamicSelf { - continue - } + private typealias Cloner = SIL.Cloner - // Proceed if the closure is passed as an argument (and not called). If it is called we have nothing to do. - // - // `closureArgumentIndex` is the index of the closure in the callee's argument list. - guard let closureArgumentIndex = pai.calleeArgumentIndex(of: use) else { - continue - } + func getOrCreateSpecializedFunction(_ context: FunctionPassContext) -> Function { + let specializedFunctionName = getSpecializedFunctionName(context) - // Ok, we know that we can perform the optimization but not whether or not the optimization is profitable. Check if - // the closure is actually called in the callee (or in a function called by the callee). - if !isClosureApplied(in: callee, closureArgIndex: closureArgumentIndex) { - continue + if let existingSpecializedFunction = context.lookupFunction(name: specializedFunctionName) { + return existingSpecializedFunction } - let onlyHaveThinToThickClosure = rootClosure is ThinToThickFunctionInst && !haveUsedReabstraction + let specializedParameters = getSpecializedParameters() - guard let closureParamInfo = pai.operandConventions[parameter: use.index] else { - fatalError("While handling apply uses, parameter info not found for operand: \(use)!") - } + let specializedFunction = + context.createSpecializedFunctionDeclaration( + from: callee, withName: specializedFunctionName, + withParams: specializedParameters, + // The specialized function is always a thin function. This is important because we add additional + // parameters after the Self parameter of witness methods. In this case the new function is not a + // method anymore. + makeThin: true, makeBare: true) - // If we are going to need to release the copied over closure, we must make sure that we understand all the exit - // blocks, i.e., they terminate with an instruction that clearly indicates whether to release the copied over - // closure or leak it. - if closureParamInfo.convention.isGuaranteed, - !onlyHaveThinToThickClosure, - !callee.blocks.allSatisfy({ $0.isReachableExitBlock || $0.terminator is UnreachableInst }) - { - continue - } + context.buildSpecializedFunction( + specializedFunction: specializedFunction, + buildFn: { (specializedFunction, specializedContext) in + var cloner = Cloner(cloneToEmptyFunction: specializedFunction, specializedContext) + defer { cloner.deinitialize() } - // Functions with a readnone, readonly or releasenone effect and a nontrivial context cannot be specialized. - // Inserting a release in such a function results in miscompilation after other optimizations. For now, the - // specialization is disabled. - // - // TODO: A @noescape closure should never be converted to an @owned argument regardless of the function's effect - // attribute. - if !callee.effectAllowsSpecialization && !onlyHaveThinToThickClosure { - continue - } + cloneAndSpecializeFunctionBody(using: &cloner) + }) - // Avoid an infinite specialization loop caused by repeated runs of ClosureSpecializer and CapturePropagation. - // CapturePropagation propagates constant function-literals. Such function specializations can then be optimized - // again by the ClosureSpecializer and so on. This happens if a closure argument is called _and_ referenced in - // another closure, which is passed to a recursive call. E.g. - // - // func foo(_ c: @escaping () -> ()) { - // c() foo({ c() }) - // } - // - // A limit of 2 is good enough and will not be exceed in "regular" optimization scenarios. - let closureCallee = rootClosure is PartialApplyInst - ? (rootClosure as! PartialApplyInst).referencedFunction! - : (rootClosure as! ThinToThickFunctionInst).referencedFunction! + context.notifyNewFunction(function: specializedFunction, derivedFrom: callee) - if closureCallee.specializationLevel > specializationLevelLimit { - continue - } + return specializedFunction + } - if haveUsedReabstraction { - markConvertedAndReabstractedClosuresAsUsed(rootClosure: rootClosure, convertedAndReabstractedClosure: use.value, - convertedAndReabstractedClosures: &convertedAndReabstractedClosures) - } - - if callSiteMap[pai] == nil { - callSiteMap.insert(key: pai, value: CallSite(applySite: pai)) - } - intermediateClosureArgDescriptorData - .append((applySite: pai, closureArgIndex: closureArgumentIndex, paramInfo: closureParamInfo)) - } + private func getSpecializedFunctionName(_ context: FunctionPassContext) -> String { + var visited = Dictionary() - return intermediateClosureArgDescriptorData -} + let argumentManglings = closureArguments.map { (argOp, closure) in + let argIdx = apply.calleeArgumentIndex(of: argOp)! + if let prevArgIdx = visited[closure] { -/// Finalizes the call sites for a given root closure by adding a corresponding `ClosureArgDescriptor` -/// to all call sites where the closure is ultimately passed as an argument. -private func finalizeCallSites(for rootClosure: SingleValueInstruction, in callSiteMap: inout CallSiteMap, - rootClosurePossibleLiveRange: InstructionRange, - intermediateClosureArgDescriptorData: [IntermediateClosureArgDescriptorDatum], - _ context: FunctionPassContext) -{ - let closureInfo = ClosureInfo(closure: rootClosure, lifetimeFrontier: Array(rootClosurePossibleLiveRange.ends)) - - for (applySite, closureArgumentIndex, parameterInfo) in intermediateClosureArgDescriptorData { - guard var callSite = callSiteMap[applySite] else { - fatalError("While finalizing call sites, call site descriptor not found for call site: \(applySite)!") + // If the same closure is passed multiple times to a function, we need to reflect this in the mangling: + // + // %3 = function_ref @closure + // %4 = partial_apply %3(%1) : (Int) -> () + // apply %6(%4, %4) + // + // sil @specialized_closure_user : (Int) -> () { + // + // is different than + // + // %3 = function_ref @closure + // %4 = partial_apply %3(%1) : (Int) -> () + // %5 = partial_apply %3(%1) : (Int) -> () + // apply %6(%4, %5) + // + // sil @specialized_closure_user : (Int, Int) -> () { + // + return (argIdx, FunctionPassContext.ClosureArgumentMangling.previousArgumentIndex(prevArgIdx)) + } else { + visited[closure] = argIdx + return (argIdx, FunctionPassContext.ClosureArgumentMangling.closure(closure)) + } } - let closureArgDesc = ClosureArgDescriptor(closureInfo: closureInfo, closureArgumentIndex: closureArgumentIndex, - parameterInfo: parameterInfo) - callSite.appendClosureArgDescriptor(closureArgDesc) - callSiteMap.update(key: applySite, value: callSite) + return context.mangle(withClosureArguments: argumentManglings, from: callee) } -} -private func isClosureApplied(in callee: Function, closureArgIndex index: Int) -> Bool { - func inner(_ callee: Function, _ index: Int, _ handledFuncs: inout Set) -> Bool { - let closureArg = callee.argument(at: index) + private func getSpecializedParameters() -> [ParameterInfo] { + var specializedParamInfoList: [ParameterInfo] = [] - for use in closureArg.uses { - if let fai = use.instruction as? ApplySite { - if fai.callee == closureArg { - return true - } + // Start by adding all original parameters except for the closure parameters. + let firstParamIndex = callee.argumentConventions.firstParameterIndex + for (index, paramInfo) in callee.convention.parameters.enumerated() { + let argIndex = index + firstParamIndex + if !closureArguments.contains(where: { apply.calleeArgumentIndex(of: $0.0) == argIndex}) { + specializedParamInfoList.append(paramInfo) + } + } - if let faiCallee = fai.referencedFunction, - !faiCallee.blocks.isEmpty, - handledFuncs.insert(faiCallee).inserted, - handledFuncs.count <= recursionBudget - { - if inner(faiCallee, fai.calleeArgumentIndex(of: use)!, &handledFuncs) { - return true - } - } + // Now, append parameters captured by each of the root closures. + for partialApply in rootClosures { + let closureConvention = partialApply.functionConvention + let unappliedArgumentCount = partialApply.unappliedArgumentCount - closureConvention.indirectSILResultCount + + for paramInfo in closureConvention.parameters[unappliedArgumentCount...] { + let newParamInfo = paramInfo.withSpecializedConvention(for: partialApply, in: callee) + specializedParamInfoList.append(newParamInfo) } } - return false + return specializedParamInfoList } - // Limit the number of recursive calls to not go into exponential behavior in corner cases. - let recursionBudget = 8 - var handledFuncs: Set = [] - return inner(callee, index, &handledFuncs) -} + private func cloneAndSpecializeFunctionBody(using cloner: inout Cloner) { + addFunctionArgumentsWithoutClosures(using: &cloner) -/// Marks any converted/reabstracted closures, corresponding to a given root closure as used. We do not want to -/// look at such closures separately as during function specialization they will be handled as part of the root closure. -private func markConvertedAndReabstractedClosuresAsUsed(rootClosure: Value, convertedAndReabstractedClosure: Value, - convertedAndReabstractedClosures: inout InstructionSet) -{ - if convertedAndReabstractedClosure != rootClosure { - switch convertedAndReabstractedClosure { - case let pai as PartialApplyInst: - convertedAndReabstractedClosures.insert(pai) - return - markConvertedAndReabstractedClosuresAsUsed(rootClosure: rootClosure, - convertedAndReabstractedClosure: pai.arguments[0], - convertedAndReabstractedClosures: &convertedAndReabstractedClosures) - case let cvt as ConvertFunctionInst: - convertedAndReabstractedClosures.insert(cvt) - return - markConvertedAndReabstractedClosuresAsUsed(rootClosure: rootClosure, - convertedAndReabstractedClosure: cvt.fromFunction, - convertedAndReabstractedClosures: &convertedAndReabstractedClosures) - case let cvt as ConvertEscapeToNoEscapeInst: - convertedAndReabstractedClosures.insert(cvt) - return - markConvertedAndReabstractedClosuresAsUsed(rootClosure: rootClosure, - convertedAndReabstractedClosure: cvt.fromFunction, - convertedAndReabstractedClosures: &convertedAndReabstractedClosures) - case let mdi as MarkDependenceInst: - convertedAndReabstractedClosures.insert(mdi) - return - markConvertedAndReabstractedClosuresAsUsed(rootClosure: rootClosure, convertedAndReabstractedClosure: mdi.value, - convertedAndReabstractedClosures: &convertedAndReabstractedClosures) - default: - log("Parent function of callSite: \(rootClosure.parentFunction)") - log("Root closure: \(rootClosure)") - log("Converted/reabstracted closure: \(convertedAndReabstractedClosure)") - fatalError("While marking converted/reabstracted closures as used, found unexpected instruction: \(convertedAndReabstractedClosure)") + for rootClosure in rootClosures { + addFunctionArgumentsForCaptures(of: rootClosure, using: &cloner) } - } -} -private extension SpecializationCloner { - func cloneAndSpecializeFunctionBody(using callSite: CallSite) { - self.cloneEntryBlockArgsWithoutOrigClosures(usingOrigCalleeAt: callSite) + let clonedClosureArguments = cloneClosures(using: &cloner) - let (allSpecializedEntryBlockArgs, closureArgIndexToAllClonedReleasableClosures) = cloneAllClosures(at: callSite) + cloner.cloneFunctionBody(from: callee) - self.cloneFunctionBody(from: callSite.applyCallee, entryBlockArguments: allSpecializedEntryBlockArgs) - - self.insertCleanupCodeForClonedReleasableClosures( - from: callSite, closureArgIndexToAllClonedReleasableClosures: closureArgIndexToAllClonedReleasableClosures) + addMissingDestroysAtFunctionExits(for: clonedClosureArguments, cloner.context) } - private func cloneEntryBlockArgsWithoutOrigClosures(usingOrigCalleeAt callSite: CallSite) { - let originalEntryBlock = callSite.applyCallee.entryBlock - let clonedFunction = self.cloned - let clonedEntryBlock = self.entryBlock - - originalEntryBlock.arguments - .enumerated() - .filter { index, _ in !callSite.hasClosureArg(at: index) } - .forEach { _, arg in - let clonedEntryBlockArgType = arg.type.getLoweredType(in: clonedFunction) - let clonedEntryBlockArg = clonedEntryBlock.addFunctionArgument(type: clonedEntryBlockArgType, self.context) - clonedEntryBlockArg.copyFlags(from: arg as! FunctionArgument) - } + private func addFunctionArgumentsWithoutClosures(using cloner: inout Cloner) { + let clonedEntryBlock = cloner.getOrCreateEntryBlock() + + for originalArg in callee.arguments where !isClosureArgument(calleeArgument: originalArg) { + let argType = originalArg.type.getLoweredType(in: cloner.targetFunction) + let clonedArg = clonedEntryBlock.addFunctionArgument(type: argType, cloner.context) + clonedArg.copyFlags(from: originalArg, cloner.context) + cloner.recordFoldedValue(originalArg, mappedTo: clonedArg) + } } - /// Clones all closures, originally passed to the callee at the given callSite, into the specialized function. - /// - /// Returns the following - - /// - allSpecializedEntryBlockArgs: Complete list of entry block arguments for the specialized function. This includes - /// the original arguments to the function (minus the closure arguments) and the arguments representing the values - /// originally captured by the skipped closure arguments. - /// - /// - closureArgIndexToAllClonedReleasableClosures: Mapping from a closure's argument index at `callSite` to the list - /// of corresponding releasable closures cloned into the specialized function. We have a "list" because we clone - /// "closure chains", which consist of a "root" closure and its conversions/reabstractions. This map is used to - /// generate cleanup code for the cloned closures in the specialized function. - private func cloneAllClosures(at callSite: CallSite) - -> (allSpecializedEntryBlockArgs: [Value], - closureArgIndexToAllClonedReleasableClosures: [Int: [SingleValueInstruction]]) - { - func entryBlockArgsWithOrigClosuresSkipped() -> [Value?] { - var clonedNonClosureEntryBlockArgs = self.entryBlock.arguments.makeIterator() - - return callSite.applyCallee - .entryBlock - .arguments - .enumerated() - .reduce(into: []) { result, origArgTuple in - let (index, _) = origArgTuple - if !callSite.hasClosureArg(at: index) { - result.append(clonedNonClosureEntryBlockArgs.next()) - } else { - result.append(Optional.none) - } - } + private func addFunctionArgumentsForCaptures(of closure: PartialApplyInst, using cloner: inout Cloner) { + for originalClosureArg in closure.arguments { + let capturedArg = cloner.targetFunction.entryBlock.addFunctionArgument( + type: originalClosureArg.type.getLoweredType(in: cloner.targetFunction), + cloner.context) + if !cloner.isCloned(value: originalClosureArg) { + cloner.recordFoldedValue(originalClosureArg, mappedTo: capturedArg) + } } + } - var entryBlockArgs: [Value?] = entryBlockArgsWithOrigClosuresSkipped() - var closureArgIndexToAllClonedReleasableClosures: [Int: [SingleValueInstruction]] = [:] + private func cloneClosures(using cloner: inout Cloner) -> [Value] { + return closureArguments.map { (closureArgOp, _) in + let clonedArg = cloner.cloneRecursively(value: closureArgOp.value) - for closureArgDesc in callSite.closureArgDescriptors { - let (finalClonedReabstractedClosure, allClonedReleasableClosures) = - self.cloneClosureChain(representedBy: closureArgDesc, at: callSite) + let originalArg = apply.calleeArgument(of: closureArgOp, in: callee)! + cloner.recordFoldedValue(originalArg, mappedTo: clonedArg) - entryBlockArgs[closureArgDesc.closureArgIndex] = finalClonedReabstractedClosure - closureArgIndexToAllClonedReleasableClosures[closureArgDesc.closureArgIndex] = allClonedReleasableClosures + return clonedArg } - - return (entryBlockArgs.map { $0! }, closureArgIndexToAllClonedReleasableClosures) } - private func cloneClosureChain(representedBy closureArgDesc: ClosureArgDescriptor, at callSite: CallSite) - -> (finalClonedReabstractedClosure: SingleValueInstruction, allClonedReleasableClosures: [SingleValueInstruction]) - { - let (origToClonedValueMap, capturedArgRange) = self.addEntryBlockArgs(forValuesCapturedBy: closureArgDesc) - let clonedFunction = self.cloned - let clonedEntryBlock = self.entryBlock - let clonedClosureArgs = Array(clonedEntryBlock.arguments[capturedArgRange]) + func rewriteApply(for specializedFunction: Function, _ context: FunctionPassContext) { + insertCompensatingDestroysForOwnedClosureArguments(context) - let builder = clonedEntryBlock.instructions.isEmpty - ? Builder(atStartOf: clonedFunction, self.context) - : Builder(atEndOf: clonedEntryBlock, location: clonedEntryBlock.instructions.last!.location, self.context) + let newApplyArgs = getNewApplyArguments(context) - let clonedRootClosure = builder.cloneRootClosure(representedBy: closureArgDesc, capturedArguments: clonedClosureArgs) + let builder = Builder(before: apply, context) + let funcRef = builder.createFunctionRef(specializedFunction) - let (finalClonedReabstractedClosure, releasableClonedReabstractedClosures) = - builder.cloneRootClosureReabstractions(rootClosure: closureArgDesc.closure, clonedRootClosure: clonedRootClosure, - reabstractedClosure: callSite.appliedArgForClosure(at: closureArgDesc.closureArgIndex)!, - origToClonedValueMap: origToClonedValueMap, - self.context) + switch apply { + case let oldPartialApply as PartialApplyInst: + let newPartialApply = builder.createPartialApply( + function: funcRef, substitutionMap: SubstitutionMap(), + capturedArguments: newApplyArgs, calleeConvention: oldPartialApply.calleeConvention, + hasUnknownResultIsolation: oldPartialApply.hasUnknownResultIsolation, + isOnStack: oldPartialApply.isOnStack) + oldPartialApply.replace(with: newPartialApply, context) - let allClonedReleasableClosures = [clonedRootClosure] + releasableClonedReabstractedClosures - return (finalClonedReabstractedClosure, allClonedReleasableClosures) - } + case let oldApply as ApplyInst: + let newApply = builder.createApply(function: funcRef, SubstitutionMap(), arguments: newApplyArgs, + isNonThrowing: oldApply.isNonThrowing, + isNonAsync: oldApply.isNonAsync) + oldApply.replace(with: newApply, context) - private func addEntryBlockArgs(forValuesCapturedBy closureArgDesc: ClosureArgDescriptor) - -> (origToClonedValueMap: [HashableValue: Value], capturedArgRange: Range) - { - var origToClonedValueMap: [HashableValue: Value] = [:] - let clonedFunction = self.cloned - let clonedEntryBlock = self.entryBlock - - let capturedArgRangeStart = clonedEntryBlock.arguments.count - - for arg in closureArgDesc.arguments { - let capturedArg = clonedEntryBlock.addFunctionArgument(type: arg.type.getLoweredType(in: clonedFunction), - self.context) - origToClonedValueMap[arg] = capturedArg - } + case let oldTryApply as TryApplyInst: + builder.createTryApply(function: funcRef, SubstitutionMap(), arguments: newApplyArgs, + normalBlock: oldTryApply.normalBlock, errorBlock: oldTryApply.errorBlock, + isNonAsync: oldTryApply.isNonAsync) + context.erase(instruction: oldTryApply) - let capturedArgRangeEnd = clonedEntryBlock.arguments.count - let capturedArgRange = capturedArgRangeStart == capturedArgRangeEnd - ? 0..<0 - : capturedArgRangeStart.. [Value] { + let newCapturedArguments = rootClosures.flatMap { partialApply in + partialApply.arguments.map { capturedArg in + if partialApply.isOnStack || capturedArg.ownership == .none { + // Non-escaping closures don't consume their captures. Therefore we pass them also as "guaranteed" + // arguments to the specialized function. + // Note that because the non-escaping closure was passed to the original function, this guarantees + // that the lifetime of the captured arguments also extend to at least the apply of the function. + capturedArg + } else { + // Escaping closures consume their captures. Therefore we pass them as "owned" arguments to the + // specialized function. + capturedArg.copy(at: partialApply, andMakeAvailableIn: apply.parentBlock, context) } } } - - if (self.context.needFixStackNesting) { - self.cloned.fixStackNesting(self.context) - } + return nonClosureArguments.values + newCapturedArguments } -} -private extension [HashableValue: Value] { - subscript(key: Value) -> Value? { - get { - self[key.hashable] - } - set { - self[key.hashable] = newValue - } - } -} + func uniqueCaptureArguments(_ context: FunctionPassContext) { + let builder = Builder(before: apply, context) -private extension CallSite { - enum NewApplyArg { - case Original(Value) - // TODO: This can be simplified in OSSA. We can just do a copy_value for everything - except for addresses??? - case PreviouslyCaptured( - value: Value, needsRetain: Bool, parentClosureArgIndex: Int) - - var value: Value { - switch self { - case let .Original(originalArg): - return originalArg - case let .PreviouslyCaptured(capturedArg, _, _): - return capturedArg + // Insert identity cast instructions for all closure arguments to make them unique. We could use any kind + // of forwarding instruction - we'll delete them afterwards, anyway. + // + // %4 = partial_apply %3(%1, %1) : (Int, Int) -> () // %1 is captured twice + // -> + // %2 = unchecked_value_cast %1 + // %3 = unchecked_value_cast %1 + // %4 = partial_apply %3(%2, %3) : (Int, Int) -> () // all arguments are unique values now! + // + for closure in rootClosures { + for argOp in closure.argumentOperands { + let cast = builder.createUncheckedValueCast(from: argOp.value, to: argOp.value.type) + argOp.set(to: cast, context) } } } - func getArgumentsForSpecializedApply(of specializedCallee: Function) -> [NewApplyArg] - { - var newApplyArgs: [NewApplyArg] = [] - - // Original arguments - for (applySiteIndex, arg) in self.applySite.arguments.enumerated() { - let calleeArgIndex = self.applySite.unappliedArgumentCount + applySiteIndex - if !self.hasClosureArg(at: calleeArgIndex) { - newApplyArgs.append(.Original(arg)) + func unUniqueCaptureArguments(_ context: FunctionPassContext) { + // Remove the inserted identity casts again. + for closure in rootClosures { + for argOp in closure.argumentOperands { + let cast = argOp.value as! UncheckedValueCastInst + cast.replace(with: cast.fromValue, context) } } + } - // Previously captured arguments - for closureArgDesc in self.closureArgDescriptors { - for (applySiteIndex, capturedArg) in closureArgDesc.arguments.enumerated() { - let needsRetain = closureArgDesc.isCapturedArgNonTrivialObjectType(applySiteIndex: applySiteIndex, - specializedCallee: specializedCallee) - - newApplyArgs.append(.PreviouslyCaptured(value: capturedArg, needsRetain: needsRetain, - parentClosureArgIndex: closureArgDesc.closureArgIndex)) + func deleteDeadClosures(_ context: FunctionPassContext) { + for (_, closure) in closureArguments where !closure.isDeleted { + if context.tryDeleteDeadClosure(closure: closure) { + context.notifyInvalidatedStackNesting() } } - - return newApplyArgs } -} - -private extension ClosureArgDescriptor { - func isCapturedArgNonTrivialObjectType(applySiteIndex: Int, specializedCallee: Function) -> Bool { - precondition(self.closure is PartialApplyInst, "ClosureArgDescriptor is not for a partial_apply closure!") - let capturedArg = self.arguments[applySiteIndex] - let pai = self.closure as! PartialApplyInst - let capturedArgIndexInCallee = applySiteIndex + pai.unappliedArgumentCount - let capturedArgConvention = self.callee.argumentConventions[capturedArgIndexInCallee] + private var nonClosureArguments: LazyFilterSequence { + apply.argumentOperands.lazy.filter{ argOp in !closureArguments.contains{ $0.0 == argOp } } + } - return !capturedArg.type.isTrivial(in: specializedCallee) && - !capturedArgConvention.isAllowedIndirectConvForClosureSpec + private func isClosureArgument(calleeArgument: FunctionArgument) -> Bool { + closureArguments.contains { apply.calleeArgument(of: $0.0, in: callee) == calleeArgument } } } -private extension Builder { - func cloneRootClosure(representedBy closureArgDesc: ClosureArgDescriptor, capturedArguments: [Value]) - -> SingleValueInstruction - { - let function = self.createFunctionRef(closureArgDesc.callee) +private func isClosureApplied(_ closure: Value) -> Bool { + var handledFuncs: Set = [] + return checkRecursivelyIfClosureIsApplied(closure, &handledFuncs) +} - if let pai = closureArgDesc.closure as? PartialApplyInst { - return self.createPartialApply(function: function, substitutionMap: SubstitutionMap(), - capturedArguments: capturedArguments, calleeConvention: pai.calleeConvention, - hasUnknownResultIsolation: pai.hasUnknownResultIsolation, - isOnStack: pai.isOnStack) - } else { - return self.createThinToThickFunction(thinFunction: function, resultType: closureArgDesc.closure.type) - } - } +private func checkRecursivelyIfClosureIsApplied(_ closure: Value, _ handledFuncs: inout Set) -> Bool { + for use in closure.uses { + switch use.instruction { - func cloneRootClosureReabstractions(rootClosure: Value, clonedRootClosure: Value, reabstractedClosure: Value, - origToClonedValueMap: [HashableValue: Value], _ context: FunctionPassContext) - -> (finalClonedReabstractedClosure: SingleValueInstruction, releasableClonedReabstractedClosures: [PartialApplyInst]) - { - func inner(_ rootClosure: Value, _ clonedRootClosure: Value, _ reabstractedClosure: Value, - _ releasableClonedReabstractedClosures: inout [PartialApplyInst], - _ origToClonedValueMap: inout [HashableValue: Value]) -> Value { - switch reabstractedClosure { - case let reabstractedClosure where reabstractedClosure == rootClosure: - origToClonedValueMap[reabstractedClosure] = clonedRootClosure - return clonedRootClosure - - case let cvt as ConvertFunctionInst: - let toBeReabstracted = inner(rootClosure, clonedRootClosure, cvt.fromFunction, - &releasableClonedReabstractedClosures, &origToClonedValueMap) - let reabstracted = self.createConvertFunction(originalFunction: toBeReabstracted, resultType: cvt.type, - withoutActuallyEscaping: cvt.withoutActuallyEscaping) - origToClonedValueMap[cvt] = reabstracted - return reabstracted - - case let cvt as ConvertEscapeToNoEscapeInst: - let toBeReabstracted = inner(rootClosure, clonedRootClosure, cvt.fromFunction, - &releasableClonedReabstractedClosures, &origToClonedValueMap) - let reabstracted = self.createConvertEscapeToNoEscape(originalFunction: toBeReabstracted, resultType: cvt.type, - isLifetimeGuaranteed: true) - origToClonedValueMap[cvt] = reabstracted - return reabstracted - - case let pai as PartialApplyInst: - let toBeReabstracted = inner(rootClosure, clonedRootClosure, pai.arguments[0], - &releasableClonedReabstractedClosures, &origToClonedValueMap) - - guard let function = pai.referencedFunction else { - log("Parent function of callSite: \(rootClosure.parentFunction)") - log("Root closure: \(rootClosure)") - log("Unsupported reabstraction closure: \(pai)") - fatalError("Encountered unsupported reabstraction (via partial_apply) of root closure!") - } - - let fri = self.createFunctionRef(function) - let reabstracted = self.createPartialApply(function: fri, substitutionMap: SubstitutionMap(), - capturedArguments: [toBeReabstracted], - calleeConvention: pai.calleeConvention, - hasUnknownResultIsolation: pai.hasUnknownResultIsolation, - isOnStack: pai.isOnStack) - releasableClonedReabstractedClosures.append(reabstracted) - origToClonedValueMap[pai] = reabstracted - return reabstracted - - case let mdi as MarkDependenceInst: - let toBeReabstracted = inner(rootClosure, clonedRootClosure, mdi.value, &releasableClonedReabstractedClosures, - &origToClonedValueMap) - let base = origToClonedValueMap[mdi.base]! - let reabstracted = self.createMarkDependence(value: toBeReabstracted, base: base, kind: .Escaping) - origToClonedValueMap[mdi] = reabstracted - return reabstracted - - default: - log("Parent function of callSite: \(rootClosure.parentFunction)") - log("Root closure: \(rootClosure)") - log("Converted/reabstracted closure: \(reabstractedClosure)") - fatalError("Encountered unsupported reabstraction of root closure: \(reabstractedClosure)") + case let apply as FullApplySite: + if apply.callee == closure { + return true } - } - - var releasableClonedReabstractedClosures: [PartialApplyInst] = [] - var origToClonedValueMap = origToClonedValueMap - let finalClonedReabstractedClosure = inner(rootClosure, clonedRootClosure, reabstractedClosure, - &releasableClonedReabstractedClosures, &origToClonedValueMap) - return (finalClonedReabstractedClosure as! SingleValueInstruction, releasableClonedReabstractedClosures) - } - - func destroyPartialApply(pai: PartialApplyInst, _ context: FunctionPassContext){ - // TODO: Support only OSSA instructions once the OSSA elimination pass is moved after all function optimization - // passes. - - if pai.isOnStack { - // for arg in pai.arguments { - // self.createDestroyValue(operand: arg) - // } - // self.createDestroyValue(operand: pai) - - if pai.parentFunction.hasOwnership { - // Under OSSA, the closure acts as an owned value whose lifetime is a borrow scope for the captures, so we need to - // end the borrow scope before ending the lifetimes of the captures themselves. - self.createDestroyValue(operand: pai) - self.destroyCapturedArgs(for: pai) - } else { - self.destroyCapturedArgs(for: pai) - self.createDeallocStack(pai) - context.notifyInvalidatedStackNesting() + let recursionBudget = 8 + + // Recurse into called function + if let callee = apply.referencedFunction, + callee.isDefinition, + handledFuncs.insert(callee).inserted, + handledFuncs.count <= recursionBudget, + let calleeArg = apply.calleeArgument(of: use, in: callee) + { + if checkRecursivelyIfClosureIsApplied(calleeArg, &handledFuncs) { + return true + } } - } else { - if pai.parentFunction.hasOwnership { - self.createDestroyValue(operand: pai) - } else { - self.createReleaseValue(operand: pai) + + case is CopyValueInst, is MoveValueInst: + if checkRecursivelyIfClosureIsApplied(use.instruction as! SingleValueInstruction, &handledFuncs) { + return true } + + default: + break } } + + return false } -private extension FunctionConvention { - func getSpecializedParameters(basedOn callSite: CallSite) -> [ParameterInfo] { - let applySiteCallee = callSite.applyCallee - var specializedParamInfoList: [ParameterInfo] = [] +/// Add destroys for values which are not consumed, yet. +/// There are two cases of values for which we need this: +/// 1. guaranteed closure arguments +/// 2. operands of copies at the call site +/// +/// ``` +/// %1 = partial_apply %closure // is cloned to the specialized function +/// %2 = copy_value %1 // is cloned to the specialized function +/// apply %function(%2) : (@guaranteed () -> ()) -> () +/// destroy_value %1 +/// +/// sil @specializedFunction() -> () { +/// bb0: +/// %1 = partial_apply %closure +/// %2 = copy_value %1 +/// ... // body +/// destroy_value %2 // case 1: destroy for the guaranteed closure argument +/// destroy_value %1 // case 2: destroy for the operand of the copy +/// return +/// ``` +private func addMissingDestroysAtFunctionExits(for clonedArguments: [Value], _ context: FunctionPassContext) { + var needDestroy = ValueWorklist(context) + defer { needDestroy.deinitialize() } - // Start by adding all original parameters except for the closure parameters. - let firstParamIndex = applySiteCallee.argumentConventions.firstParameterIndex - for (index, paramInfo) in applySiteCallee.convention.parameters.enumerated() { - let argIndex = index + firstParamIndex - if !callSite.hasClosureArg(at: argIndex) { - specializedParamInfoList.append(paramInfo) - } - } + for clonedArg in clonedArguments { + findValuesWhichNeedDestroyRecursively(value: clonedArg, needDestroy: &needDestroy) + } - // Now, append parameters captured by each of the original closure parameter. - // - // Captured parameters are always appended to the function signature. If the argument type of the captured - // parameter in the callee is: - // - direct and trivial, pass the new parameter as Direct_Unowned. - // - direct and non-trivial, pass the new parameter as Direct_Owned. - // - indirect, pass the new parameter using the same parameter convention as in - // the original closure. - for closureArgDesc in callSite.closureArgDescriptors { - if let closure = closureArgDesc.closure as? PartialApplyInst { - let closureCallee = closureArgDesc.callee - let closureCalleeConvention = closureCallee.convention - let unappliedArgumentCount = closure.unappliedArgumentCount - closureCalleeConvention.indirectSILResultCount - - let prevCapturedParameters = - closureCalleeConvention - .parameters[unappliedArgumentCount...] - .enumerated() - .map { index, paramInfo in - let argIndexOfParam = closureCallee.argumentConventions.firstParameterIndex + unappliedArgumentCount + index - let argType = closureCallee.argumentTypes[argIndexOfParam] - return paramInfo.withSpecializedConvention(isArgTypeTrivial: argType.isTrivial(in: closureCallee)) - } - - specializedParamInfoList.append(contentsOf: prevCapturedParameters) - } + while let valueToDestroy = needDestroy.pop() { + Builder.insertCleanupAtFunctionExits(of: valueToDestroy.parentFunction, context) { builder in + builder.createDestroyValue(operand: valueToDestroy) } - - return specializedParamInfoList } } -private extension ParameterInfo { - func withSpecializedConvention(isArgTypeTrivial: Bool) -> Self { - let specializedParamConvention = self.convention.isAllowedIndirectConvForClosureSpec - ? self.convention - : isArgTypeTrivial ? ArgumentConvention.directUnowned : ArgumentConvention.directOwned - - return ParameterInfo(type: self.type, convention: specializedParamConvention, options: self.options, - hasLoweredAddresses: self.hasLoweredAddresses) +private func findValuesWhichNeedDestroyRecursively(value: Value, needDestroy: inout ValueWorklist) { + if let svi = value as? SingleValueInstruction { + for op in svi.operands { + findValuesWhichNeedDestroyRecursively(value: op.value, needDestroy: &needDestroy) + } } - - var isTrivialNoescapeClosure: Bool { - SILFunctionType_isTrivialNoescape(type.bridged) + if value.ownership == .owned && value.uses.endingLifetime.isEmpty { + needDestroy.pushIfNotVisited(value) } } -private extension ArgumentConvention { - var isAllowedIndirectConvForClosureSpec: Bool { - switch self { - case .indirectInout, .indirectInoutAliasable: - return true - default: - return false +private extension ParameterInfo { + func withSpecializedConvention(for partialApply: PartialApplyInst, in callee: Function) -> Self { + let argType = type.loweredType(in: partialApply.parentFunction) + let specializedParamConvention = if self.convention.isIndirect { + self.convention + } else { + if argType.isTrivial(in: callee) { + ArgumentConvention.directUnowned + } else { + if partialApply.isOnStack { + ArgumentConvention.directGuaranteed + } else { + ArgumentConvention.directOwned + } + } } + + return ParameterInfo( + type: self.type, convention: specializedParamConvention, options: self.options, + hasLoweredAddresses: self.hasLoweredAddresses) } } @@ -1112,10 +749,10 @@ private extension PartialApplyInst { /// pullback returned from an autodiff VJP var isPullbackInResultOfAutodiffVJP: Bool { if self.parentFunction.isAutodiffVJP, - let use = self.uses.singleUse, - let tupleInst = use.instruction as? TupleInst, - let returnInst = self.parentFunction.returnInstruction, - tupleInst == returnInst.returnedValue + let use = self.uses.singleUse, + let tupleInst = use.instruction as? TupleInst, + let returnInst = self.parentFunction.returnInstruction, + tupleInst == returnInst.returnedValue { return true } @@ -1124,47 +761,25 @@ private extension PartialApplyInst { } var isPartialApplyOfThunk: Bool { - if self.numArguments == 1, - let fun = self.referencedFunction, - fun.thunkKind == .reabstractionThunk || fun.thunkKind == .thunk, - self.arguments[0].type.isLoweredFunction, - self.arguments[0].type.isReferenceCounted(in: self.parentFunction) || self.callee.type.isThickFunction + if self.numArguments == 1, + let fun = self.referencedFunction, + fun.thunkKind == .reabstractionThunk || fun.thunkKind == .thunk, + self.arguments[0].type.isLoweredFunction, + self.arguments[0].type.isReferenceCounted(in: self.parentFunction) || self.callee.type.isThickFunction { return true } - return false } var hasOnlyInoutIndirectArguments: Bool { self.argumentOperands .filter { !$0.value.type.isObject } - .allSatisfy { self.convention(of: $0)!.isInout } - } -} - -private extension Instruction { - var asSupportedClosure: SingleValueInstruction? { - switch self { - case let tttf as ThinToThickFunctionInst where tttf.callee is FunctionRefInst: - return tttf - // TODO: figure out what to do with non-inout indirect arguments - // https://forums.swift.org/t/non-inout-indirect-types-not-supported-in-closure-specialization-optimization/70826 - case let pai as PartialApplyInst where pai.callee is FunctionRefInst && pai.hasOnlyInoutIndirectArguments: - return pai - default: - return nil - } + .allSatisfy { self.convention(of: $0)!.isInout } } - var isSupportedClosure: Bool { - asSupportedClosure != nil - } -} - -private extension ApplySite { - var calleeIsDynamicFunctionRef: Bool { - return !(callee is DynamicFunctionRefInst || callee is PreviousDynamicFunctionRefInst) + var allArgumentsCanBeCopied: Bool { + arguments.allSatisfy { !$0.type.isMoveOnly } } } @@ -1176,214 +791,3 @@ private extension Function { } } } - -// ===================== Utility Types ===================== // -private struct OrderedDict { - private var valueIndexDict: [Key: Int] = [:] - private var entryList: [(Key, Value)] = [] - - subscript(key: Key) -> Value? { - if let index = valueIndexDict[key] { - return entryList[index].1 - } - return nil - } - - mutating func insert(key: Key, value: Value) { - if valueIndexDict[key] == nil { - valueIndexDict[key] = entryList.count - entryList.append((key, value)) - } - } - - mutating func update(key: Key, value: Value) { - if let index = valueIndexDict[key] { - entryList[index].1 = value - } - } - - var keys: LazyMapSequence, Key> { - entryList.lazy.map { $0.0 } - } - - var values: LazyMapSequence, Value> { - entryList.lazy.map { $0.1 } - } -} - -private typealias CallSiteMap = OrderedDict - -private extension CallSiteMap { - var callSites: [CallSite] { - Array(self.values) - } -} - -/// Represents all the information required to represent a closure in isolation, i.e., outside of a callsite context -/// where the closure may be getting passed as an argument. -/// -/// Composed with other information inside a `ClosureArgDescriptor` to represent a closure as an argument at a callsite. -private struct ClosureInfo { - let closure: SingleValueInstruction - let lifetimeFrontier: [Instruction] - - init(closure: SingleValueInstruction, lifetimeFrontier: [Instruction]) { - self.closure = closure - self.lifetimeFrontier = lifetimeFrontier - } - -} - -/// Represents a closure as an argument at a callsite. -private struct ClosureArgDescriptor { - let closureInfo: ClosureInfo - /// The index of the closure in the callsite's argument list. - let closureArgumentIndex: Int - let parameterInfo: ParameterInfo - - var closure: SingleValueInstruction { - closureInfo.closure - } - var lifetimeFrontier: [Instruction] { - closureInfo.lifetimeFrontier - } - - var isPartialApplyOnStack: Bool { - if let pai = closure as? PartialApplyInst { - return pai.isOnStack - } - return false - } - - var callee: Function { - if let pai = closure as? PartialApplyInst { - return pai.referencedFunction! - } else { - return (closure as! ThinToThickFunctionInst).referencedFunction! - } - } - - var location: Location { - closure.location - } - - var closureArgIndex: Int { - closureArgumentIndex - } - - var closureParamInfo: ParameterInfo { - parameterInfo - } - - var numArguments: Int { - if let pai = closure as? PartialApplyInst { - return pai.numArguments - } else { - return 0 - } - } - - var arguments: LazyMapSequence { - if let pai = closure as? PartialApplyInst { - return pai.arguments - } - - return OperandArray.empty.lazy.map { $0.value } as LazyMapSequence - } - - var isClosureGuaranteed: Bool { - closureParamInfo.convention.isGuaranteed - } - - var isClosureConsumed: Bool { - closureParamInfo.convention.isConsumed - } -} - -/// Represents a callsite containing one or more closure arguments. -private struct CallSite { - let applySite: ApplySite - var closureArgDescriptors: [ClosureArgDescriptor] = [] - - init(applySite: ApplySite) { - self.applySite = applySite - } - - mutating func appendClosureArgDescriptor(_ descriptor: ClosureArgDescriptor) { - self.closureArgDescriptors.append(descriptor) - } - - var applyCallee: Function { - applySite.referencedFunction! - } - - var reachableExitBBsInCallee: [BasicBlock] { - applyCallee.blocks.filter { $0.isReachableExitBlock } - } - - func hasClosureArg(at index: Int) -> Bool { - closureArgDescriptors.contains { $0.closureArgumentIndex == index } - } - - func closureArgDesc(at index: Int) -> ClosureArgDescriptor? { - closureArgDescriptors.first { $0.closureArgumentIndex == index } - } - - func appliedArgForClosure(at index: Int) -> Value? { - if let closureArgDesc = closureArgDesc(at: index) { - return applySite.arguments[closureArgDesc.closureArgIndex - applySite.unappliedArgumentCount] - } - - return nil - } - - func specializedCalleeName(_ context: FunctionPassContext) -> String { - let closureArgs = Array(self.closureArgDescriptors.map { $0.closure }) - let closureIndices = Array(self.closureArgDescriptors.map { $0.closureArgIndex }) - - return context.mangle(withClosureArguments: closureArgs, closureArgIndices: closureIndices, - from: applyCallee) - } -} - -// ===================== Unit tests ===================== // - -let gatherCallSitesTest = FunctionTest("closure_specialize_gather_call_sites") { function, arguments, context in - print("Specializing closures in function: \(function.name)") - print("===============================================") - var callSites = gatherCallSites(in: function, context) - - callSites.forEach { callSite in - print("PartialApply call site: \(callSite.applySite)") - print("Passed in closures: ") - for index in callSite.closureArgDescriptors.indices { - var closureArgDescriptor = callSite.closureArgDescriptors[index] - print("\(index+1). \(closureArgDescriptor.closureInfo.closure)") - } - } - print("\n") -} - -let specializedFunctionSignatureAndBodyTest = FunctionTest( - "closure_specialize_specialized_function_signature_and_body") { function, arguments, context in - - var callSites = gatherCallSites(in: function, context) - - for callSite in callSites { - let (specializedFunction, _) = getOrCreateSpecializedFunction(basedOn: callSite, context) - print("Generated specialized function: \(specializedFunction.name)") - print("\(specializedFunction)\n") - } -} - -let rewrittenCallerBodyTest = FunctionTest("closure_specialize_rewritten_caller_body") { function, arguments, context in - var callSites = gatherCallSites(in: function, context) - - for callSite in callSites { - let (specializedFunction, _) = getOrCreateSpecializedFunction(basedOn: callSite, context) - rewriteApplyInstruction(using: specializedFunction, callSite: callSite, context) - - print("Rewritten caller body for: \(function.name):") - print("\(function)\n") - } -} diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeEscapeEffects.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeEscapeEffects.swift index 265254bf82035..3b2ab2c5e242c 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeEscapeEffects.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeEscapeEffects.swift @@ -72,7 +72,7 @@ let computeEscapeEffects = FunctionPass(name: "compute-escape-effects") { return } - context.modifyEffects(in: function) { (effects: inout FunctionEffects) in + function.modifyEffects(context) { (effects: inout FunctionEffects) in effects.escapeEffects.arguments = newEffects } } @@ -141,8 +141,8 @@ private func isOperandOfRecursiveCall(_ op: Operand) -> Bool { if let applySite = inst as? FullApplySite, let callee = applySite.referencedFunction, callee == inst.parentFunction, - let argIdx = applySite.calleeArgumentIndex(of: op), - op.value == callee.arguments[argIdx] { + let calleeArg = applySite.calleeArgument(of: op, in: callee), + op.value == calleeArg { return true } return false diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift index bdc76968e795f..aa5c8ef5ec691 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift @@ -37,40 +37,109 @@ let computeSideEffects = FunctionPass(name: "compute-side-effects") { return } - if function.effectAttribute != .none { - // Don't try to infer side effects if there are defined effect attributes. - return - } - var collectedEffects = CollectedEffects(function: function, context) - // First step: collect effects from all instructions. - // + // Collect effects from all instructions. for block in function.blocks { for inst in block.instructions { collectedEffects.addInstructionEffects(inst) } } - - // Second step: If an argument has unknown uses, we must add all previously collected - // global effects to the argument, because we don't know to which "global" side-effect - // instruction the argument might have escaped. + // If an argument has unknown uses, we must add all previously collected + // global effects to the argument, because we don't know to which "global" + // side-effect instruction the argument might have escaped. for argument in function.arguments { collectedEffects.addEffectsForEscapingArgument(argument: argument) collectedEffects.addEffectsForConsumingArgument(argument: argument) } + let globalEffects: SideEffects.GlobalEffects + do { + let computed = collectedEffects.globalEffects + + // Combine computed global effects with effects defined by the function's effect attribute, if it has one. + + // The defined and computed global effects of a function with an effect attribute should be treated as + // worst case global effects of the function. + // This means a global effect should only occur iff it is computed AND defined to occur. + + let defined = function.definedGlobalEffects + + globalEffects = SideEffects.GlobalEffects( + memory: SideEffects.Memory(read: defined.memory.read && computed.memory.read, + write: defined.memory.write && computed.memory.write), + ownership: SideEffects.Ownership(copy: defined.ownership.copy && computed.ownership.copy, + destroy: defined.ownership.destroy && computed.ownership.destroy), + allocates: defined.allocates && computed.allocates, + isDeinitBarrier: defined.isDeinitBarrier && computed.isDeinitBarrier + ) + } + + // Obtain the argument effects of the function. + var argumentEffects = collectedEffects.argumentEffects + + // `[readnone]` and `[readonly]` functions can still access the value fields + // of their indirect arguments, permitting v** read and write effects. If + // additional read or write effects are computed, we can replace. + switch function.effectAttribute { + case .readNone: + for i in (0.. () { +/// bb0(%0 : $Builtin.Int64): +/// ... +/// ``` +/// -> +/// ``` +/// %1 = function_ref @specialized_closure +/// %3 = partial_apply %1() +/// +/// sil @specialized_closure : $() -> () { +/// bb0: +/// %0 = integer_literal $Builtin.Int64, 27 +/// ... +/// ``` +/// +/// Also, optimizes away a `partial_apply` of a thunk to a closure where all partially applied +/// arguments are dead: +/// +/// ``` +/// %2 = function_ref @thunk +/// %3 = partial_apply %2(%1) +/// +/// sil @thunk : $(T, V) -> () { +/// bb0(%0 : $T, %1 : $V): // %1 is dead +/// %2 = function_ref @closure +/// %3 = apply %2(%0) // alive arguments are forwarded 1:1 +/// return %3 +/// } +/// ``` +/// -> +/// ``` +/// %2 = function_ref @closure +/// %3 = thin_to_thick_function %2 +/// ``` +/// +let constantCapturePropagation = FunctionPass(name: "constant-capture-propagation") { + (function: Function, context: FunctionPassContext) in + + for inst in function.instructions { + guard let partialApply = inst as? PartialApplyInst, + // Only support closures which - after generic specialization - are not generic anymore. + !partialApply.substitutionMap.replacementTypes.contains(where: { $0.hasArchetype }) + else { + continue + } + + if !context.continueWithNextSubpassRun(for: partialApply) { + return + } + + optimizeClosureWithDeadCaptures(of: partialApply, context) + + if partialApply.isDeleted { + continue + } + + constantPropagateCaptures(of: partialApply, context) + } +} + +private func optimizeClosureWithDeadCaptures(of partialApply: PartialApplyInst, _ context: FunctionPassContext) { + if let callee = getSpecializedCalleeWithDeadParams(of: partialApply, context) { + rewritePartialApply(partialApply, withSpecialized: callee, arguments: [], context) + } +} + +private func constantPropagateCaptures(of partialApply: PartialApplyInst, _ context: FunctionPassContext) { + guard let callee = partialApply.referencedFunction, + callee.isDefinition, + let (constArgs, nonConstArgs) = partialApply.classifyArgumentsForConstness() + else { + return + } + + let specializedName = context.mangle(withConstantCaptureArguments: constArgs.map { + (partialApply.calleeArgumentIndex(of: $0)!, $0.value) + }, + from: callee) + + let specializedCallee: Function + if let existing = context.lookupFunction(name: specializedName) { + specializedCallee = existing + } else { + specializedCallee = specializeClosure(specializedName: specializedName, + partialApply: partialApply, + constantArguments: constArgs, nonConstantArguments: nonConstArgs, + context) + } + if !partialApply.isOnStack { + // Escaping closures consume their arguments. Therefore we need to destroy the removed argument values. + addCompensatingDestroys(for: constArgs, context) + } + let newArguments = Array(nonConstArgs.values) + rewritePartialApply(partialApply, withSpecialized: specializedCallee, arguments: newArguments, context) +} + +private func getSpecializedCalleeWithDeadParams(of partialApply: PartialApplyInst, + _ context: FunctionPassContext +) -> Function? { + guard let specialized = partialApply.getCalleeOfForwardingThunkWithDeadCaptures(), + specialized.abi == .Swift + else { + return nil + } + + // Specialize the callee if it is generic + if partialApply.substitutionMap.hasAnySubstitutableParams { + guard specialized.isDefinition, + partialApply.referencedFunction!.shouldOptimize, specialized.shouldOptimize + else { + return nil + } + + let genericSpecialized = context.specialize(function: specialized, for: partialApply.substitutionMap, + convertIndirectToDirect: false, isMandatory: false) + return genericSpecialized + } + + return specialized +} + +private func specializeClosure(specializedName: String, + partialApply: PartialApplyInst, + constantArguments: [Operand], nonConstantArguments: [Operand], + _ context: FunctionPassContext +) -> Function { + let callee = partialApply.referencedFunction! + var newParams = [ParameterInfo]() + newParams.append(contentsOf: callee.convention.parameters.dropLast(partialApply.numArguments)) + newParams.append(contentsOf: nonConstantArguments.map { partialApply.parameter(for: $0)! }) + + let isGeneric = newParams.contains { $0.type.hasTypeParameter } || + callee.convention.results.contains { $0.type.hasTypeParameter() } || + callee.convention.errorResult?.type.hasTypeParameter() ?? false + + let specializedClosure = context.createSpecializedFunctionDeclaration(from: callee, + withName: specializedName, + withParams: newParams, + preserveGenericSignature: isGeneric) + + context.buildSpecializedFunction(specializedFunction: specializedClosure) { (specializedClosure, specContext) in + cloneAndSpecializeFunction(from: callee, toEmpty: specializedClosure, + substitutions: partialApply.substitutionMap, + specContext) + + let entryBlock = specializedClosure.entryBlock + for constArgOp in constantArguments { + cloneArgument(constArgOp, of: partialApply, to: specializedClosure, specContext) + } + // Erase the cloned arguments from the entry block. + for constArgOp in constantArguments.reversed() { + let calleeArgIdx = partialApply.calleeArgumentIndex(of: constArgOp)! + entryBlock.eraseArgument(at: calleeArgIdx, specContext) + } + } + context.notifyNewFunction(function: specializedClosure, derivedFrom: callee) + return specializedClosure +} + +private func cloneArgument(_ argumentOp: Operand, + of partialApply: PartialApplyInst, + to targetFunction: Function, + _ context: FunctionPassContext +) { + var argCloner = Cloner(cloneBefore: targetFunction.entryBlock.instructions.first!, context) + defer { argCloner.deinitialize() } + + let clonedArg = argCloner.cloneRecursively(value: argumentOp.value) + let calleeArgIdx = partialApply.calleeArgumentIndex(of: argumentOp)! + let calleeArg = targetFunction.arguments[calleeArgIdx] + calleeArg.uses.replaceAll(with: clonedArg, context) + + if partialApply.calleeArgumentConventions[calleeArgIdx].isGuaranteed { + // If the original argument was passed as guaranteed, i.e. is _not_ destroyed in the closure, we have + // to destroy the cloned argument at function exits. + Builder.insertCleanupAtFunctionExits(of: targetFunction, context) { builder in + builder.emitDestroy(of: clonedArg) + } + } + +} + +private func addCompensatingDestroys(for constantArguments: [Operand], _ context: FunctionPassContext) { + for argOp in constantArguments { + let builder = Builder(before: argOp.instruction, context) + builder.emitDestroy(of: argOp.value) + } +} + +private func rewritePartialApply(_ partialApply: PartialApplyInst, withSpecialized specialized: Function, + arguments: [Value], _ context: FunctionPassContext) { + let builder = Builder(before: partialApply, context) + let fri = builder.createFunctionRef(specialized) + let newClosure: Value + if arguments.isEmpty { + newClosure = builder.createThinToThickFunction(thinFunction: fri, resultType: partialApply.type) + context.erase(instructions: partialApply.uses.users(ofType: DeallocStackInst.self)) + } else { + newClosure = builder.createPartialApply( + function: fri, + substitutionMap: specialized.genericSignature.isEmpty ? SubstitutionMap() : partialApply.substitutionMap, + capturedArguments: arguments, calleeConvention: partialApply.calleeConvention, + hasUnknownResultIsolation: partialApply.hasUnknownResultIsolation, isOnStack: partialApply.isOnStack) + } + partialApply.uses.replaceAll(with: newClosure, context) + + // Bypass any mark_dependence on the captures we specialized away. + // + // TODO: If we start to specialize away key path literals with operands (subscripts etc.), then a + // dependence of the new partial_apply on those operands may still exist. However, we should still + // leave the key path itself out of the dependency chain, and introduce dependencies on those + // operands instead, so that the key path object itself can be made dead. + for md in newClosure.uses.users(ofType: MarkDependenceInst.self) { + if md.base.uses.getSingleUser(ofType: PartialApplyInst.self) == partialApply { + md.replace(with: newClosure, context) + } + } + context.erase(instruction: partialApply) +} + +private extension PartialApplyInst { + + /// Returns the callee if this is a `partial_apply` of a thunk which directly forwards all arguments + /// to the callee and has no other side-effects. + func getCalleeOfForwardingThunkWithDeadCaptures() -> Function? { + guard let thunk = referencedFunction, + let thunkEntryBlock = thunk.blocks.first + else { + return nil + } + let numDeadArguments = self.arguments.count + let numAliveArgs = thunkEntryBlock.arguments.count - numDeadArguments + let deadCalleeArgs = thunkEntryBlock.arguments.dropFirst(numAliveArgs) + + // TODO: handle non-trivial dead arguments, i.e. accept destroy instructions of such arguments. + guard deadCalleeArgs.allSatisfy({ $0.type.isTrivial(in: thunk )}) else { + return nil + } + + var callee: Function? = nil + var returnValue: Value? = nil + var errorValue: Value? = nil + + for inst in thunk.instructions { + switch inst { + case let apply as ApplyInst: + guard callee == nil, let c = apply.getCalleeWithForwardedArguments(numArguments: numAliveArgs) else { + return nil + } + callee = c + returnValue = apply + case let tryApply as TryApplyInst: + guard callee == nil, + let c = tryApply.getCalleeWithForwardedArguments(numArguments: numAliveArgs) + else { + return nil + } + callee = c + returnValue = tryApply.normalBlock.arguments.first + errorValue = tryApply.errorBlock.arguments.first + case let returnInst as ReturnInst: + guard let returnValue, returnInst.returnedValue == returnValue else { + return nil + } + case let throwInst as ThrowInst: + guard let errorValue, throwInst.thrownValue == errorValue else { + return nil + } + case is TermInst: + return nil + default: + if inst.mayHaveSideEffects { + return nil + } + } + } + return callee + } + + func classifyArgumentsForConstness() -> (constArguments: [Operand], nonConstArguments: [Operand])? { + var constArgs = [Operand]() + var nonConstArgs = [Operand]() + var hasKeypath = false + for argOp in argumentOperands { + // In non-OSSA we don't know where to insert the compensating release for a propagated keypath. + // Therefore bail if a keypath has multiple uses. + switch argOp.value.isConstant(requireSingleUse: !parentFunction.hasOwnership && !isOnStack) { + case .constant: + constArgs.append(argOp) + case .constantWithKeypath: + constArgs.append(argOp) + hasKeypath = true + case .notConstant: + nonConstArgs.append(argOp) + } + } + + // The optimization is beneficial if we can either get rid of all captures, because this + // avoids allocating the context. + // Or if we can constant propagate at least one keypath. Keypaths are so expensive that constant + // propagating a single keypath is already beneficial. + if !constArgs.isEmpty, + nonConstArgs.isEmpty || hasKeypath + { + return (constArgs, nonConstArgs) + } + return nil + } +} + +private extension FullApplySite { + func getCalleeWithForwardedArguments(numArguments: Int) -> Function? { + if let callee = referencedFunction, + callee.numArguments == numArguments, + zip(parentFunction.entryBlock.arguments, arguments).allSatisfy({ $0.0 == $0.1 }) + { + return callee + } + return nil + } +} + +private enum ConstantKind { + case notConstant + case constant + case constantWithKeypath + + func merge(with other: ConstantKind) -> ConstantKind { + switch (self, other) { + case (.notConstant, _): return .notConstant + case (_, .notConstant): return .notConstant + case (.constant, .constant): return .constant + default: return .constantWithKeypath + } + } +} + +private extension Value { + func isConstant(requireSingleUse: Bool) -> ConstantKind { + // All instructions handled here must also be handled in + // `FunctionSignatureSpecializationMangler::mangleConstantProp`. + let result: ConstantKind + switch self { + case let si as StructInst: + result = si.operands.reduce(.constant, { + $0.merge(with: $1.value.isConstant(requireSingleUse: requireSingleUse)) + }) + case is ThinToThickFunctionInst, is ConvertFunctionInst, is UpcastInst, is OpenExistentialRefInst: + result = (self as! UnaryInstruction).operand.value.isConstant(requireSingleUse: requireSingleUse) + case is StringLiteralInst, is IntegerLiteralInst, is FloatLiteralInst, is FunctionRefInst, is GlobalAddrInst: + result = .constant + case let keyPath as KeyPathInst: + guard keyPath.operands.isEmpty, + keyPath.hasPattern, + !keyPath.substitutionMap.hasAnySubstitutableParams + else { + return .notConstant + } + result = .constantWithKeypath + default: + return .notConstant + } + if requireSingleUse, result == .constantWithKeypath, !uses.ignoreDebugUses.isSingleUse { + return .notConstant + } + return result + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift index b9caf36d8b892..3e00c4eea75ab 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift @@ -39,7 +39,7 @@ import SIL /// ``` /// The optimization can be done if: -/// * In caseof a `load`: during the (forward-extended) lifetime of the loaded value the +/// * In case of a `load`: during the (forward-extended) lifetime of the loaded value the /// memory location is not changed. /// * In case of a `copy_value`: the (guaranteed) lifetime of the source operand extends /// the lifetime of the copied value. @@ -360,6 +360,13 @@ private extension Value { } var lookThroughForwardingInstructions: Value { + if let bfi = definingInstruction as? BorrowedFromInst, + !bfi.borrowedPhi.isReborrow, + bfi.enclosingValues.count == 1 + { + // Return the single forwarded enclosingValue + return bfi.enclosingValues[0] + } if let fi = definingInstruction as? ForwardingInstruction, let forwardedOp = fi.singleForwardedOperand { diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DeadStoreElimination.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DeadStoreElimination.swift index 26d765f47907f..ef7eb618e10f9 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DeadStoreElimination.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DeadStoreElimination.swift @@ -76,7 +76,7 @@ let deadStoreElimination = FunctionPass(name: "dead-store-elimination") { private func tryEliminate(store: StoreInst, complexityBudget: inout Int, _ context: FunctionPassContext) { // Check if the type can be expanded without a significant increase to code - // size. This pass splits values into its consitutent parts which effectively + // size. This pass splits values into its constituent parts which effectively // expands the value into projections which can increase code size. if !store.hasValidOwnershipForDeadStoreElimination || !store.source.type.shouldExpand(context) { return @@ -167,8 +167,15 @@ private extension StoreInst { var hasValidOwnershipForDeadStoreElimination: Bool { switch storeOwnership { - case .unqualified, .trivial: + case .unqualified: return true + case .trivial: + // Storing a trivial enum case in a non-trivial enum must be treated like a non-trivial + // init or assign, e.g. + // %1 = enum $Optional, #Optional.none!enumelt + // store %1 to [trivial] %0 // <- cannot delete this store! + // store %2 to [assign] %0 + return source.type.isTrivial(in: parentFunction) case .initialize, .assign: // In OSSA, non-trivial values cannot be dead-store eliminated because that could shrink // the lifetime of the original stored value (because it's not kept in memory anymore). @@ -256,7 +263,7 @@ private extension Deallocation { private extension DeallocStackInst { func isStackDeallocation(of base: AccessBase) -> Bool { - if case .stack(let allocStack) = base, allocstack == allocStack { + if case .stack(let allocStack) = base, operand.value == allocStack { return true } return false diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DeinitDevirtualizer.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DeinitDevirtualizer.swift index aa8553adefbc7..3682d78de5025 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DeinitDevirtualizer.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DeinitDevirtualizer.swift @@ -27,6 +27,8 @@ let deinitDevirtualizer = FunctionPass(name: "deinit-devirtualizer") { _ = devirtualizeDeinits(of: destroyValue, context) case let destroyAddr as DestroyAddrInst: _ = devirtualizeDeinits(of: destroyAddr, context) + case let builtin as BuiltinInst: + _ = devirtualizeDeinits(of: builtin, context) default: break } diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DestroyHoisting.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DestroyHoisting.swift index e240991749f64..42f521711cdbe 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DestroyHoisting.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DestroyHoisting.swift @@ -83,7 +83,7 @@ let destroyHoisting = FunctionPass(name: "destroy-hoisting") { private func optimize(value: Value, _ context: FunctionPassContext) { guard value.ownership == .owned, // Avoid all the analysis effort if there are no destroys to hoist. - !value.uses.filterUsers(ofType: DestroyValueInst.self).isEmpty + !value.uses.filterUses(ofType: DestroyValueInst.self).isEmpty else { return } diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DiagnoseInfiniteRecursion.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DiagnoseInfiniteRecursion.swift index 0e1d88425c583..84683e97bcced 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DiagnoseInfiniteRecursion.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DiagnoseInfiniteRecursion.swift @@ -289,7 +289,7 @@ private struct Analysis { worklist.pushIfNotVisited(function.entryBlock) while let block = worklist.pop() { if case .recursive(let apply) = block.getKind(for: invariants, context) { - context.diagnosticEngine.diagnose(apply.location.sourceLoc, .warn_infinite_recursive_call) + context.diagnosticEngine.diagnose(.warn_infinite_recursive_call, at: apply.location) } else { for succ in block.successors where isInInfiniteRecursionLoop(succ) { worklist.pushIfNotVisited(succ) diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift index 7dd89bffac9c1..2526ac706327d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift @@ -45,324 +45,587 @@ import SIL let initializeStaticGlobalsPass = FunctionPass(name: "initialize-static-globals") { (function: Function, context: FunctionPassContext) in - if context.hadError { - // In case of a preceding error, there is no guarantee that the SIL is valid. - return - } - - if !function.isGlobalInitOnceFunction { - return - } - - // Sometimes structs are not stored in one piece, but as individual elements. - // Merge such individual stores to a single store of the whole struct. - mergeStores(in: function, context) - - guard let (allocInst, storeToGlobal, inlineArrays) = getGlobalInitializerInfo(of: function, context) else { - return - } - - if !allocInst.global.canBeInitializedStatically { + guard // In case of a preceding error, there is no guarantee that the SIL is valid. + !context.hadError, + // Is `function` a global init function? + let global = function.initializedGlobal, + // Even if the init value is a constant, the initializer can have side effects, e.g. + // let g = { print("hello"); return 27 }() + !function.hasSideEffects(besideStoringTo: global), + // Try to get a constant global init value. + let initValue = GlobalInitValue(of: function, context) + else { return } - /// Replace inline arrays, which are allocated in stack locations with `vector` instructions. - /// Note that `vector` instructions are only allowed in global initializers. Therefore it's important - /// that the code in this global initializer is eventually completely removed after copying it to the global. - for array in inlineArrays { - lowerInlineArray(array: array, context) - } - - var cloner = StaticInitCloner(cloneTo: allocInst.global, context) - defer { cloner.deinitialize() } - - _ = cloner.clone(storeToGlobal.source) + initValue.materialize(into: global, from: function, context) // The initial value can contain a `begin_access` if it references another global variable by address, e.g. // var p = Point(x: 10, y: 20) // let o = UnsafePointer(&p) // - allocInst.global.stripAccessInstructionFromInitializer(context) + global.stripAccessInstructionFromInitializer(context) - context.erase(instruction: allocInst) - context.erase(instruction: storeToGlobal) - context.removeTriviallyDeadInstructionsIgnoringDebugUses(in: function) + function.removeAllInitializationCode(for: global, context) } -/// Gets all info about a global initializer function if it can be converted to a statically initialized global. -private func getGlobalInitializerInfo( - of function: Function, - _ context: FunctionPassContext -) -> (allocInst: AllocGlobalInst, storeToGlobal: StoreInst, inlineArrays: [InlineArray])? { - - var arrayInitInstructions = InstructionSet(context) - defer { arrayInitInstructions.deinitialize() } - - var inlineArrays = [InlineArray]() - - guard let (allocInst, storeToGlobal) = getGlobalInitialization(of: function, context, - handleUnknownInstruction: { inst in - if let asi = inst as? AllocStackInst { - if let array = getInlineArrayInfo(of: asi) { - inlineArrays.append(array) - arrayInitInstructions.insertAllAddressUses(of: asi) - return true +/// A tree, which represents a constant init value of a global variable. +private indirect enum GlobalInitValue { + case undefined + + // A value which is a SIL "constant", e.g. a literal instruction, a struct/tuple of literals, etc. + case constant(Value) + + // A struct, tuple or vector, which is _not_ a SIL "constant". + // For example, a struct or vector which is initialized by storing its elements. + case aggregate([GlobalInitValue]) + + // An enum case without a payload of an address-only enum. + case enumCase(caseIndex: Int) + + // An enum case which is not a SIL "constant", e.g. because it's address-only + case enumCaseWithPayload(caseIndex: Int, payload: GlobalInitValue) + + init?(of globalInitFunction: Function, _ context: FunctionPassContext) { + self = .undefined + var global: GlobalVariable? = nil + + // The init values for temporary stack locations. This is needed if e.g. a vector is initialized in a + // stack location and the loaded from it and stored in the global. + var stackValues = Dictionary() + + for inst in globalInitFunction.instructions { + switch inst { + case let allocGlobal as AllocGlobalInst: + guard global == nil, allocGlobal.global.canBeInitializedStatically else { + return nil } - return false + global = allocGlobal.global + case let ga as GlobalAddrInst where ga.global == global: + // SILGen only creates a single global_addr in the init function. So no need to support multiple ones. + guard case .undefined = self, + let selfInitVal = GlobalInitValue(of: ga, context) + else { + return nil + } + self = selfInitVal + case let allocStack as AllocStackInst: + guard let stackInitVal = GlobalInitValue(of: allocStack, context) else { + return nil + } + stackValues[allocStack] = stackInitVal + default: + break } - // Accept all instructions which are part of inline array initialization, because we'll remove them anyway. - return arrayInitInstructions.contains(inst) - }) - else { - return nil + } + + // At this point some `.constant` elements can be (or contain) loads from stack locations. + // Replace those elements with the init value of the corresponding stack location. + resolveLoads(from: &stackValues, context) + + if !isValid(context) { + return nil + } } - return (allocInst, storeToGlobal, inlineArrays) -} + private init?(of address: Value, _ context: FunctionPassContext) { + var builder = InitValueBuilder(originalAddress: address) + if builder.walkDownUses(ofAddress: address, path: UnusedWalkingPath()) == .abortWalk { + return nil + } + self = builder.initValue + } -/// Represents an inline array which is initialized by a literal. -private struct InlineArray { - let elementType: Type - - /// In case the `elementType` is a tuple, the element values are flattened, - /// i.e. `elements` contains elementcount * tupleelements values. - let elements: [Value] - - /// The final load instruction which loads the initialized array from a temporary stack location. - let finalArrayLoad: LoadInst - - /// The stack location which contains the initialized array. - var stackLoocation: AllocStackInst { finalArrayLoad.address as! AllocStackInst } -} + enum InitValue { + // The common case + case value(Value) + + // For payload-less cases of address-only enums. Such cases are initialized purely with an `inject_enum_addr`, + // and we don't have a `Value` which represents the resulting enum(-case). + case enumCaseWithoutPayload(InjectEnumAddrInst) -/// Replaces an initialized inline array (which is allocated in a temporary stack location) with a -/// `vector` instruction. -/// The stack location of the array is removed. -private func lowerInlineArray(array: InlineArray, _ context: FunctionPassContext) { - let vector: VectorInst - let builder = Builder(after: array.finalArrayLoad, context) - if array.elementType.isTuple { - let numTupleElements = array.elementType.tupleElements.count - assert(array.elements.count % numTupleElements == 0) - var tuples: [TupleInst] = [] - for tupleIdx in 0..<(array.elements.count / numTupleElements) { - let range = (tupleIdx * numTupleElements) ..< ((tupleIdx + 1) * numTupleElements) - let tuple = builder.createTuple(type: array.elementType, elements: Array(array.elements[range])) - tuples.append(tuple) + var parentFunction: Function { + switch self { + case .value(let value): return value.parentFunction + case .enumCaseWithoutPayload(let iea): return iea.parentFunction + } } - vector = builder.createVector(type: array.elementType, arguments: tuples) - } else { - vector = builder.createVector(type: array.elementType, arguments: array.elements) } - array.finalArrayLoad.uses.replaceAll(with: vector, context) - context.erase(instructionIncludingAllUsers: array.stackLoocation) -} -/// An alloc_stack could be a temporary object which holds an initialized inline-array literal. -/// It looks like: -/// -/// %1 = alloc_stack $InlineArray -/// %2 = unchecked_addr_cast %1 to $*ElementType // the elementStorage -/// store %firstElement to [trivial] %2 -/// %4 = integer_literal $Builtin.Word, 1 -/// %5 = index_addr %2, %4 -/// store %secondElement to [trivial] %5 -/// ... -/// %10 = load [trivial] %1 // the final arrayLoad -/// dealloc_stack %1 -/// -/// Returns nil if `allocStack` is not a properly initialized inline array. -/// -private func getInlineArrayInfo(of allocStack: AllocStackInst) -> InlineArray? { - var arrayLoad: LoadInst? = nil - var elementStorage: UncheckedAddrCastInst? = nil - - for use in allocStack.uses { - switch use.instruction { - case let load as LoadInst: - if arrayLoad != nil { - return nil + // Sets an element in the constant tree. + // Returns true if this was successful. One reason for being not successful is if a certain + // element is set twice, i.e. does not have a single defined value. + mutating func setElement(to value: InitValue, at path: SmallProjectionPath, type: Type) -> Bool { + let (kind, index, subPath) = path.pop() + switch kind { + case .root: + guard case .undefined = self else { + // The element was set twice. + return false } - // It's guaranteed that the array load is located after all element stores. - // Otherwise it would load uninitialized memory. - arrayLoad = load - case is DeallocStackInst: - break - case let addrCastToElement as UncheckedAddrCastInst: - if elementStorage != nil { - return nil + switch value { + case .value(let value): + self = .constant(value) + case .enumCaseWithoutPayload: + fatalError("should have been handled in the .enumCase of the SmallProjectionPath below") } - elementStorage = addrCastToElement + return true + + case .enumCase: + return setEnumCase(to: value, at: subPath, index: index, type: type) + + case .structField: + guard let structFields = type.getNominalFields(in: value.parentFunction) else { + return false + } + return setField(to: value, at: subPath, index: index, type: structFields[index], numFields: structFields.count) + + case .tupleField: + let tupleElements = type.tupleElements + return setField(to: value, at: subPath, index: index, type: tupleElements[index], numFields: tupleElements.count) + + case .vectorBase: + let elementType = type.builtinFixedArrayElementType(in: value.parentFunction) + guard let vectorSize = type.builtinFixedArraySizeType.valueOfInteger else { + return false + } + let (indexKind, vectorIndex, vectorSubPath) = subPath.pop() + switch indexKind { + case .indexedElement: + return setField(to: value, at: vectorSubPath, index: vectorIndex, type: elementType, numFields: vectorSize) + case .anyIndexedElement: + // A non-constant index means: all elements are initialized with the same value. + // (that's what we checked in `storesToAllVectorElements`) + for i in 0..() - if !findArrayElementStores(toElementAddress: elementStorage, elementIndex: 0, stores: &stores) { - return nil - } - if stores.isEmpty { - // We cannot create an empty `vector` instruction, therefore we don't support empty inline arrays. - return nil - } - // Usually there must be a store for each element. Otherwise the `arrayLoad` would load uninitialized memory. - // We still check this to not crash in some weird corner cases, like the element type is an empty tuple. - if stores.contains(nil) { - return nil + + private mutating func setField( + to value: InitValue, at path: SmallProjectionPath, + index: Int, type: Type, numFields: Int + ) -> Bool { + if case .undefined = self { + // Initialize the aggregate array if not done, yet. + self = .aggregate(Array(repeating: GlobalInitValue.undefined, count: numFields)) + } + if case .aggregate(let fields) = self { + var newFields = fields + self = .undefined // avoid copy-on-write + if !newFields[index].setElement(to: value, at: path, type: type) { + return false + } + self = .aggregate(newFields) + return true + } + return false } - return InlineArray(elementType: elementStorage.type.objectType, - elements: stores.map { $0!.source }, - finalArrayLoad: arrayLoad) -} + private mutating func setEnumCase(to value: InitValue, at path: SmallProjectionPath, index: Int, type: Type) -> Bool { + if path.isEmpty, case .enumCaseWithoutPayload(let iea) = value { -/// Recursively traverses all uses of `elementAddr` and finds all stores to an inline array storage. -/// The element store instructions are put into `stores` - one store for each element. -/// In case the element type is a tuple, the tuples are flattened. See `InlineArray.elements`. -private func findArrayElementStores( - toElementAddress elementAddr: Value, - elementIndex: Int, - stores: inout [StoreInst?] -) -> Bool { - for use in elementAddr.uses { - switch use.instruction { - case let indexAddr as IndexAddrInst: - guard let indexLiteral = indexAddr.index as? IntegerLiteralInst, - let tailIdx = indexLiteral.value else - { + guard case .undefined = self else { + // The enum was set twice. return false } - if !findArrayElementStores(toElementAddress: indexAddr, elementIndex: elementIndex + tailIdx, stores: &stores) { + assert(index == iea.caseIndex) + self = .enumCase(caseIndex: index) + } else { + guard let payloadType = type.getEnumCases(in: value.parentFunction)!.getPayloadType(ofCaseIndex: index) else { return false } - case let tea as TupleElementAddrInst: - // The array elements are tuples. There is a separate store for each tuple element. - let numTupleElements = tea.tuple.type.tupleElements.count - let tupleIdx = tea.fieldIndex - if !findArrayElementStores(toElementAddress: tea, - elementIndex: elementIndex * numTupleElements + tupleIdx, - stores: &stores) { + switch self { + case .undefined: + // It's the first time we set the payload or a sub-field of it. + var payload = GlobalInitValue.undefined + if !payload.setElement(to: value, at: path, type: payloadType) { + return false + } + self = .enumCaseWithPayload(caseIndex: index, payload: payload) + case .enumCaseWithPayload(let existingIndex, var payload) where index == existingIndex: + // Some sub-field of the enum-payload was already set. + self = .undefined // avoid copy-on-write + if !payload.setElement(to: value, at: path, type: payloadType) { + return false + } + self = .enumCaseWithPayload(caseIndex: index, payload: payload) + default: return false } - case let store as StoreInst: - if store.source.type.isTuple { - // This kind of SIL is never generated because tuples are stored with separated stores to tuple_element_addr. - // Just to be on the safe side.. - return false + } + return true + } + + /// Creates SIL for this global init value in the initializer of the `global`. + func materialize(into global: GlobalVariable, from function: Function, _ context: FunctionPassContext) { + var cloner = Cloner(cloneToGlobal: global, context) + defer { cloner.deinitialize() } + let builder = Builder(staticInitializerOf: global, context) + + _ = materializeRecursively(type: global.type, &cloner, builder, function) + } + + private func materializeRecursively( + type: Type, + _ cloner: inout Cloner, + _ builder: Builder, + _ function: Function + ) -> Value { + switch self { + case .undefined: + fatalError("cannot materialize undefined init value") + + case .constant(let value): + return cloner.cloneRecursively(globalInitValue: value) + + case .aggregate(let fields): + if type.isStruct { + let fieldTypes = type.getNominalFields(in: function)! + let fieldValues = zip(fields, fieldTypes).map { + $0.0.materializeRecursively(type: $0.1, &cloner, builder, function) + } + return builder.createStruct(type: type, elements: fieldValues) } - if elementIndex >= stores.count { - stores += Array(repeating: nil, count: elementIndex - stores.count + 1) + if type.isTuple { + let elementValues = zip(fields, type.tupleElements).map { + $0.0.materializeRecursively(type: $0.1, &cloner, builder, function) + } + return builder.createTuple(type: type, elements: elementValues) } - if stores[elementIndex] != nil { - // An element is stored twice. - return false + assert(type.isBuiltinFixedArray) + let elementType = type.builtinFixedArrayElementType(in: function) + let elementValues = fields.map { + $0.materializeRecursively(type: elementType, &cloner, builder, function) } - stores[elementIndex] = store - default: + return builder.createVector(type: type, arguments: elementValues) + + case .enumCase(let caseIndex): + return builder.createEnum(caseIndex: caseIndex, payload: nil, enumType: type) + + case .enumCaseWithPayload(let caseIndex, let payload): + let payloadType = type.getEnumCases(in: function)!.getPayloadType(ofCaseIndex: caseIndex)! + let payloadValue = payload.materializeRecursively(type: payloadType, &cloner, builder, function) + return builder.createEnum(caseIndex: caseIndex, payload: payloadValue, enumType: type) + } + } + + // Replace `.constant` elements, which are (or contain) loads, init value of the corresponding stack location. + // Some from stack locations. + mutating func resolveLoads( + from stackValues: inout Dictionary, + _ context: FunctionPassContext + ) { + var resolvedAllocStacks = InstructionSet(context) + defer { resolvedAllocStacks.deinitialize() } + resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context) + } + + private mutating func resolveLoadsRecursively( + from stackValues: inout Dictionary, + _ resolvedAllocStacks: inout InstructionSet, + _ context: FunctionPassContext + ) { + switch self { + case .undefined, .enumCase: + break + case .constant(let value): + if value.containsLoad(context) { + switch value { + case is StructInst, is TupleInst: + self = .aggregate((value as! Instruction).operands.lazy.map { .constant($0.value) }) + resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context) + case let ei as EnumInst: + self = .enumCaseWithPayload(caseIndex: ei.caseIndex, payload: .constant(ei.payload!)) + resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context) + case let li as LoadInst: + guard let allocStack = li.address as? AllocStackInst, + var stackInit = stackValues[allocStack] + else { + self = .undefined + return + } + if resolvedAllocStacks.insert(allocStack) { + stackInit.resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context) + stackValues[allocStack] = stackInit + } + self = stackInit + default: + break + } + } + case .aggregate(let fields): + var newFields = fields + self = .undefined // avoid copy-on-write + for i in 0.. Bool { + switch self { + case .undefined: return false + case .constant(let value): + return value.isValidGlobalInitValue(context) + case .aggregate(let fields): + return fields.allSatisfy { $0.isValid(context) } + case .enumCase: + return true + case .enumCaseWithPayload(_, let payload): + return payload.isValid(context) } } - return true } -/// Merges stores to individual struct fields to a single store of the whole struct. -/// -/// store %element1 to %element1Addr -/// store %element2 to %element2Addr -/// -> -/// %s = struct $S (%element1, %element2) -/// store %s to @structAddr -private func mergeStores(in function: Function, _ context: FunctionPassContext) { - for inst in function.instructions { - if let store = inst as? StoreInst { - if let (elementStores, lastStore) = getSequenceOfElementStores(firstStore: store) { - merge(elementStores: elementStores, lastStore: lastStore, context) +private extension Value { + /// Returns true if this value is a `load` or a struct/tuple/enum with a `load` operand. + func containsLoad(_ context: FunctionPassContext) -> Bool { + var worklist = ValueWorklist(context) + defer { worklist.deinitialize() } + worklist.pushIfNotVisited(self) + while let v = worklist.pop() { + switch v { + case is LoadInst: + return true + case is StructInst, is TupleInst: + worklist.pushIfNotVisited(contentsOf: (v as! Instruction).operands.values) + case let ei as EnumInst: + if let payload = ei.payload { + worklist.pushIfNotVisited(payload) + } + default: + break } } + return false } } -/// Returns a sequence of individual stores to elements of a struct. -/// -/// %addr1 = struct_element_addr %structAddr, #field1 -/// store %element1 to %addr1 -/// // ... -/// %addr_n = struct_element_addr %structAddr, #field_n -/// store %element_n to %addr_n -/// -private func getSequenceOfElementStores(firstStore: StoreInst) -> ([StoreInst], lastStore: StoreInst)? { - guard let elementAddr = firstStore.destination as? StructElementAddrInst else { - return nil - } - let structAddr = elementAddr.struct - let structType = structAddr.type - if structType.isMoveOnly { - return nil - } - if (structType.nominal as! StructDecl).hasUnreferenceableStorage { - return nil - } - guard let fields = structType.getNominalFields(in: firstStore.parentFunction) else { - return nil - } - let numElements = fields.count - var elementStores = Array(repeating: nil, count: numElements) - var numStoresFound = 0 +private struct InitValueBuilder: AddressDefUseWalker { + let originalAddress: Value + var initValue = GlobalInitValue.undefined - for inst in InstructionList(first: firstStore) { - switch inst { + mutating func leafUse(address: Operand, path: UnusedWalkingPath) -> WalkResult { + switch address.instruction { case let store as StoreInst: - guard store.storeOwnership == .trivial, - let sea = store.destination as? StructElementAddrInst, - sea.struct == structAddr, - // Multiple stores to the same element? - elementStores[sea.fieldIndex] == nil else { - return nil + let accessPath = store.destination.lookThroughRawLayoutAddress.constantAccessPath + switch accessPath.base { + case .global, .stack: + if !initValue.setElement(to: .value(store.source), at: accessPath.projectionPath, type: originalAddress.type) { + return .abortWalk + } + return .continueWalk + case .index(let indexAddr): + // If we have a non-constant index in the access path, check if this is from a loop which + // initializes all elements. + guard store.storesToAllVectorElements(using: indexAddr) else { + return .abortWalk + } + let nonConstAccessPath = store.destination.accessPath + guard indexAddr.base.constantAccessPath.base == nonConstAccessPath.base else { + return .abortWalk + } + // The `nonConstAccessPath` now contains a single `.anyIndexedElement`. + if !initValue.setElement(to: .value(store.source), at: nonConstAccessPath.projectionPath, type: originalAddress.type) { + return .abortWalk + } + return .continueWalk + default: + fatalError("could not compute access path") + } + case let injectEnum as InjectEnumAddrInst: + if injectEnum.element.hasAssociatedValues { + if !injectEnum.operand.value.type.isLoadable(in: injectEnum.parentFunction) { + // TODO: we don't support non-loadable enum cases with payload yet, because IRGen support is missing. + // e.g. `var global: Atomic? = Atomic(0)` + // FixedTypeInfo (= used for non-loadable types) is missing the ability to pack a payload into an enum. + return .abortWalk + } + return .continueWalk + } + let accessPath = injectEnum.enum.getAccessPath(fromInitialPath: SmallProjectionPath(.enumCase, + index: injectEnum.caseIndex)) + switch accessPath.base { + case .global, .stack: + if !initValue.setElement(to: .enumCaseWithoutPayload(injectEnum), at: accessPath.projectionPath, type: originalAddress.type) { + return .abortWalk + } + return .continueWalk + default: + return .abortWalk } - elementStores[sea.fieldIndex] = store - numStoresFound += 1 - if numStoresFound == numElements { - // If we saw `numElements` distinct stores, it implies that all elements in `elementStores` are not nil. - return (elementStores.map { $0! }, lastStore: store) + case is LoadInst, is DeallocStackInst: + return .continueWalk + case let bi as BuiltinInst: + switch bi.id { + case .PrepareInitialization: + return .continueWalk + case .AddressOfRawLayout: + if let addr2Ptr = bi.uses.getSingleUser(ofType: PointerToAddressInst.self) { + return walkDownUses(ofAddress: addr2Ptr, path: path) + } + return .abortWalk + default: + return .abortWalk } default: - if inst.mayReadOrWriteMemory { - return nil - } + return .abortWalk } } - return nil } -private func merge(elementStores: [StoreInst], lastStore: StoreInst, _ context: FunctionPassContext) { - let builder = Builder(after: lastStore, context) +private extension StoreInst { + func storesToAllVectorElements(using indexAddr: IndexAddrInst) -> Bool { + if let vectorBase = indexAddr.base as? VectorBaseAddrInst, + let headerBr = vectorBase.parentBlock.terminator as? BranchInst, + headerBr.targetBlock == parentBlock, + let vectorSize = vectorBase.vector.type.builtinFixedArraySizeType.valueOfInteger, + let (start, loopCount, increment) = getLoopInductionInfo(of: indexAddr.index.lookThroughIndexScalarCast), + start == 0, loopCount == vectorSize, increment == 1 + { + return true + } + return false + } +} - let structAddr = (lastStore.destination as! StructElementAddrInst).struct - let str = builder.createStruct(type: structAddr.type.objectType, elements: elementStores.map { $0.source }) - builder.createStore(source: str, destination: structAddr, ownership: lastStore.storeOwnership) +/// Matches the pattern: +/// ``` +/// %startValue = integer_literal +/// %incrementValue = integer_literal +/// %loopEndValue = integer_literal +/// br loopBlock(%startValue) +/// loopBlock(%inductionVar): +/// %add = builtin "sadd_with_overflow"(%inductionVar, %incrementValue, ...) +/// %incrementedInductionVar = tuple_extract %add, 0 +/// %loopBreakCondition = builtin "cmp_eq"(%incrementedInductionVar, %loopEndValue) +/// cond_br %loopBreakCondition, exitBlock, backEdgeBlock +/// backEdgeBlock: +/// br loopBlock(%incrementedInductionVar) +/// exitBlock: +/// ``` +private func getLoopInductionInfo(of inductionVar: Value) -> (start: Int, loopCount: Int, increment: Int)? { + let loopBlock = inductionVar.parentBlock + if let inductionVarArg = inductionVar as? Argument, inductionVarArg.parentBlock == loopBlock, + let inductionVarPhi = Phi(inductionVarArg), + let condBr = loopBlock.terminator as? CondBranchInst, + let loopBreakCondition = condBr.condition as? BuiltinInst, loopBreakCondition.id == .ICMP_EQ, + let incrementedInductionVar = loopBreakCondition.arguments[0] as? TupleExtractInst, + incrementedInductionVar.fieldIndex == 0, + let add = incrementedInductionVar.tuple as? BuiltinInst, add.id == .SAddOver, + add.arguments[0] == inductionVarArg, + let incrementValue = add.arguments[1] as? IntegerLiteralInst, + let increment = incrementValue.value, + let loopEndValue = loopBreakCondition.arguments[1] as? IntegerLiteralInst, + let loopCount = loopEndValue.value, + let backEdgeBranch = condBr.falseBlock.terminator as? BranchInst, + backEdgeBranch.targetBlock == loopBlock, + inductionVarPhi.incomingOperand(inPredecessor: backEdgeBranch.parentBlock).value == incrementedInductionVar, + let startValue = inductionVarPhi.incomingValue(notInBlock: backEdgeBranch.parentBlock) as? IntegerLiteralInst, + let start = startValue.value + { + return (start, loopCount, increment) + } + return nil +} - for store in elementStores { - let destAddr = store.destination as! StructElementAddrInst - context.erase(instruction: store) - if destAddr.uses.isEmpty { - context.erase(instruction: destAddr) +private extension Phi { + func incomingValue(notInBlock: BasicBlock) -> Value? { + if let block = predecessors.lazy.filter({ $0 != notInBlock }).singleElement { + return incomingOperand(inPredecessor: block).value } + return nil } } -private extension InstructionSet { - mutating func insertAllAddressUses(of value: Value) { - for use in value.uses { - if insert(use.instruction) { - for result in use.instruction.results where result.type.isAddress { - insertAllAddressUses(of: result) - } +private extension Function { + func hasSideEffects(besideStoringTo global: GlobalVariable) -> Bool { + return instructions.contains { inst in + switch inst { + case is DeallocStackInst, is DebugStepInst, is DebugValueInst, is BeginAccessInst, is EndAccessInst: + return false + case let alloc as AllocGlobalInst where alloc.global == global: + return false + case let store as StoreInst: + return !store.destination.lookThroughRawLayoutAddress.isAddressOfStack(orGlobal: global) + case let injectEnum as InjectEnumAddrInst: + return !injectEnum.enum.isAddressOfStack(orGlobal: global) + case let bi as BuiltinInst where bi.id == .PrepareInitialization: + return false + default: + return inst.hasUnspecifiedSideEffects } } } + + func removeAllInitializationCode(for global: GlobalVariable, _ context: FunctionPassContext) { + for inst in instructions { + switch inst { + case let allocGlobal as AllocGlobalInst where allocGlobal.global == global: + context.erase(instruction: allocGlobal) + case let globalAddr as GlobalAddrInst where globalAddr.global == global: + context.erase(instructionIncludingAllUsers: globalAddr) + default: + break + } + } + context.removeTriviallyDeadInstructionsIgnoringDebugUses(in: self) + } +} + +private extension Value { + func isAddressOfStack(orGlobal global: GlobalVariable) -> Bool { + switch accessBase { + case .global(let g) where g == global: + return true + case .stack: + return true + default: + return false + } + } + + /// Looks through a `@_rawLayout` projection, which "type casts" a raw-layout struct to it's content, which + /// must match the like-type of the raw-layout, e.g. + /// ``` + /// @_rawLayout(like: T) + /// struct S {} + /// + /// %2 = builtin "addressOfRawLayout"(%1 : $*S) : $Builtin.RawPointer + /// %3 = pointer_to_address %2 to $*T + /// ``` + var lookThroughRawLayoutAddress: Value { + if let ptr2Addr = self as? PointerToAddressInst, + let builtin = ptr2Addr.pointer as? BuiltinInst, builtin.id == .AddressOfRawLayout, + let likeType = builtin.arguments[0].type.rawLayoutSubstitutedLikeType, + builtin.arguments[0].type.rawLayoutSubstitutedCountType == nil, + likeType.canonical == type.canonicalType + { + return builtin.arguments[0] + } + return self + } +} + +private extension EnumCases { + func getPayloadType(ofCaseIndex caseIndex: Int) -> Type? { + return first(where: { $0.index == caseIndex })!.payload + } } diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LetPropertyLowering.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LetPropertyLowering.swift index 2f1e5d3cec5a5..ad456de533ef8 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LetPropertyLowering.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LetPropertyLowering.swift @@ -122,6 +122,8 @@ private func insertEndInitInstructions( use.set(to: ssaUpdater.getValue(atEndOf: use.instruction.parentBlock), context) } } + // This peephole optimization is required to avoid ownership errors. + replacePhisWithIncomingValues(phis: ssaUpdater.insertedPhis, context) } private func constructLetInitRegion( @@ -136,7 +138,7 @@ private func constructLetInitRegion( // root-class initializer). initRegion.insert(markUninitialized) - var borrows = Stack(context) + var borrows = Stack(context) defer { borrows.deinitialize() } for inst in markUninitialized.parentFunction.instructions { @@ -153,7 +155,7 @@ private func constructLetInitRegion( case let copy as CopyAddrInst where copy.destination.isLetFieldAddress(of: markUninitialized): - assert(copy.isInitializationOfDest) + assert(copy.isInitializationOfDestination) initRegion.insert(inst) case let beginAccess as BeginAccessInst diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift index 679a3ae276c59..b7bd49bb7923a 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift @@ -53,7 +53,7 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass( } } for instruction in function.instructions { - if let markDep = instruction as? MarkDependenceInst, markDep.isUnresolved { + if let markDep = instruction as? MarkDependenceInstruction, markDep.isUnresolved { if let lifetimeDep = LifetimeDependence(markDep, context) { if analyze(dependence: lifetimeDep, context) { // Note: This promotes the mark_dependence flag but does not invalidate analyses; preserving analyses is good, @@ -66,18 +66,22 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass( // For now, if the mark_dependence wasn't recognized as a lifetime dependency, or if the dependencies uses are not // in scope, conservatively settle it as escaping. For example, it is not uncommon for the pointer value returned // by `unsafeAddress` to outlive its `self` argument. This will not be diagnosed as an error, but the - // mark_dependence will hanceforth be treated as an unknown use by the optimizer. In the future, we should not + // mark_dependence will henceforth be treated as an unknown use by the optimizer. In the future, we should not // need to set this flag during diagnostics because, for escapable types, mark_dependence [unresolved] will all be // settled during an early LifetimeNormalization pass. - markDep.settleToEscaping() + markDep.settleToEscaping(context) continue } - if let apply = instruction as? FullApplySite { - // Handle ~Escapable results that do not have a lifetime dependence. This includes implicit initializers and - // @_unsafeNonescapableResult. + if let apply = instruction as? FullApplySite, !apply.hasResultDependence { + // Handle ~Escapable results that do not have a lifetime dependence. This includes implicit initializers, calls to + // closures, and @_unsafeNonescapableResult. apply.resultOrYields.forEach { - if let lifetimeDep = LifetimeDependence(unsafeApplyResult: $0, - context) { + if let lifetimeDep = LifetimeDependence(unsafeApplyResult: $0, apply: apply, context) { + _ = analyze(dependence: lifetimeDep, context) + } + } + apply.indirectResultOperands.forEach { + if let lifetimeDep = LifetimeDependence(unsafeApplyResult: $0.value, apply: apply, context) { _ = analyze(dependence: lifetimeDep, context) } } @@ -106,6 +110,20 @@ private func analyze(dependence: LifetimeDependence, _ context: FunctionPassCont } } + // Check for immortal dependence. + switch dependence.scope { + case .global: + log("Immortal global dependence.") + return true + case let .unknown(value): + if value.type.isVoid { + log("Immortal void dependence.") + return true + } + default: + break + } + // Compute this dependence scope. var range = dependence.computeRange(context) defer { range?.deinitialize() } @@ -135,13 +153,13 @@ private struct DiagnoseDependence { func diagnose(_ position: SourceLoc?, _ id: DiagID, _ args: DiagnosticArgument...) { - context.diagnosticEngine.diagnose(position, id, args) + context.diagnosticEngine.diagnose(id, arguments: args, at: position) } /// Check that this use is inside the dependence scope. func checkInScope(operand: Operand) -> WalkResult { if let range, !range.inclusiveRangeContains(operand.instruction) { - log(" out-of-range: \(operand.instruction)") + log(" out-of-range error: \(operand.instruction)") reportError(operand: operand, diagID: .lifetime_outside_scope_use) return .abortWalk } @@ -150,7 +168,7 @@ private struct DiagnoseDependence { } func reportEscaping(operand: Operand) { - log(" escaping: \(operand.instruction)") + log(" escaping error: \(operand.instruction)") reportError(operand: operand, diagID: .lifetime_outside_scope_escape) } @@ -195,19 +213,46 @@ private struct DiagnoseDependence { if function.hasUnsafeNonEscapableResult { return .continueWalk } - // If the dependence scope is global, then it has immortal lifetime. - if case .global = dependence.scope { + // Check for immortal lifetime. + // + // FIXME: remove this immortal check. It should be redundant with the earlier check that bypasses dependence + // diagnostics. + switch dependence.scope { + case .global: return .continueWalk + case let .unknown(value): + if value.type.isVoid { + return .continueWalk + } + default: + break } // Check that the parameter dependence for this result is the same // as the current dependence scope. if let arg = dependence.scope.parentValue as? FunctionArgument, - function.argumentConventions[resultDependsOn: arg.index] != nil { - // The returned value depends on a lifetime that is inherited or - // borrowed in the caller. The lifetime of the argument value - // itself is irrelevant here. - log(" has dependent function result") - return .continueWalk + let argDep = function.argumentConventions[resultDependsOn: arg.index] { + switch argDep { + case .inherit: + if dependence.markDepInst != nil { + // A mark_dependence represents a "borrow" scope. A local borrow scope cannot inherit the caller's dependence + // because the borrow scope depends on the argument value itself, while the caller allows the result to depend + // on a value that the argument was copied from. + break + } + fallthrough + case .scope: + // The returned value depends on a lifetime that is inherited or + // borrowed in the caller. The lifetime of the argument value + // itself is irrelevant here. + log(" has dependent function result") + return .continueWalk + } + // Briefly (April 2025), RawSpan._extracting, Span._extracting, and UTF8Span.span returned a borrowed value that + // depended on a copied argument. Continue to support those interfaces. The implementations were correct but + // needed an explicit _overrideLifetime. + if let sourceFileKind = dependence.function.sourceFileKind, sourceFileKind == .interface { + return .continueWalk + } } return .abortWalk } @@ -220,38 +265,57 @@ private struct DiagnoseDependence { onError() // Identify the escaping variable. - let escapingVar = LifetimeVariable(dependent: operand.value, context) - let varName = escapingVar.name - if let varName { - diagnose(escapingVar.sourceLoc, .lifetime_variable_outside_scope, - varName) + let escapingVar = LifetimeVariable(usedBy: operand, context) + if let varDecl = escapingVar.varDecl { + // Use the variable location, not the access location. + // Variable names like $return_value and $implicit_value don't have source locations. + let sourceLoc = varDecl.nameLoc ?? escapingVar.sourceLoc + diagnose(sourceLoc, .lifetime_variable_outside_scope, escapingVar.name ?? "") + } else if let sourceLoc = escapingVar.sourceLoc { + diagnose(sourceLoc, .lifetime_value_outside_scope) } else { - diagnose(escapingVar.sourceLoc, .lifetime_value_outside_scope) + // Always raise an error even if we can't find a source location. + let sourceLoc = function.location.sourceLoc + if let accessorKind = escapingVar.accessorKind { + diagnose(sourceLoc, .lifetime_value_outside_accessor, accessorKind) + } else { + // Thunks do not have a source location, but we try to use the function location anyway. + let thunkSelect = dependence.function.thunkKind == .noThunk ? 0 : 1 + diagnose(sourceLoc, .lifetime_value_outside_thunk, thunkSelect, function.name) + } } + diagnoseImplicitFunction() reportScope() // Identify the use point. - let userSourceLoc = operand.instruction.location.sourceLoc - diagnose(userSourceLoc, diagID) + if let userSourceLoc = operand.instruction.location.sourceLoc { + diagnose(userSourceLoc, diagID) + } } - // Identify the dependence scope. + // Identify the dependence scope. If no source location is found, bypass this diagnostic. func reportScope() { - if case let .access(beginAccess) = dependence.scope { - let parentVar = LifetimeVariable(dependent: beginAccess, context) - if let sourceLoc = beginAccess.location.sourceLoc ?? parentVar.sourceLoc { - diagnose(sourceLoc, .lifetime_outside_scope_access, - parentVar.name ?? "") - } + let parentVar = LifetimeVariable(definedBy: dependence.parentValue, context) + // First check if the dependency is limited to an access scope. If the access has no source location then + // fall-through to report possible dependence on an argument. + if parentVar.isAccessScope, let accessLoc = parentVar.sourceLoc { + diagnose(accessLoc, .lifetime_outside_scope_access, parentVar.name ?? "") return } - if let arg = dependence.parentValue as? Argument, - let varDecl = arg.varDecl, - let sourceLoc = arg.sourceLoc { - diagnose(sourceLoc, .lifetime_outside_scope_argument, - varDecl.userFacingName) + // If the argument does not have a source location (e.g. a synthesized accessor), report the function location. The + // function's source location is sufficient for argument diagnostics, but if the function has no location, don't + // report any scope. + if parentVar.isArgument, let argLoc = parentVar.sourceLoc ?? function.location.sourceLoc { + if parentVar.isClosureCapture { + diagnose(argLoc, .lifetime_outside_scope_capture) + } else if let parentName = parentVar.name { + diagnose(argLoc, .lifetime_outside_scope_argument, parentName) + } else { + diagnose(argLoc, .lifetime_outside_scope_synthesized_argument, parentVar.accessorKind ?? function.name) + } return } - let parentVar = LifetimeVariable(dependent: dependence.parentValue, context) + // Now diagnose dependencies on regular variable and value scopes. + // Thunks do not have a function location, so any scopes inside the thunk will be ignored. if let parentLoc = parentVar.sourceLoc { if let parentName = parentVar.name { diagnose(parentLoc, .lifetime_outside_scope_variable, parentName) @@ -260,51 +324,101 @@ private struct DiagnoseDependence { } } } + + func diagnoseImplicitFunction() { + guard let funcLoc = function.location.sourceLoc else { + return + } + if let kindName = { + if function.isInitializer { + return "init" + } + if function.isDeinitializer { + return "deinit" + } + return function.accessorKindName + }() { + diagnose(funcLoc, .implicit_function_note, kindName) + } + } } // Identify a best-effort variable declaration based on a defining SIL // value or any lifetime dependent use of that SIL value. private struct LifetimeVariable { - var varDecl: VarDecl? - var sourceLoc: SourceLoc? + var varDecl: VarDecl? = nil + var sourceLoc: SourceLoc? = nil + var isAccessScope: Bool = false + var isArgument: Bool = false + var isClosureCapture: Bool = false + var accessorKind: String? + var thunkKind: Function.ThunkKind = .noThunk var name: StringRef? { return varDecl?.userFacingName } - init(dependent value: Value, _ context: some Context) { - if value.type.isAddress { - self = Self(accessBase: value.accessBase, context) + init(usedBy operand: Operand, _ context: some Context) { + self = .init(dependent: operand.value, context) + // variable names like $return_value and $implicit_value don't have source locations. + // For @out arguments, the operand's location is the best answer. + // Otherwise, fall back to the function's location. + self.sourceLoc = self.sourceLoc ?? operand.instruction.location.sourceLoc + ?? operand.instruction.parentFunction.location.sourceLoc + } + + init(definedBy value: Value, _ context: some Context) { + self = .init(dependent: value, context) + // Fall back to the function's location. + self.sourceLoc = self.sourceLoc ?? value.parentFunction.location.sourceLoc + } + + private init(dependent value: Value, _ context: some Context) { + guard let introducer = getFirstVariableIntroducer(of: value, context) else { return } - if let firstIntroducer = getFirstVariableIntroducer(of: value, context) { - self = Self(introducer: firstIntroducer) + if introducer.type.isAddress { + if let beginAccess = introducer as? BeginAccessInst { + // Recurse through beginAccess to find the variable introducer rather than the variable access. + self = .init(dependent: beginAccess.address, context) + self.isAccessScope = true + // However, remember source location of the innermost access. + self.sourceLoc = beginAccess.location.sourceLoc ?? self.sourceLoc + return + } + self = .init(accessBase: introducer.accessBase, context) return } - self.varDecl = nil - self.sourceLoc = nil + self = Self(introducer: introducer, context) } private func getFirstVariableIntroducer(of value: Value, _ context: some Context) -> Value? { var introducer: Value? - var useDefVisitor = VariableIntroducerUseDefWalker(context, scopedValue: value) { + var useDefVisitor = VariableIntroducerUseDefWalker(context, scopedValue: value, ignoreTrivialCopies: false) { introducer = $0 return .abortWalk } defer { useDefVisitor.deinitialize() } - _ = useDefVisitor.walkUp(valueOrAddress: value) + _ = useDefVisitor.walkUp(newLifetime: value) return introducer } - private init(introducer: Value) { - if let arg = introducer as? Argument { - self.varDecl = arg.varDecl - } else { - self.sourceLoc = introducer.definingInstruction?.location.sourceLoc - self.varDecl = introducer.definingInstruction?.findVarDecl() + private init(introducer: Value, _ context: some Context) { + if let arg = introducer as? FunctionArgument { + self.varDecl = arg.findVarDecl() + self.sourceLoc = arg.sourceLoc + self.isArgument = true + self.isClosureCapture = arg.isClosureCapture + return } - if let varDecl { - sourceLoc = varDecl.nameLoc + if let varDecl = introducer.definingInstruction?.findVarDecl() { + self.varDecl = varDecl + self.sourceLoc = varDecl.nameLoc + } else if let sourceLoc = introducer.definingInstruction?.location.sourceLoc { + self.sourceLoc = sourceLoc + } else { + self.accessorKind = introducer.parentFunction.accessorKindName + self.thunkKind = introducer.parentFunction.thunkKind } } @@ -318,32 +432,27 @@ private struct LifetimeVariable { // never be produced by one of these, except when it is redundant with the `alloc_box` VarDecl. It does not seem // possible for a box to be moved/borrowed directly into another variable's box. Reassignment always loads/stores // the value. - self = Self(introducer: projectBox.box.referenceRoot) + self = .init(introducer: projectBox.box.referenceRoot, context) case .stack(let allocStack): - self = Self(introducer: allocStack) + self = .init(introducer: allocStack, context) case .global(let globalVar): self.varDecl = globalVar.varDecl self.sourceLoc = varDecl?.nameLoc case .class(let refAddr): - self.varDecl = refAddr.varDecl - self.sourceLoc = refAddr.location.sourceLoc + self = .init(introducer: refAddr, context) case .tail(let refTail): - self = Self(introducer: refTail.instance) + self = .init(introducer: refTail.instance, context) case .argument(let arg): - self.varDecl = arg.varDecl - self.sourceLoc = arg.sourceLoc + self = .init(introducer: arg, context) case .yield(let result): // TODO: bridge VarDecl for FunctionConvention.Yields - self.varDecl = nil - self.sourceLoc = result.parentInstruction.location.sourceLoc + self = .init(introducer: result, context) case .storeBorrow(let sb): self = .init(dependent: sb.source, context) case .pointer(let ptrToAddr): - self.varDecl = nil - self.sourceLoc = ptrToAddr.location.sourceLoc + self = .init(introducer: ptrToAddr, context) case .index, .unidentified: - self.varDecl = nil - self.sourceLoc = nil + break } } } @@ -355,8 +464,8 @@ private struct LifetimeVariable { /// /// This supports store-to-yield. Storing to a yield is an escape unless the yielded memory location depends on another /// lifetime that already depends on the current scope. When setter depends on 'newValue', 'newValue' is stored to the -/// yielded address, and the yielded addrses depends on the lifetime of 'self'. A mark_dependence should have already -/// been inserted for that lifetime depenence: +/// yielded address, and the yielded addresses depends on the lifetime of 'self'. A mark_dependence should have already +/// been inserted for that lifetime dependence: /// /// (%a, %t) = begin_apply %f(%self) /// : $@yield_once @convention(method) (@inout Self) -> _inherit(0) @yields @inout Self.field @@ -392,7 +501,7 @@ extension DependentAddressUseDefWalker: AddressUseDefWalker { } } -/// Walk down lifetime depenence uses. For each check that all dependent +/// Walk down lifetime dependence uses. For each check that all dependent /// leaf uses are non-escaping and within the dependence scope. The walk /// starts with add address for .access dependencies. The walk can /// transition from an address to a value at a load. The walk can diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift index 61ffbee1a612c..a2547d21fdd37 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift @@ -95,6 +95,7 @@ extension LifetimeDependentApply { struct LifetimeSource { let targetKind: TargetKind let convention: LifetimeDependenceConvention + let isInout: Bool let value: Value } @@ -116,7 +117,8 @@ extension LifetimeDependentApply { guard let dep = applySite.resultDependence(on: operand) else { continue } - info.sources.push(LifetimeSource(targetKind: .result, convention: dep, value: operand.value)) + let isInout = applySite.convention(of: operand)?.isInout ?? false + info.sources.push(LifetimeSource(targetKind: .result, convention: dep, isInout: isInout, value: operand.value)) } return info } @@ -135,6 +137,7 @@ extension LifetimeDependentApply { ? TargetKind.yieldAddress : TargetKind.yield info.sources.push(LifetimeSource(targetKind: targetKind, convention: .scope(addressable: false, addressableForDeps: false), + isInout: false, value: beginApply.token)) } for operand in applySite.parameterOperands { @@ -145,9 +148,15 @@ extension LifetimeDependentApply { case .inherit: continue case .scope: + // FIXME: For yields with a scoped lifetime dependence, dependence on parameter operands is redundant, + // since we introduce dependence on the begin_apply's token as well. + // This can lead to duplicate lifetime dependence diagnostics in some cases. + // However this is neccessary for safety when begin_apply gets inlined which will delete the dependence on the token. for yieldedValue in beginApply.yieldedValues { let targetKind = yieldedValue.type.isAddress ? TargetKind.yieldAddress : TargetKind.yield - info.sources.push(LifetimeSource(targetKind: targetKind, convention: .inherit, value: operand.value)) + let isInout = applySite.convention(of: operand)?.isInout ?? false + info.sources.push(LifetimeSource(targetKind: targetKind, convention: dep, isInout: isInout, + value: operand.value)) } } } @@ -176,7 +185,8 @@ extension LifetimeDependentApply { guard let dep = dep else { continue } - info.sources.push(LifetimeSource(targetKind: targetKind, convention: dep, value: operand.value)) + let isInout = applySite.convention(of: operand)?.isInout ?? false + info.sources.push(LifetimeSource(targetKind: targetKind, convention: dep, isInout: isInout, value: operand.value)) } return info } @@ -191,8 +201,8 @@ private extension LifetimeDependentApply.LifetimeSourceInfo { // (a) the result or yield is never returned from this function // // (b) the inherited lifetime has a dependence root within this function (it comes from a dependent function - // argument or scoped dependence). In this case, when that depedence root is diagnosed, the analysis will find - // transtive uses of this apply's result. + // argument or scoped dependence). In this case, when that dependence root is diagnosed, the analysis will find + // transitive uses of this apply's result. // // (c) the dependent value is passed to another call with a dependent inout argument, or it is stored to a yielded // address of a coroutine that has a dependent inout argument. In this case, a mark_dependence will already be @@ -219,8 +229,8 @@ private extension LifetimeDependentApply.LifetimeSourceInfo { return } // Create a new dependence on the apply's access to the argument. - for varIntoducer in gatherVariableIntroducers(for: source.value, context) { - let scope = LifetimeDependence.Scope(base: varIntoducer, context) + for varIntroducer in gatherVariableIntroducers(for: source.value, ignoreTrivialCopies: !source.isInout, context) { + let scope = LifetimeDependence.Scope(base: varIntroducer, context) log("Scoped lifetime from \(source.value)") log(" scope: \(scope)") bases.append(scope.parentValue) @@ -247,18 +257,21 @@ private func insertResultDependencies(for apply: LifetimeDependentApply, _ conte insertMarkDependencies(value: dependentValue, initializer: nil, bases: sources.bases, builder: builder, context) } for resultOper in apply.applySite.indirectResultOperands { - let accessBase = resultOper.value.accessBase - guard case let .store(initializingStore, initialAddress) = accessBase.findSingleInitializer(context) else { - continue + guard let initialAddress = resultOper.value.accessBase.address else { + diagnoseUnknownDependenceSource(sourceLoc: apply.applySite.location.sourceLoc, context) + return } - assert(initializingStore == resultOper.instruction, "an indirect result is a store") Builder.insert(after: apply.applySite, context) { builder in - insertMarkDependencies(value: initialAddress, initializer: initializingStore, bases: sources.bases, + insertMarkDependencies(value: initialAddress, initializer: resultOper.instruction, bases: sources.bases, builder: builder, context) } } } +private func diagnoseUnknownDependenceSource(sourceLoc: SourceLoc?, _ context: FunctionPassContext) { + context.diagnosticEngine.diagnose(.lifetime_value_outside_scope, at: sourceLoc) +} + private func insertParameterDependencies(apply: LifetimeDependentApply, target: Operand, _ context: FunctionPassContext ) { guard var sources = apply.getParameterDependenceSources(target: target) else { @@ -300,8 +313,8 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?, } } -/// Walk up the value dependence chain to find the best-effort variable declaration. Typically called while diagnosing -/// an error. +/// Walk up the value dependence chain to find the best-effort variable declaration. Used to find the source of a borrow +/// dependence or to print the source variable in a diagnostic message. /// /// Returns an array with at least one introducer value. /// @@ -309,16 +322,17 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?, /// - a variable declaration (begin_borrow [var_decl], move_value [var_decl]) /// - a begin_access for a mutable variable access /// - the value or address "root" of the dependence chain -func gatherVariableIntroducers(for value: Value, _ context: Context) +func gatherVariableIntroducers(for value: Value, ignoreTrivialCopies: Bool, _ context: Context) -> SingleInlineArray { var introducers = SingleInlineArray() - var useDefVisitor = VariableIntroducerUseDefWalker(context, scopedValue: value) { + var useDefVisitor = VariableIntroducerUseDefWalker(context, scopedValue: value, + ignoreTrivialCopies: ignoreTrivialCopies) { introducers.push($0) return .continueWalk } defer { useDefVisitor.deinitialize() } - _ = useDefVisitor.walkUp(valueOrAddress: value) + _ = useDefVisitor.walkUp(newLifetime: value) assert(!introducers.isEmpty, "missing variable introducer") return introducers } @@ -330,7 +344,7 @@ func gatherVariableIntroducers(for value: Value, _ context: Context) /// Walk up lifetime dependencies to the first value associated with a variable declaration. /// /// To start walking: -/// walkUp(valueOrAddress: Value) -> WalkResult +/// walkUp(newLifetime: Value) -> WalkResult /// /// This utility finds the value or address associated with the lvalue (variable declaration) that is passed as the /// source of a lifetime dependent argument. If no lvalue is found, then it finds the "root" of the chain of temporary @@ -343,7 +357,7 @@ func gatherVariableIntroducers(for value: Value, _ context: Context) /// /// dependsOn(lvalue.computed) // finds the temporary value directly returned by a getter. /// -/// SILGen emits temporary copies that violate lifetime dependence semantcs. This utility looks through such temporary +/// SILGen emits temporary copies that violate lifetime dependence semantics. This utility looks through such temporary /// copies, stopping at a value that introduces an immutable variable: move_value [var_decl] or begin_borrow [var_decl], /// or at an access of a mutable variable: begin_access [read] or begin_access [modify]. /// @@ -382,10 +396,7 @@ func gatherVariableIntroducers(for value: Value, _ context: Context) /// All of the dependent uses including `end_borrow %5` and `destroy_value %4` must be before the end of the dependence /// scope: `destroy_value %parent`. In this case, the dependence parent is an owned value, so the scope is simply the /// value's OSSA lifetime. -struct VariableIntroducerUseDefWalker : ForwardingUseDefWalker { - // The ForwardingUseDefWalker's context is the most recent lifetime owner. - typealias PathContext = Value? - +struct VariableIntroducerUseDefWalker : LifetimeDependenceUseDefValueWalker, LifetimeDependenceUseDefAddressWalker { let context: Context // If the scoped value is trivial, then only the variable's lexical scope is relevant, and access scopes can be @@ -399,11 +410,15 @@ struct VariableIntroducerUseDefWalker : ForwardingUseDefWalker { // Call \p visit rather than calling this directly. private let visitorClosure: (Value) -> WalkResult - init(_ context: Context, scopedValue: Value, _ visitor: @escaping (Value) -> WalkResult) { + init(_ context: Context, scopedValue: Value, ignoreTrivialCopies: Bool, _ visitor: @escaping (Value) -> WalkResult) { self.context = context - self.isTrivialScope = scopedValue.type.isAddress - ? scopedValue.type.objectType.isTrivial(in: scopedValue.parentFunction) - : scopedValue.isTrivial(context) + if ignoreTrivialCopies { + self.isTrivialScope = scopedValue.type.isAddress + ? scopedValue.type.objectType.isTrivial(in: scopedValue.parentFunction) + : scopedValue.isTrivial(context) + } else { + self.isTrivialScope = false + } self.visitedValues = ValueSet(context) self.visitorClosure = visitor } @@ -412,128 +427,55 @@ struct VariableIntroducerUseDefWalker : ForwardingUseDefWalker { visitedValues.deinitialize() } - mutating func needWalk(for value: Value, _ owner: Value?) -> Bool { - visitedValues.insert(value) - } - mutating func introducer(_ value: Value, _ owner: Value?) -> WalkResult { + if let addrToPtr = value as? AddressToPointerInst { + // AddressToPointer introduces the value dependence. To handle Builtin.addressOfBorrow, follow the address that + // the pointer is derived from. + return walkUp(address: addrToPtr.address) + } return visitorClosure(value) } - mutating func walkUp(valueOrAddress: Value) -> WalkResult { - if valueOrAddress.type.isAddress { - return walkUp(address: valueOrAddress) - } - return walkUp(newLifetime: valueOrAddress) + mutating func addressIntroducer(_ address: Value, access: AccessBaseAndScopes) -> WalkResult { + return visitorClosure(address) + } + + mutating func needWalk(for value: Value, _ owner: Value?) -> Bool { + visitedValues.insert(value) + } + + mutating func needWalk(for address: Value) -> Bool { + visitedValues.insert(address) } -} -// Helpers -extension VariableIntroducerUseDefWalker { mutating func walkUp(newLifetime: Value) -> WalkResult { + if newLifetime.type.isAddress { + return walkUp(address: newLifetime) + } let newOwner = newLifetime.ownership == .owned ? newLifetime : nil return walkUp(value: newLifetime, newOwner) } + /// Override to check for variable introducers: move_value, begin_value, before following + /// OwnershipTransitionInstruction. mutating func walkUp(value: Value, _ owner: Value?) -> WalkResult { - // Check for variable introducers: move_value, begin_value, before following OwnershipTransitionInstruction. if let inst = value.definingInstruction, VariableScopeInstruction(inst) != nil { return visitorClosure(value) } - switch value.definingInstruction { - case let transition as OwnershipTransitionInstruction: - return walkUp(newLifetime: transition.operand.value) - case let load as LoadInstruction: - return walkUp(address: load.address) - default: - break - } - // If the dependence chain has a phi, consider it a root. Dependence roots dominate all dependent values. - if Phi(value) != nil { - return introducer(value, owner) - } - // ForwardingUseDefWalker will callback to introducer() when it finds no forwarding instruction. - return walkUpDefault(forwarded: value, owner) + return walkUpDefault(value: value, owner) } - // Handle temporary allocations and access scopes. - mutating func walkUp(address: Value) -> WalkResult { - let accessBaseAndScopes = address.accessBaseWithScopes - // Continue walking for some kinds of access base. - switch accessBaseAndScopes.base { - case .box, .global, .class, .tail, .pointer, .index, .unidentified: - break - case let .stack(allocStack): - if allocStack.varDecl == nil { - // Ignore temporary stack locations. Their access scopes do not affect lifetime dependence. - return walkUp(stackInitializer: allocStack, at: address) - } - case let .argument(arg): - // Ignore access scopes for @in or @in_guaranteed arguments when all scopes are reads. Do not ignore a [read] - // access of an inout argument or outer [modify]. Mutation later with the outer scope could invalidate the - // borrowed state in this narrow scope. Do not ignore any mark_depedence on the address. - if arg.convention.isIndirectIn && accessBaseAndScopes.isOnlyReadAccess { - return introducer(arg, nil) - } - // @inout arguments may be singly initialized (when no modification exists in this function), but this is not - // relevant here because they require nested access scopes which can never be ignored. - case let .yield(yieldedAddress): - // Ignore access scopes for @in or @in_guaranteed yields when all scopes are reads. - let apply = yieldedAddress.definingInstruction as! FullApplySite - if apply.convention(of: yieldedAddress).isIndirectIn && accessBaseAndScopes.isOnlyReadAccess { - return introducer(yieldedAddress, nil) - } - case .storeBorrow(let sb): - // Walk up through a store into a temporary. - if accessBaseAndScopes.scopes.isEmpty, - case .stack = sb.destinationOperand.value.accessBase { - return walkUp(newLifetime: sb.source) - } - } - // Skip the access scope for unsafe[Mutable]Address. Treat it like a projection of 'self' rather than a separate - // variable access. - if case let .access(innerAccess) = accessBaseAndScopes.scopes.first, - let addressorSelf = innerAccess.unsafeAddressorSelf { - return walkUp(valueOrAddress: addressorSelf) - } - // Ignore the acces scope for trivial values regardless of whether it is singly-initialized. Trivial values do not - // need to be kept alive in memory and can be safely be overwritten in the same scope. Lifetime dependence only - // cares that the loaded value is within the lexical scope of the trivial value's variable declaration. Rather than - // skipping all access scopes, call 'walkUp' on each nested access in case one of them needs to redirect the walk, - // as required for 'access.unsafeAddressorSelf'. - if isTrivialScope { - switch accessBaseAndScopes.scopes.first { - case .none, .base: - break - case let .access(beginAccess): - return walkUp(address: beginAccess.address) - case let .dependence(markDep): - return walkUp(address: markDep.value) - } - } - return introducer(accessBaseAndScopes.enclosingAccess.address ?? address, nil) - } - - // Handle singly-initialized temporary stack locations. - mutating func walkUp(stackInitializer allocStack: AllocStackInst, at address: Value) -> WalkResult { - guard let initializer = allocStack.accessBase.findSingleInitializer(context) else { - return introducer(address, nil) - } - if case let .store(store, _) = initializer { - switch store { - case let store as StoringInstruction: - return walkUp(newLifetime: store.source) - case let srcDestInst as SourceDestAddrInstruction: - return walkUp(address: srcDestInst.destination) - case let apply as FullApplySite: - if let f = apply.referencedFunction, f.isConvertPointerToPointerArgument { - return walkUp(address: apply.parameterOperands[0].value) + /// Override to check for on-stack variables before following an initializer. + mutating func walkUp(address: Value, access: AccessBaseAndScopes) -> WalkResult { + // Check for stack locations that correspond to an lvalue if there isn't any nested access scope. + if access.innermostAccess == nil { + if case let .stack(allocStack) = access.base { + if allocStack.varDecl != nil { + return addressIntroducer(allocStack, access: access) } - default: - break } } - return introducer(address, nil) + return walkUpDefault(address: address, access: access) } } @@ -541,5 +483,5 @@ let variableIntroducerTest = FunctionTest("variable_introducer") { function, arguments, context in let value = arguments.takeValue() print("Variable introducers of: \(value)") - print(gatherVariableIntroducers(for: value, context)) + print(gatherVariableIntroducers(for: value, ignoreTrivialCopies: false, context)) } diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift index ecb69fde5b197..94dfc78e820ba 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift @@ -106,6 +106,7 @@ let lifetimeDependenceScopeFixupPass = FunctionPass( let localReachabilityCache = LocalVariableReachabilityCache() + var mustFixStackNesting = false for instruction in function.instructions { guard let markDep = instruction as? MarkDependenceInstruction else { continue @@ -116,12 +117,100 @@ let lifetimeDependenceScopeFixupPass = FunctionPass( // Redirect the dependence base to ignore irrelevant borrow scopes. let newLifetimeDep = markDep.rewriteSkippingBorrow(scope: innerLifetimeDep.scope, context) - // Recursively sink enclosing end_access, end_borrow, or end_apply. - let args = extendScopes(dependence: newLifetimeDep, localReachabilityCache, context) + // Recursively sink enclosing end_access, end_borrow, end_apply, and destroy_value. If the scope can be extended + // into the caller, return the function arguments that are the dependency sources. + var scopeExtension = ScopeExtension(localReachabilityCache, context) + guard scopeExtension.extendScopes(dependence: newLifetimeDep) else { + continue + } + mustFixStackNesting = mustFixStackNesting || scopeExtension.mustFixStackNesting + let args = scopeExtension.findArgumentDependencies() + + // If the scope cannot be extended to the caller, this must be the outermost dependency level. + // Insert end_cow_mutation_addr if needed. + if args.isEmpty { + createEndCOWMutationIfNeeded(lifetimeDep: newLifetimeDep, context) + } // Redirect the dependence base to the function arguments. This may create additional mark_dependence instructions. markDep.redirectFunctionReturn(to: args, context) } + if mustFixStackNesting { + context.fixStackNesting(in: function) + } +} + +private extension Type { + func mayHaveMutableSpan(in function: Function, _ context: FunctionPassContext) -> Bool { + if hasArchetype { + return true + } + if isBuiltinType { + return false + } + // Only result types that are nominal can have a MutableSpan derived from an inout array access. + if nominal == nil { + return false + } + if nominal == context.swiftMutableSpan { + return true + } + if isStruct { + guard let fields = getNominalFields(in: function) else { + return false + } + return fields.contains { $0.mayHaveMutableSpan(in: function, context) } + } + if isTuple { + return tupleElements.contains { $0.mayHaveMutableSpan(in: function, context) } + } + if isEnum { + guard let cases = getEnumCases(in: function) else { + return true + } + return cases.contains { $0.payload?.mayHaveMutableSpan(in: function, context) ?? false } + } + // Classes cannot be ~Escapable, therefore cannot hold a MutableSpan. + if isClass { + return false + } + return false + } +} + +/// Insert end_cow_mutation_addr for lifetime dependent values that maybe of type MutableSpan and depend on a mutable address. +private func createEndCOWMutationIfNeeded(lifetimeDep: LifetimeDependence, _ context: FunctionPassContext) { + var scoped : ScopedInstruction + + // Handle cases which generate mutable addresses: begin_access [modify] and yield & + switch lifetimeDep.scope { + case let .access(beginAccess): + if beginAccess.accessKind != .modify { + return + } + scoped = beginAccess + case let .yield(value): + let beginApply = value.definingInstruction as! BeginApplyInst + if value == beginApply.token { + return + } + if beginApply.convention(of: value as! MultipleValueInstructionResult) != .indirectInout { + return + } + scoped = beginApply + // None of the below cases can generate a mutable address. + case .owned, .borrowed, .local, .initialized, .caller, .global, .unknown: + return + } + + guard lifetimeDep.dependentValue.type.mayHaveMutableSpan(in: lifetimeDep.dependentValue.parentFunction, context) else { + return + } + + for endInstruction in scoped.endInstructions { + let builder = Builder(before: endInstruction, context) + builder.createEndCOWMutationAddr(address: lifetimeDep.parentValue) + } } private extension MarkDependenceInstruction { @@ -189,6 +278,34 @@ private extension MarkDependenceAddrInst { } } +/// A scope extension is a set of nested scopes and their owners. The owner is a value that represents ownership of +/// the outermost scopes, which cannot be extended; it limits how far the nested scopes can be extended. +private struct ScopeExtension { + let context: FunctionPassContext + let localReachabilityCache: LocalVariableReachabilityCache + + /// The ownership lifetime of the dependence base, which cannot be extended. + var owners = SingleInlineArray() + + // Initialized after walking dependent uses. True if the scope can be extended into the caller. + var dependsOnCaller: Bool? + + // Does scope extension potentially invalidate stack nesting? + var mustFixStackNesting = false + + // Scopes listed in RPO over an upward walk. The outermost scope is first. + var scopes = SingleInlineArray() + + var innermostScope: ExtendableScope { get { scopes.last! } } + + var visitedValues: ValueSet? + + init(_ localReachabilityCache: LocalVariableReachabilityCache, _ context: FunctionPassContext) { + self.localReachabilityCache = localReachabilityCache + self.context = context + } +} + /// Transitively extend nested scopes that enclose the dependence base. /// /// If the parent function returns the dependent value, then this returns the function arguments that represent the @@ -218,215 +335,270 @@ private extension MarkDependenceAddrInst { // access. This scope fixup pass must extend '%a1' to cover the `@useDependent` but must not extend the base of the // `mark_dependence` to the outer access `%0`. This ensures that exclusivity diagnostics correctly reports the // violation, and that subsequent optimizations do not shrink the inner access `%a1`. -private func extendScopes(dependence: LifetimeDependence, - _ localReachabilityCache: LocalVariableReachabilityCache, - _ context: FunctionPassContext) -> SingleInlineArray { - log("Scope fixup for lifetime dependent instructions: \(dependence)") - - // Each scope extension is a set of nested scopes and an owner. The owner is a value that represents ownerhip of the - // outermost scope, which cannot be extended; it limits how far the nested scopes can be extended. - guard let scopeExtensions = dependence.scope.gatherExtensions(context) else { - return SingleInlineArray() - } - var dependsOnArgs = SingleInlineArray() - for scopeExtension in scopeExtensions { - var scopeExtension = scopeExtension - guard var useRange = computeDependentUseRange(of: dependence, within: &scopeExtension, localReachabilityCache, - context) else { - continue - } +extension ScopeExtension { + mutating func extendScopes(dependence: LifetimeDependence) -> Bool { + log("Scope fixup for lifetime dependent instructions:\n\(dependence)") - // deinitializes 'useRange' - guard scopeExtension.tryExtendScopes(over: &useRange, context) else { - continue + gatherExtensions(dependence: dependence) + + // computeDependentUseRange initializes scopeExtension.dependsOnCaller. + guard var useRange = computeDependentUseRange(of: dependence) else { + return false } - if scopeExtension.dependsOnCaller, let arg = scopeExtension.dependsOnArg { - dependsOnArgs.push(arg) + // tryExtendScopes deinitializes 'useRange' + var scopesToExtend = SingleInlineArray() + guard canExtendScopes(over: &useRange, scopesToExtend: &scopesToExtend) else { + useRange.deinitialize() + return false } + // extend(over:) must receive the original unmodified `useRange`, without intermediate scope ending instructions. + // This deinitializes `useRange` before erasing instructions. + extend(scopesToExtend: scopesToExtend, over: &useRange, context) + return true } - return dependsOnArgs } -/// All scopes nested within a single dependence base that require extension. -private struct ScopeExtension { - /// The ownership lifetime of the dependence base, which cannot be extended. - let owner: Value +// TODO: add parent and child indices to model a DAG of scopes. This will allow sibling scopes that do not follow a +// stack discipline among them but still share the same parent and child scopes. This can occur with dependencies on +// multiple call operands. Until then, scope extension may bail out unnecessarily while trying to extend over a sibling +// scope. +private struct ExtendableScope { + enum Introducer { + case scoped(ScopedInstruction) + case stack(Instruction) + case owned(Value) + } - /// The scopes nested under 'value' that may be extended, in inside-out order. There is always at - /// least one element, otherwise there is nothing to consider extending. - let nestedScopes: SingleInlineArray + // scope.allocStackInstruction is always valid for Introducer.allocStack and is valid for Introducer.scoped when + // ScopedInstruction is a store_borrow. + let scope: LifetimeDependence.Scope + let introducer: Introducer + + var firstInstruction: Instruction { + switch introducer { + case let .scoped(scopedInst): + return scopedInst + case let .stack(initializingStore): + return initializingStore + case let .owned(value): + if let definingInst = value.definingInstructionOrTerminator { + return definingInst + } + return value.parentBlock.instructions.first! + } + } - var innerScope: LifetimeDependence.Scope { get { nestedScopes.first! } } + var endInstructions: LazyMapSequence, Instruction> { + switch introducer { + case let .scoped(scopedInst): + return scopedInst.scopeEndingOperands.users + case .stack: + // For alloc_stack without a store-borrow scope, include the deallocs in its scope to ensure that we never shorten + // the original allocation. It's possible that some other use depends on the address. + // + // Same as 'AllocStackInst.deallocations' but as an Instruction list... + return scope.allocStackInstruction!.uses.lazy.filter + { $0.instruction is DeallocStackInst }.lazy.map { $0.instruction } + + case let .owned(value): + return value.uses.endingLifetime.users + } + } - /// `dependsOnArg` is set to the function argument that represents the caller's dependency source. - /// - /// Note: for non-address owners, this is equivalent to: owner as? FunctionArg? - var dependsOnArg: FunctionArgument? + var deallocs: LazyMapSequence, DeallocStackInst>? { + guard let allocStack = scope.allocStackInstruction else { + return nil + } + return allocStack.deallocations + } - /// `dependsOnCaller` is true if the dependent value is returned by the function. - /// Initialized during computeDependentUseRange(). - var dependsOnCaller = false -} + // Allow scope extension as long as `beginInst` does not define a variable scope and is either a scoped instruction or + // a store to a singly-initialized temporary. + init?(_ scope: LifetimeDependence.Scope, beginInst: Instruction?) { + self.scope = scope + guard let beginInst = beginInst, VariableScopeInstruction(beginInst) == nil else { + return nil + } + // Check for "scoped" store_borrow extension before checking allocStackInstruction. + if let scopedInst = beginInst as? ScopedInstruction { + self.introducer = .scoped(scopedInst) + return + } + if scope.allocStackInstruction != nil { + self.introducer = .stack(beginInst) + return + } + return nil + } -private extension LifetimeDependence.Scope { - /// The instruction that introduces an extendable scope. This returns a non-nil scope introducer for - /// each scope in ScopeExtension.nestedScopes. - var extendableBegin: ScopedInstruction? { - switch self { - case let .access(beginAccess): - return beginAccess - case let .borrowed(beginBorrow): - return beginBorrow.value.definingInstruction as? ScopedInstruction - case let .yield(yieldedValue): - return yieldedValue.definingInstruction as? ScopedInstruction - case let .initialized(initializer): - switch initializer { - case let .store(initializingStore: store, initialAddress: _): - if let sb = store as? StoreBorrowInst { - return sb - } - return nil - case .argument, .yield: - // TODO: extend indirectly yielded scopes. - return nil - } - default: + // Allow extension of owned temporaries that + // (a) are Escapable + // (b) do not define a variable scope + // (c) are only consumed by destroy_value + init?(_ scope: LifetimeDependence.Scope, owner: Value) { + self.scope = scope + // TODO: allow extension of lifetime dependent values by implementing a ScopeExtensionWalker that extends + // LifetimeDependenceUseDefWalker. + guard owner.type.isEscapable(in: owner.parentFunction), + VariableScopeInstruction(owner.definingInstruction) == nil, + owner.uses.endingLifetime.allSatisfy({ $0.instruction is DestroyValueInst }) else { return nil } + self.introducer = .owned(owner) } +} - /// Precondition: the 'self' scope encloses a dependent value. 'innerScopes' are the extendable scopes enclosed by - /// 'self' that also enclose the dependent value. - /// - /// Gather the list of ScopeExtensions. Each extension is a list of scopes, including 'innerScopes', 'self' and, - /// recursively, any of its enclosing scopes that are extendable. We may have multiple extensions because a scope - /// introducer may itself depend on multiple operands. - /// - /// Return 'nil' if 'self' is not extendable. - func gatherExtensions(innerScopes: SingleInlineArray? = nil, _ context: FunctionPassContext) - -> SingleInlineArray? { +// Gather extendable scopes. +extension ScopeExtension { + mutating func gatherExtensions(dependence: LifetimeDependence) { + visitedValues = ValueSet(context) + defer { + visitedValues!.deinitialize() + visitedValues = nil + } + gatherExtensions(scope: dependence.scope) + } + + mutating func gatherExtensions(valueOrAddress: Value) { + if visitedValues!.insert(valueOrAddress) { + gatherExtensions(scope: LifetimeDependence.Scope(base: valueOrAddress, context)) + } + } - // Note: LifetimeDependence.Scope.extend() will assume that all inner scopes begin with a ScopedInstruction. - var innerScopes = innerScopes ?? SingleInlineArray() - switch self { + mutating func nonExtendable(_ scope: LifetimeDependence.Scope) { + owners.push(scope.parentValue) + } + + // If `scope` is extendable, find its owner or outer scopes first, then push for extension. + mutating func gatherExtensions(scope: LifetimeDependence.Scope) { + switch scope { case let .access(beginAccess): - return gatherAccessExtension(beginAccess: beginAccess, innerScopes: &innerScopes, context) + gatherAccessExtensions(beginAccess: beginAccess) + return case let .borrowed(beginBorrow): - // begin_borrow is extendable, so push this scope. - innerScopes.push(self) - return gatherBorrowExtension(borrowedValue: beginBorrow.baseOperand!.value, innerScopes: innerScopes, context) - - case let .yield(yieldedValue): - // A yield is extendable, so push this scope. - innerScopes.push(self) - // Create a separate ScopeExtension for each operand that the yielded value depends on. - var extensions = SingleInlineArray() - let applySite = yieldedValue.definingInstruction as! BeginApplyInst - for operand in applySite.parameterOperands { - guard let dep = applySite.resultDependence(on: operand), dep.isScoped else { - continue + if let beginInst = beginBorrow.value.definingInstruction { + if let extScope = ExtendableScope(scope, beginInst: beginInst) { + gatherExtensions(valueOrAddress: beginBorrow.baseOperand!.value) + scopes.push(extScope) + return } - // Pass a copy of innerScopes without modifying this one. - extensions.append(contentsOf: gatherOperandExtension(on: operand, innerScopes: innerScopes, context)) } - return extensions + + case let .yield(yieldedValue): + let beginApply = yieldedValue.definingInstruction as! BeginApplyInst + gatherYieldExtension(beginApply) + scopes.push(ExtendableScope(scope, beginInst: beginApply)!) + return + case let .initialized(initializer): switch initializer { case let .store(initializingStore: store, initialAddress: _): if let sb = store as? StoreBorrowInst { - innerScopes.push(self) - // Only follow the source of the store_borrow. The address is always an alloc_stack without any access scope. - return gatherBorrowExtension(borrowedValue: sb.source, innerScopes: innerScopes, context) + // Follow the stored value since the owner of the borrowed value needs to cover this allocation. + gatherExtensions(valueOrAddress: sb.source) } - return nil + if scope.allocStackInstruction != nil { + scopes.push(ExtendableScope(scope, beginInst: store)!) + return + } + break case .argument, .yield: // TODO: extend indirectly yielded scopes. - return nil + break + } + case let .owned(value): + if let extScope = ExtendableScope(scope, owner: value) { + scopes.push(extScope) + return + } + + case let .local(varInst): + switch varInst { + case let .beginBorrow(beginBorrow): + if let extScope = ExtendableScope(scope, beginInst: beginBorrow) { + gatherExtensions(valueOrAddress: beginBorrow.operand.value) + scopes.push(extScope) + return + } + + case let .moveValue(moveValue): + if let extScope = ExtendableScope(scope, owner: moveValue) { + scopes.push(extScope) + return + } } default: - return nil + break } + nonExtendable(scope) } - /// Unlike LifetimeDependenceInsertion this does not use gatherVariableIntroducers. The purpose here is to extend - /// any enclosing OSSA scopes as far as possible to achieve the longest possible owner lifetime, rather than to - /// find the "dependence root" for a call argument. - func gatherOperandExtension(on operand: Operand, innerScopes: SingleInlineArray, - _ context: FunctionPassContext) -> SingleInlineArray { - let enclosingScope = LifetimeDependence.Scope(base: operand.value, context) - if let extensions = enclosingScope.gatherExtensions(innerScopes: innerScopes, context) { - return extensions + /// Unlike LifetimeDependenceInsertion, this does not stop at an argument's "variable introducer" and does not stop at + /// an addressable parameter. The purpose here is to extend any enclosing OSSA scopes as far as possible to achieve + /// the longest possible owner lifetime, rather than to find the source-level lvalue for a call argument. + mutating func gatherYieldExtension(_ beginApply: BeginApplyInst) { + // Create a separate ScopeExtension for each operand that the yielded value depends on. + for operand in beginApply.parameterOperands { + gatherExtensions(valueOrAddress: operand.value) } - // This is the outermost scope to be extended because gatherExtensions did not find an enclosing scope. - return SingleInlineArray(element: getOuterExtension(owner: operand.value, nestedScopes: innerScopes, context)) } - func getOuterExtension(owner: Value, nestedScopes: SingleInlineArray, - _ context: FunctionPassContext) -> ScopeExtension { - let dependsOnArg = owner as? FunctionArgument - return ScopeExtension(owner: owner, nestedScopes: nestedScopes, dependsOnArg: dependsOnArg) - } - - // Find the nested access scopes that may be extended as if they are the same access. This includes any combination of - // read/modify accesses, regardless of whether they may cause an exclusivity violation. The outer accesses will only - // be extended as far as required such that the innermost access coveres all dependent uses. - // Set ScopeExtension.dependsOnArg if the nested accesses are all compatible with the argument's convention. Then, if - // all nested accesses were extended to the return statement, it is valid to logically combine them into a single - // access for the purpose of diagnostinc lifetime dependence. - func gatherAccessExtension(beginAccess: BeginAccessInst, - innerScopes: inout SingleInlineArray, - _ context: FunctionPassContext) -> SingleInlineArray { - // Finding the access base also finds all intermediate nested scopes; there is no need to recursively call - // gatherExtensions(). + mutating func gatherAccessExtensions(beginAccess: BeginAccessInst) { let accessBaseAndScopes = beginAccess.accessBaseWithScopes - var isCompatibleAccess = true - for nestedScope in accessBaseAndScopes.scopes { + if let baseAddress = accessBaseAndScopes.base.address { + gatherExtensions(valueOrAddress: baseAddress) + } + for nestedScope in accessBaseAndScopes.scopes.reversed() { switch nestedScope { case let .access(nestedBeginAccess): - innerScopes.push(.access(nestedBeginAccess)) - if nestedBeginAccess.accessKind != beginAccess.accessKind { - isCompatibleAccess = false - } + scopes.push(ExtendableScope(.access(nestedBeginAccess), beginInst: nestedBeginAccess)!) case .dependence, .base: // ignore recursive mark_dependence base for the purpose of extending scopes. This pass will extend the base // of that mark_dependence (if it is unresolved) later as a separate LifetimeDependence.Scope. break } } - guard case let .access(outerBeginAccess) = innerScopes.last else { - // beginAccess is included in accessBaseWithScopes; so at least one access was added to innerScopes. - fatalError("missing outer access") + } +} + +extension ScopeExtension { + /// Check if the dependent value depends only on function arguments and can therefore be returned to caller. If so, + /// return the list of arguments that it depends on. If this returns an empty list, then the dependent value cannot be + /// returned. + /// + /// The conditions for returning a dependent value are: + /// - The dependent value is returned from this function. + /// - All nested scopes are access scopes that are redundant with the caller's exclusive access scope. + /// - All scope owners are function arguments. + func findArgumentDependencies() -> SingleInlineArray { + let noCallerScope = SingleInlineArray() + // Check that the dependent value is returned by this function. + if !dependsOnCaller! { + return noCallerScope } - if case let .argument(arg) = accessBaseAndScopes.base { - if isCompatibleAccess && beginAccess.accessKind.isCompatible(with: arg.convention) { - let scopes = ScopeExtension(owner: outerBeginAccess.address, nestedScopes: innerScopes, dependsOnArg: arg) - return SingleInlineArray(element: scopes) + // Check that all nested scopes that it depends on can be covered by exclusive access in the caller. + for extScope in scopes { + switch extScope.scope { + case .access: + break + default: + return noCallerScope } } - /// Recurse in case of indirect yields. - let enclosingScope = LifetimeDependence.Scope(base: outerBeginAccess.address, context) - if let extensions = enclosingScope.gatherExtensions(innerScopes: innerScopes, context) { - return extensions - } - // When the owner is an address, the owner's scope is considered the availability of its access base starting at the - // position of innerScopes.last. - let scopes = ScopeExtension(owner: outerBeginAccess.address, nestedScopes: innerScopes, dependsOnArg: nil) - return SingleInlineArray(element: scopes) - } - - func gatherBorrowExtension(borrowedValue: Value, - innerScopes: SingleInlineArray, - _ context: FunctionPassContext) - -> SingleInlineArray { - - let enclosingScope = LifetimeDependence.Scope(base: borrowedValue, context) - if let extensions = enclosingScope.gatherExtensions(innerScopes: innerScopes, context) { - return extensions + // All owners must be arguments with exclusive access to depend on the caller's scope (inout_aliasable arguments do + // not have exclusivity). + var compatibleArgs = SingleInlineArray() + for owner in owners { + guard let arg = owner as? FunctionArgument else { + return noCallerScope + } + guard arg.convention.isIndirectIn || arg.convention.isInout else { + return noCallerScope + } + compatibleArgs.push(arg) } - // This is the outermost scope to be extended because gatherExtensions did not find an enclosing scope. - return SingleInlineArray(element: getOuterExtension(owner: enclosingScope.parentValue, nestedScopes: innerScopes, - context)) + return compatibleArgs } } @@ -460,121 +632,178 @@ extension ScopeExtension { return range.deinitialize() } } + + var description: String { + switch self { + case .fullRange: + return "full range" + case let .addressRange(range): + return range.description + case let .valueRange(range): + return range.description + } + } } /// Return nil if the scope's owner is valid across the function, such as a guaranteed function argument. - func computeRange(_ localReachabilityCache: LocalVariableReachabilityCache, _ context: FunctionPassContext) -> Range? - { + func computeSingleOwnerRange(owner: Value) -> Range? { if owner.type.isAddress { // Get the range of the accessBase lifetime at the point where the outermost extendable scope begins. - if let range = AddressOwnershipLiveRange.compute(for: owner, at: nestedScopes.last!.extendableBegin!.instruction, + if let range = AddressOwnershipLiveRange.compute(for: owner, at: scopes.first!.firstInstruction, localReachabilityCache, context) { return .addressRange(range) } return nil } - if owner.ownership == .owned { + switch owner.ownership { + case .owned: return .valueRange(computeLinearLiveness(for: owner, context)) + case .guaranteed: + if let bbv = BeginBorrowValue(owner) { + if case .functionArgument = bbv { + return .fullRange + } + return .valueRange(computeLinearLiveness(for: bbv.value, context)) + } + return nil + case .none: + return .fullRange + case .unowned: + return nil } - // Trivial or guaranted owner. - // - // TODO: limit extension to the begin_borrow [var_decl] scope - return .fullRange - } -} - -/// Return an InstructionRange covering all the dependent uses of 'dependence'. -private func computeDependentUseRange(of dependence: LifetimeDependence, within scopeExtension: inout ScopeExtension, - _ localReachabilityCache: LocalVariableReachabilityCache, - _ context: FunctionPassContext) - -> InstructionRange? { - let function = dependence.function - guard var ownershipRange = scopeExtension.computeRange(localReachabilityCache, context) else { - return nil } - defer {ownershipRange.deinitialize()} - // The innermost scope that must be extended must dominate all uses. - var useRange = InstructionRange(begin: scopeExtension.innerScope.extendableBegin!.instruction, context) - var walker = LifetimeDependentUseWalker(function, localReachabilityCache, context) { - // Do not extend the useRange past the ownershipRange. - let dependentInst = $0.instruction - if ownershipRange.coversUse(dependentInst) { - useRange.insert(dependentInst) + /// Return an InstructionRange covering all the dependent uses of 'dependence'. + /// + /// Initialize dependsOnCaller. + mutating func computeDependentUseRange(of dependence: LifetimeDependence) -> InstructionRange? { + if scopes.isEmpty { + return nil } - return .continueWalk - } - defer {walker.deinitialize()} - - _ = walker.walkDown(dependence: dependence) - - log("Scope fixup for dependent uses:\n\(useRange)") - - scopeExtension.dependsOnCaller = walker.dependsOnCaller + let function = dependence.function + var inRangeUses = [Instruction]() + do { + // The innermost scope that must be extended must dominate all uses. + var walker = LifetimeDependentUseWalker(function, localReachabilityCache, context) { + inRangeUses.append($0.instruction) + return .continueWalk + } + defer {walker.deinitialize()} + // walkDown may abort if any utility used by address use walker, such asLocalVarUtils, has unhandled cases. + if walker.walkDown(dependence: dependence) == .abortWalk { + return nil + } + dependsOnCaller = walker.dependsOnCaller + } + for owner in owners { + guard var ownershipRange = computeSingleOwnerRange(owner: owner) else { + return nil + } + defer { ownershipRange.deinitialize() } - // Lifetime dependenent uses may not be dominated by the access. The dependent value may be used by a phi or stored - // into a memory location. The access may be conditional relative to such uses. If any use was not dominated, then - // `useRange` will include the function entry. There is not way to directly check - // useRange.isValid. useRange.blockRange.isValid is not a strong enough check because it will always succeed when - // useRange.begin == entryBlock even if a use if above useRange.begin. - let firstInst = function.entryBlock.instructions.first! - if firstInst != useRange.begin, useRange.contains(firstInst) { - useRange.deinitialize() - return nil + inRangeUses = inRangeUses.filter { ownershipRange.coversUse($0) } + } + var useRange = InstructionRange(begin: innermostScope.firstInstruction, context) + useRange.insert(contentsOf: inRangeUses) + + log("Scope fixup for dependent uses:\n\(useRange)") + + // Lifetime dependent uses may not be dominated by `innermostScope`. The dependent value may be used by a phi or + // stored into a memory location. The access may be conditional relative to such uses. If any use was not dominated, + // then `useRange` will include the function entry. There is no way to directly check if `useRange` is + // valid. `useRange.blockRange.isValid` is not a strong enough check because it will always succeed when + // `useRange.begin == entryBlock` even if a use is above `useRange.begin`. Instead check if `useRange` contains the + // first instruction, and the first instruction does not itself start `innermostScope`. + let firstInst = function.entryBlock.instructions.first! + if firstInst != useRange.begin, useRange.contains(firstInst) { + useRange.deinitialize() + return nil + } + return useRange } - return useRange } -// Extend nested scopes across a use-range within their owner's range. extension ScopeExtension { - // Prepare to extend each scope. - func tryExtendScopes(over useRange: inout InstructionRange, _ context: some MutatingContext) -> Bool { + /// Return true if all nested scopes were extended across `useRange`. `useRange` has already been pruned to be a + /// subset of the ranges of the owners. + /// + /// Note: the scopes may not be strictly nested. Two adjacent scopes in the nested scopes array may have begin at the + /// same nesting level. Their begin instructions may occur in any order relative to the nested scopes array, but we + /// order the end instructions according to the arbitrary order that the scopes were inserted in the array. This is + /// conservative and could extend some scopes longer than strictly necessary. To improve this, `scopes` must be + /// represnted as a DAG by recording parent and child indices. + func canExtendScopes(over useRange: inout InstructionRange, + scopesToExtend: inout SingleInlineArray) -> Bool { var extendedUseRange = InstructionRange(begin: useRange.begin!, ends: useRange.ends, context) - // Insert the first instruction of the exit blocks to mimic 'useRange'. This is innacurate, but it produces the same - // result for canExtend() check below, which only checks reachability of end_apply. + + // Insert the first instruction of the exit blocks to mimic `useRange`. There is no way to directly copy + // `useRange`. Inserting the exit block instructions is inaccurate, but for the purpose of canExtend() below, it has + // the same effect as a copy of `useRange`. extendedUseRange.insert(contentsOf: useRange.exits) - for innerScope in nestedScopes { - guard let beginInst = innerScope.extendableBegin else { - fatalError("all nested scopes must have a scoped begin instruction") - } - // Extend 'extendedUseRange' to to cover this scope's end instructions. The extended scope must at least cover the - // original scope because the original scope may protect other operations. - extendedUseRange.insert(contentsOf: beginInst.endInstructions) - if !innerScope.canExtend(over: &extendedUseRange, context) { + defer { extendedUseRange.deinitialize() } + + // Append each scope that needs extension to scopesToExtend from the inner to the outer scope. + for extScope in scopes.reversed() { + var mustExtend = false + // Iterating over scopeEndInst ignores unreachable paths which may not include the dealloc_stack. This is fine + // because the stack allocation effectively covers the entire unreachable path. + for scopeEndInst in extScope.endInstructions { + switch extendedUseRange.overlaps(pathBegin: extScope.firstInstruction, pathEnd: scopeEndInst, context) { + case .containsPath, .containsEnd, .disjoint: + // containsPath can occur when the extendable scope has the same begin as the use range. + // disjoint is unexpected, but if it occurs then `extScope` must be before the useRange. + mustExtend = true + break + case .containsBegin, .overlappedByPath: + // containsBegin can occur when the extendable scope has the same begin as the use range. + // + // An outer scope might not originally cover one of its inner scopes. Therefore, extend 'extendedUseRange' to + // to cover this scope's end instructions. The extended scope must at least cover the original scopes because + // the original scopes may protect other operations. + extendedUseRange.insert(scopeEndInst) + break + } + } + if !mustExtend { + continue + } + scopesToExtend.push(extScope) + if !extScope.canExtend(over: &extendedUseRange, context) { // Scope ending instructions cannot be inserted at the 'range' boundary. Ignore all nested scopes. // - // Note: We could still extend previously prepared inner scopes up to this 'innerScope'. To do that, we would - // need to repeat the steps above: treat 'innerScope' as the new owner, and recompute 'useRange'. But this + // Note: We could still extend previously prepared inner scopes up to this scope. To do that, we would + // need to repeat the steps above: treat 'extScope' as the new owner, and recompute `useRange`. But this // scenario could only happen with nested coroutine, where the range boundary is reachable from the outer // coroutine's EndApply and AbortApply--it is vanishingly unlikely if not impossible. return false } } - extendedUseRange.deinitialize() - // extend(over:) must receive the original unmodified 'useRange'. - extend(over: &useRange, context) return true } // Extend the scopes that actually required extension. // // Consumes 'useRange' - private func extend(over useRange: inout InstructionRange, _ context: some MutatingContext) { + private mutating func extend(scopesToExtend: SingleInlineArray, + over useRange: inout InstructionRange, + _ context: some MutatingContext) { var deadInsts = [Instruction]() - for innerScope in nestedScopes { - guard let beginInst = innerScope.extendableBegin else { - fatalError("all nested scopes must have a scoped begin instruction") - } - let mustExtend = beginInst.endInstructions.contains(where: { useRange.contains($0) }) - + for extScope in scopesToExtend { // Extend 'useRange' to to cover this scope's end instructions. 'useRange' cannot be extended until the // inner scopes have been extended. - for endInst in beginInst.endInstructions { - useRange.insert(endInst) - } - if mustExtend { - deadInsts += innerScope.extend(over: &useRange, context) - } + useRange.insert(contentsOf: extScope.endInstructions) + + // Note, we could Skip extension here if we have a fully overlapping scope. But that requires computing the scope + // of [beginInst : beginInst.endInstructions) because an outer scope may be disjoint from the inner scope but + // still requires extension: + // %access = begin_access [read] %owner // <=== outer scoope + // %temp = load [copy] %access + // end_access %access + // (%dependent, %token) = begin_apply (%temp) // <=== inner scope + // end_apply %token + // + deadInsts += extScope.extend(over: &useRange, context) + // Continue checking enclosing scopes for extension even if 'mustExtend' is false. Multiple ScopeExtensions may // share the same inner scope, so this inner scope may already have been extended while handling a previous // ScopeExtension. Nonetheless, some enclosing scopes may still require extension. This only happens when a @@ -582,106 +811,155 @@ extension ScopeExtension { } // 'useRange' is invalid as soon as instructions are deleted. useRange.deinitialize() + // Delete original end instructions. for deadInst in deadInsts { + if deadInst is DeallocStackInst { + mustFixStackNesting = true + } context.erase(instruction: deadInst) } } } // Extend a dependence scope to cover the dependent uses. -private extension LifetimeDependence.Scope { +extension ExtendableScope { /// Return true if new scope-ending instruction can be inserted at the range boundary. func canExtend(over range: inout InstructionRange, _ context: some Context) -> Bool { - switch self { + switch self.scope { case let .yield(yieldedValue): - let beginApply = yieldedValue.definingInstruction as! BeginApplyInst - let canEndAtBoundary = { (boundaryInst: Instruction) in - switch beginApply.endReaches(block: boundaryInst.parentBlock, context) { - case .abortReaches, .endReaches: - return true - case .none: - return false - } - } - for end in range.ends { - if (!canEndAtBoundary(end)) { - return false - } - } - for exit in range.exits { - if (!canEndAtBoundary(exit)) { - return false - } + return canExtend(beginApply: yieldedValue.definingInstruction as! BeginApplyInst, over: &range, context) + case let .initialized(initializer): + switch initializer { + case .argument, .yield: + // A yield is already considered nested within the coroutine. + return true + case .store: + return self.scope.allocStackInstruction != nil } - return true default: // non-yield scopes can always be ended at any point. return true } } + func canExtend(beginApply: BeginApplyInst, over range: inout InstructionRange, _ context: some Context) -> Bool { + let canEndAtBoundary = { (boundaryInst: Instruction) in + switch beginApply.endReaches(block: boundaryInst.parentBlock, context) { + case .abortReaches, .endReaches, .deadEndReaches: + return true + case .none: + return false + } + } + for end in range.ends { + if (!canEndAtBoundary(end)) { + return false + } + } + for exit in range.exits { + if (!canEndAtBoundary(exit)) { + return false + } + } + return true + } + /// Extend this scope over the 'range' boundary. Return the old scope ending instructions to be deleted. func extend(over range: inout InstructionRange, _ context: some MutatingContext) -> [Instruction] { - guard let beginInst = extendableBegin else { - fatalError("all nested scoped must have a scoped begin instruction") - } // Collect the original end instructions and extend the range to to cover them. The resulting access scope // must cover the original scope because it may protect other memory operations. - var endInsts = [Instruction]() - for end in beginInst.endInstructions { + let originalScopeEnds = [Instruction](self.endInstructions) + // Track scope-ending instructions that have not yet been reused as range-ending instructions. + var unreusedEnds = InstructionSet(context) + for end in originalScopeEnds { assert(range.inclusiveRangeContains(end)) - endInsts.append(end) + unreusedEnds.insert(end) + } + defer { unreusedEnds.deinitialize() } + + // Never reuse dealloc_stack to avoid running data flow. + var endsToErase = [Instruction]() + if let deallocs = self.deallocs { + endsToErase.append(contentsOf: deallocs.map { $0 }) + for dealloc in deallocs { + unreusedEnds.erase(dealloc) + } } - insertBoundaryEnds(range: &range, context) - return endInsts - } - /// Create new scope-ending instructions at the boundary of 'range'. - func insertBoundaryEnds(range: inout InstructionRange, _ context: some MutatingContext) { for end in range.ends { - let location = end.location.autoGenerated - if end is ReturnInst { + let location = end.location.asAutoGenerated + switch end { + case is BranchInst: + assert(end.parentBlock.singleSuccessor!.terminator is ReturnInst, + "a phi only ends a use range if it is a returned value") + fallthrough + case is ReturnInst: // End this inner scope just before the return. The mark_dependence base operand will be redirected to a // function argument. let builder = Builder(before: end, location: location, context) // Insert newEnd so that this scope will be nested in any outer scopes. - range.insert(createEndInstruction(builder, context)) + range.insert(contentsOf: createEndInstructions(builder, context)) + continue + default: + break + } + // If this range ending instruction was also scope-ending, then mark it as reused by removing it from the set. + if unreusedEnds.contains(end) { + unreusedEnds.erase(end) + assert(!unreusedEnds.contains(end)) continue } Builder.insert(after: end, location: location, context) { - range.insert(createEndInstruction($0, context)) + range.insert(contentsOf: createEndInstructions($0, context)) } } for exitInst in range.exits { - let location = exitInst.location.autoGenerated + let location = exitInst.location.asAutoGenerated let builder = Builder(before: exitInst, location: location, context) - range.insert(createEndInstruction(builder, context)) + range.insert(contentsOf: createEndInstructions(builder, context)) } + endsToErase.append(contentsOf: originalScopeEnds.filter { unreusedEnds.contains($0) }) + return endsToErase } /// Create a scope-ending instruction at 'builder's insertion point. - func createEndInstruction(_ builder: Builder, _ context: some Context) -> Instruction { - switch self { + func createEndInstructions(_ builder: Builder, _ context: some Context) -> SingleInlineArray { + switch self.scope { case let .access(beginAccess): - return builder.createEndAccess(beginAccess: beginAccess) + return SingleInlineArray(element: builder.createEndAccess(beginAccess: beginAccess)) case let .borrowed(beginBorrow): - return builder.createEndBorrow(of: beginBorrow.value) + return SingleInlineArray(element: builder.createEndBorrow(of: beginBorrow.value)) case let .yield(yieldedValue): let beginApply = yieldedValue.definingInstruction as! BeginApplyInst // createEnd() returns non-nil because beginApply.endReaches() was checked by canExtend() - return beginApply.createEnd(builder, context)! + return SingleInlineArray(element: beginApply.createEnd(builder, context)!) case let .initialized(initializer): switch initializer { case let .store(initializingStore: store, initialAddress: _): + var endInsts = SingleInlineArray() if let sb = store as? StoreBorrowInst { - return builder.createEndBorrow(of: sb) + endInsts.append(builder.createEndBorrow(of: sb)) + } + if let allocStack = self.scope.allocStackInstruction { + endInsts.append(builder.createDeallocStack(allocStack)) + return endInsts } break case .argument, .yield: // TODO: extend indirectly yielded scopes. break } + case let .owned(value): + return SingleInlineArray(element: builder.createDestroyValue(operand: value)) + case let .local(varInst): + switch varInst { + case let .beginBorrow(beginBorrow): + // FIXME: we may need to rewrite the dealloc_stack. + return SingleInlineArray(element: builder.createEndBorrow(of: beginBorrow)) + case let .moveValue(moveValue): + return SingleInlineArray(element: builder.createDestroyValue(operand: moveValue)) + } default: break } @@ -703,64 +981,62 @@ private extension BeginApplyInst { return builder.createEndApply(beginApply: self) case .abortReaches: return builder.createAbortApply(beginApply: self) + case .deadEndReaches: + return builder.createEndBorrow(of: self.token) } } enum EndReaches { case endReaches case abortReaches + case deadEndReaches } /// Return the single kind of coroutine termination that reaches 'reachableBlock' or nil. func endReaches(block reachableBlock: BasicBlock, _ context: some Context) -> EndReaches? { - var endBlocks = BasicBlockSet(context) - var abortBlocks = BasicBlockSet(context) + // TODO: use InlineArray<3> once bootstrapping is fixed. + var endingBlockMap: [(EndReaches, BasicBlockSet)] = [ + (.endReaches, BasicBlockSet(context)), + (.abortReaches, BasicBlockSet(context)), + (.deadEndReaches, BasicBlockSet(context)) + ] defer { - endBlocks.deinitialize() - abortBlocks.deinitialize() + for index in endingBlockMap.indices { + endingBlockMap[index].1.deinitialize() + } } for endInst in endInstructions { + let endKind: EndReaches switch endInst { case let endApply as EndApplyInst: // Cannot extend the scope of a coroutine when the resume produces a value. if !endApply.type.isEmpty(in: parentFunction) { return nil } - endBlocks.insert(endInst.parentBlock) + endKind = .endReaches case is AbortApplyInst: - abortBlocks.insert(endInst.parentBlock) + endKind = .abortReaches + case is EndBorrowInst: + endKind = .deadEndReaches default: fatalError("invalid begin_apply ending instruction") } + let endingBlocksIndex = endingBlockMap.firstIndex(where: { $0.0 == endKind })! + endingBlockMap[endingBlocksIndex].1.insert(endInst.parentBlock) } var endReaches: EndReaches? var backwardWalk = BasicBlockWorklist(context) defer { backwardWalk.deinitialize() } let backwardVisit = { (block: BasicBlock) -> WalkResult in - if endBlocks.contains(block) { - switch endReaches { - case .none: - endReaches = .endReaches - break - case .endReaches: - break - case .abortReaches: - return .abortWalk + for (endKind, endingBlocks) in endingBlockMap { + if endingBlocks.contains(block) { + if let endReaches = endReaches, endReaches != endKind { + return .abortWalk + } + endReaches = endKind + return .continueWalk } - return .continueWalk - } - if abortBlocks.contains(block) { - switch endReaches { - case .none: - endReaches = .abortReaches - break - case .abortReaches: - break - case .endReaches: - return .abortWalk - } - return .continueWalk } if block == self.parentBlock { // the insertion point is not dominated by the coroutine @@ -850,7 +1126,7 @@ private struct LifetimeDependentUseWalker : LifetimeDependenceDefUseWalker { } mutating func yieldedDependence(result: Operand) -> WalkResult { - return .continueWalk + return visitor(result) } mutating func storeToYieldDependence(address: Value, of operand: Operand) -> WalkResult { diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LoopInvariantCodeMotion.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LoopInvariantCodeMotion.swift new file mode 100644 index 0000000000000..ee4d03629e8c3 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LoopInvariantCodeMotion.swift @@ -0,0 +1,1284 @@ +//===--- LoopInvariantCodeMotion.swift ------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL + +/// Hoist loop invariant code out of innermost loops. +let loopInvariantCodeMotionPass = FunctionPass(name: "loop-invariant-code-motion") { function, context in + for loop in context.loopTree.loops { + optimizeTopLevelLoop(topLevelLoop: loop, context) + } + + if context.needFixStackNesting { + context.fixStackNesting(in: function) + } +} + +private func optimizeTopLevelLoop(topLevelLoop: Loop, _ context: FunctionPassContext) { + var innerLoops = Stack(context) + defer { innerLoops.deinitialize() } + + getWorkList(forLoop: topLevelLoop, workList: &innerLoops) + + while let thisLoop = innerLoops.pop() { + // We only support Loops with a preheader. + guard thisLoop.preheader != nil else { + continue + } + + var thisLoopChanged = false + + repeat { + var movableInstructions = analyzeLoopAndSplitLoads(loop: thisLoop, context) + thisLoopChanged = optimizeLoop(loop: thisLoop, movableInstructions: &movableInstructions, context) + } while thisLoopChanged + } +} + +/// Creates post-order DFS work list of inner loops. +private func getWorkList(forLoop loop: Loop, workList: inout Stack) { + workList.push(loop) + + for innerLoop in loop.innerLoops { + getWorkList(forLoop: innerLoop, workList: &workList) + } +} + +/// Instructions that can be moved outside the loop. +private struct MovableInstructions { + var loadAndStoreAccessPaths: [AccessPath] = [] + + var speculativelyHoistable: [Instruction] = [] + var loadsAndStores: [Instruction] = [] + var hoistUp: [Instruction] = [] + var sinkDown: [Instruction] = [] + var scopedInsts: [ScopedInstruction] = [] +} + +/// Analyzed instructions inside the currently processed loop. +private struct AnalyzedInstructions { + /// Side effects of the loop. + var loopSideEffects: StackWithCount + + private var blockSideEffectBottomMarker: StackWithCount.Marker + + /// Side effects of the currently analyzed block. + var sideEffectsOfCurrentBlock: StackWithCount.Segment { + return StackWithCount.Segment( + in: loopSideEffects, + low: blockSideEffectBottomMarker, + high: loopSideEffects.top + ) + } + + /// Contains either: + /// * an apply to the addressor of the global + /// * a builtin "once" of the global initializer + var globalInitCalls: Stack + var readOnlyApplies: Stack + var loads: Stack + var stores: Stack + var scopedInsts: Stack + var fullApplies: Stack + + /// `true` if the loop has instructions which (may) read from memory, which are not in `Loads` and not in `sideEffects`. + var hasOtherMemReadingInsts = false + + /// `true` if one of the side effects might release. + lazy var sideEffectsMayRelease = loopSideEffects.contains(where: { $0.mayRelease }) + + init (_ context: FunctionPassContext) { + self.loopSideEffects = StackWithCount(context) + self.blockSideEffectBottomMarker = loopSideEffects.top + + self.globalInitCalls = Stack(context) + self.readOnlyApplies = Stack(context) + self.loads = Stack(context) + self.stores = Stack(context) + self.scopedInsts = Stack(context) + self.fullApplies = Stack(context) + } + + mutating func deinitialize() { + readOnlyApplies.deinitialize() + globalInitCalls.deinitialize() + loopSideEffects.deinitialize() + loads.deinitialize() + stores.deinitialize() + scopedInsts.deinitialize() + fullApplies.deinitialize() + } + + /// Mark the start of currently processed block side effects. + mutating func markBeginOfBlock() { + blockSideEffectBottomMarker = loopSideEffects.top + } +} + +/// Analyzes `loop` for hosting/sinking potential. +/// Computes `MovableInstructions` we may be able to move out of the loop +/// +/// This may split some loads into smaller loads. +private func analyzeLoopAndSplitLoads(loop: Loop, _ context: FunctionPassContext) -> MovableInstructions { + // TODO: Remove once uses lowered OSSA. + loop.splitCriticalExitingAndBackEdges(context) + + var movableInstructions = MovableInstructions() + var analyzedInstructions = AnalyzedInstructions(context) + defer { analyzedInstructions.deinitialize() } + + analyzeInstructions(in: loop, &analyzedInstructions, &movableInstructions, context) + + collectHoistableGlobalInitCalls(in: loop, analyzedInstructions, &movableInstructions, context) + + collectProjectableAccessPathsAndSplitLoads(in: loop, &analyzedInstructions, &movableInstructions, context) + + collectMovableInstructions(in: loop, &analyzedInstructions, &movableInstructions, context) + + return movableInstructions +} + +/// Analyze instructions inside the `loop`. Compute side effects and populate `analyzedInstructions`. +/// +/// - note: Ideally, `movableInstructions` should be fully computed in `collectMovableInstructions`. +private func analyzeInstructions( + in loop: Loop, + _ analyzedInstructions: inout AnalyzedInstructions, + _ movableInstructions: inout MovableInstructions, + _ context: FunctionPassContext +) { + for bb in loop.loopBlocks { + analyzedInstructions.markBeginOfBlock() + + for inst in bb.instructions { + switch inst { + case is FixLifetimeInst: + break // We can ignore the side effects of FixLifetimes + case let loadInst as LoadInst: + analyzedInstructions.loads.append(loadInst) + case let uncheckedOwnershipConversionInst as UncheckedOwnershipConversionInst: + analyzedInstructions.analyzeSideEffects(ofInst: uncheckedOwnershipConversionInst) + case let storeInst as StoreInst: + analyzedInstructions.stores.append(storeInst) + analyzedInstructions.analyzeSideEffects(ofInst: storeInst) + case let beginAccessInst as BeginAccessInst: + analyzedInstructions.scopedInsts.append(beginAccessInst) + analyzedInstructions.analyzeSideEffects(ofInst: beginAccessInst) + case let beginBorrowInst as BeginBorrowInstruction: + analyzedInstructions.analyzeSideEffects(ofInst: beginBorrowInst) + case let refElementAddrInst as RefElementAddrInst: + movableInstructions.speculativelyHoistable.append(refElementAddrInst) + case let condFailInst as CondFailInst: + analyzedInstructions.analyzeSideEffects(ofInst: condFailInst) + case let fullApply as FullApplySite: + if fullApply.isSafeReadOnlyApply(context.calleeAnalysis) { + analyzedInstructions.readOnlyApplies.append(fullApply) + } else if let callee = fullApply.referencedFunction, + callee.isGlobalInitFunction, // Calls to global inits are different because we don't care about side effects which are "after" the call in the loop. + !fullApply.globalInitMayConflictWith( + blockSideEffectSegment: analyzedInstructions.sideEffectsOfCurrentBlock, + context.aliasAnalysis + ) { + // Check against side-effects within the same block. + // Side-effects in other blocks are checked later (after we + // scanned all blocks of the loop) in `collectHoistableGlobalInitCalls`. + analyzedInstructions.globalInitCalls.append(fullApply) + } + + analyzedInstructions.fullApplies.append(fullApply) + + // Check for array semantics and side effects - same as default + fallthrough + default: + switch inst { + case let builtinInst as BuiltinInst: + switch builtinInst.id { + case .Once, .OnceWithContext: + if !builtinInst.globalInitMayConflictWith( + blockSideEffectSegment: analyzedInstructions.sideEffectsOfCurrentBlock, + context.aliasAnalysis + ) { + analyzedInstructions.globalInitCalls.append(builtinInst) + } + default: break + } + default: break + } + + analyzedInstructions.analyzeSideEffects(ofInst: inst) + + if inst.canBeHoisted(outOf: loop, context) { + movableInstructions.hoistUp.append(inst) + } + } + } + } +} + +/// Process collected global init calls. Moves them to `hoistUp` if they don't conflict with any side effects. +private func collectHoistableGlobalInitCalls( + in loop: Loop, + _ analyzedInstructions: AnalyzedInstructions, + _ movableInstructions: inout MovableInstructions, + _ context: FunctionPassContext +) { + for globalInitCall in analyzedInstructions.globalInitCalls { + // Check against side effects which are "before" (i.e. post-dominated by) the global initializer call. + // + // The effects in the same block have already been checked before + // adding this global init call to `analyzedInstructions.globalInitCalls` in `analyzeInstructions`. + if globalInitCall.parentBlock.postDominates(loop.preheader!, context.postDominatorTree), + !globalInitCall.globalInitMayConflictWith( + loopSideEffects: analyzedInstructions.loopSideEffects, + context.aliasAnalysis, + context.postDominatorTree + ) { + movableInstructions.hoistUp.append(globalInitCall) + } + } +} + +/// Collect memory locations for which we can move all loads and stores out of the loop. +/// `loads` may mutate during this loop. +private func collectProjectableAccessPathsAndSplitLoads( + in loop: Loop, + _ analyzedInstructions: inout AnalyzedInstructions, + _ movableInstructions: inout MovableInstructions, + _ context: FunctionPassContext +) { + if !analyzedInstructions.hasOtherMemReadingInsts { + for storeInst in analyzedInstructions.stores { + let accessPath = storeInst.destination.accessPath + if accessPath.isLoopInvariant(loop: loop), + analyzedInstructions.isOnlyLoadedAndStored( + accessPath: accessPath, + storeAddr: storeInst.destination, + context.aliasAnalysis + ), + !movableInstructions.loadAndStoreAccessPaths.contains(accessPath), + // This is not a requirement for functional correctness, but we don't want to + // _speculatively_ load and store the value (outside of the loop). + analyzedInstructions.storesCommonlyDominateExits(of: loop, storingTo: accessPath, context), + analyzedInstructions.splitLoads( + storeAddr: storeInst.destination, + accessPath: accessPath, + context + ) { + movableInstructions.loadAndStoreAccessPaths.append(accessPath) + } + } + } +} + +/// Computes movable instructions using computed analyzed instructions. +private func collectMovableInstructions( + in loop: Loop, + _ analyzedInstructions: inout AnalyzedInstructions, + _ movableInstructions: inout MovableInstructions, + _ context: FunctionPassContext +) { + var loadInstCounter = 0 + var readOnlyApplyCounter = 0 + for bb in loop.loopBlocks { + for inst in bb.instructions { + switch inst { + case let fixLifetimeInst as FixLifetimeInst: + guard fixLifetimeInst.parentBlock.dominates(loop.preheader!, context.dominatorTree) else { + continue + } + + if !analyzedInstructions.sideEffectsMayRelease || !analyzedInstructions.loopSideEffectsMayWriteTo(address: fixLifetimeInst.operand.value, context.aliasAnalysis) { + movableInstructions.sinkDown.append(fixLifetimeInst) + } + case let loadInst as LoadInst: + // Avoid quadratic complexity in corner cases. Usually, this limit will not be exceeded. + if loadInstCounter * analyzedInstructions.loopSideEffects.count < 8000, + !analyzedInstructions.loopSideEffectsMayWriteTo(address: loadInst.operand.value, context.aliasAnalysis) { + movableInstructions.hoistUp.append(loadInst) + } + + loadInstCounter += 1 + + movableInstructions.loadsAndStores.append(loadInst) + case is UncheckedOwnershipConversionInst: + break // TODO: Add support + case let storeInst as StoreInst: + switch storeInst.storeOwnership { + case .assign: + continue // TODO: Add support + case .unqualified, .trivial, .initialize: + break + } + movableInstructions.loadsAndStores.append(storeInst) + case let condFailInst as CondFailInst: + // We can (and must) hoist cond_fail instructions if the operand is + // invariant. We must hoist them so that we preserve memory safety. A + // cond_fail that would have protected (executed before) a memory access + // must - after hoisting - also be executed before said access. + movableInstructions.hoistUp.append(condFailInst) + case let beginAccessInst as BeginAccessInst: + if beginAccessInst.canScopedInstructionBeHoisted(outOf: loop, analyzedInstructions: analyzedInstructions, context) { + movableInstructions.scopedInsts.append(beginAccessInst) + } + case let beginBorrowInst as BeginBorrowInstruction: + if !beginBorrowInst.isLexical && beginBorrowInst.canScopedInstructionBeHoisted(outOf: loop, analyzedInstructions: analyzedInstructions, context) { + movableInstructions.scopedInsts.append(beginBorrowInst) + } + case let fullApplySite as FullApplySite: + guard analyzedInstructions.readOnlyApplies.contains(where: { $0 == fullApplySite }) else { + break + } + + // Avoid quadratic complexity in corner cases. Usually, this limit will not be exceeded. + if readOnlyApplyCounter * analyzedInstructions.loopSideEffects.count < 8000, + fullApplySite.isSafeReadOnlyApply( + for: analyzedInstructions.loopSideEffects, + context.aliasAnalysis, + context.calleeAnalysis + ) { + if let beginApplyInst = fullApplySite as? BeginApplyInst { + movableInstructions.scopedInsts.append(beginApplyInst) + } else { + movableInstructions.hoistUp.append(fullApplySite) + } + + readOnlyApplyCounter += 1 + } + default: + break + } + } + } +} + +/// Optimizes the loop by performing in following order: +/// - speculative hoist +/// - projection, hoist and sink of loads and stores +/// - hoist of instructions that are guaranteed to be executed +/// - sink +/// - hoist with sink of scoped instructions +private func optimizeLoop( + loop: Loop, + movableInstructions: inout MovableInstructions, + _ context: FunctionPassContext +) -> Bool { + var changed = false + + // TODO: If we hoist tuple_element_addr and struct_element_addr instructions here, hoistAndSinkLoadsAndStores could converge after just one execution! + changed = movableInstructions.speculativelyHoistInstructions(outOf: loop, context) || changed + changed = movableInstructions.hoistAndSinkLoadsAndStores(outOf: loop, context) || changed + changed = movableInstructions.hoistInstructions(outOf: loop, context) || changed + changed = movableInstructions.sinkInstructions(outOf: loop, context) || changed + changed = movableInstructions.hoistWithSinkScopedInstructions(outOf: loop, context) || changed + + return changed +} + +extension BasicBlock { + func containsStoresTo(accessPath: AccessPath) -> Bool { + return instructions.contains { inst in + return inst.operands.contains { operand in + if let storeInst = operand.instruction as? StoreInst, + storeInst.destination.accessPath == accessPath { + return true + } else { + return false + } + } + } + } +} + +private extension AnalyzedInstructions { + /// Adds side effects of `inst` to the analyzed instructions. + mutating func analyzeSideEffects(ofInst inst: Instruction) { + if inst.mayHaveSideEffects { + loopSideEffects.append(inst) + } else if inst.mayReadFromMemory { + hasOtherMemReadingInsts = true + } + } + + /// Returns true if all instructions in `sideEffects` which may alias with + /// this path are either loads or stores from this path. + /// + /// `storeAddr` is only needed for AliasAnalysis until we have an interface + /// that supports `AccessPath`. + func isOnlyLoadedAndStored( + accessPath: AccessPath, + storeAddr: Value, + _ aliasAnalysis: AliasAnalysis + ) -> Bool { + if (loopSideEffects.contains { sideEffect in + switch sideEffect { + case let storeInst as StoreInst: + if storeInst.storesTo(accessPath) { + return false + } + case let loadInst as LoadInst: + if loadInst.loadsFrom(accessPath) { + return false + } + default: break + } + + // Pass the original address value until we can fix alias analysis. + return sideEffect.mayReadOrWrite(address: storeAddr, aliasAnalysis) + }) { + return false + } + + if (loads.contains { loadInst in + loadInst.mayRead(fromAddress: storeAddr, aliasAnalysis) && !loadInst.overlaps(accessPath: accessPath) + }) { + return false + } + + if (stores.contains { storeInst in + storeInst.mayWrite(toAddress: storeAddr, aliasAnalysis) && !storeInst.storesTo(accessPath) + }) { + return false + } + + return true + } + + /// Returns `true` if all stores to `accessPath` commonly dominate the loop exits. + func storesCommonlyDominateExits(of loop: Loop, storingTo accessPath: AccessPath, _ context: FunctionPassContext) -> Bool { + var exitingBlocksSet = BasicBlockSet(context) + var storeBlocks = BasicBlockSet(context) + var worklist = BasicBlockWorklist(context) + defer { + exitingBlocksSet.deinitialize() + storeBlocks.deinitialize() + worklist.deinitialize() + } + + // Also a store in the pre-header dominates all exists. Although the situation + // is a bit different here: the store in the pre-header remains - it's not + // (re)moved by the LICM transformation. + // But even if the loop-stores are not dominating the loop exits, it + // makes sense to move them out of the loop if this case. When this is done, + // dead-store-elimination can then most likely eliminate the store in the + // pre-header. + // + // pre_header: + // store %v1 to %addr + // header: + // cond_br %cond, then, tail + // then: + // store %v2 to %addr // a conditional store in the loop + // br tail + // tail: + // cond_br %loop_cond, header, exit + // exit: + // + // will be transformed to + // + // pre_header: + // store %v1 to %addr // <- can be removed by DSE afterwards + // header: + // cond_br %cond, then, tail + // then: + // br tail + // tail(%phi): + // cond_br %loop_cond, header, exit + // exit: + // store %phi to %addr + // + if loop.preheader!.containsStoresTo(accessPath: accessPath) { + return true + } + + // Create a set of exiting blocks for efficient lookup later. + exitingBlocksSet.insert(contentsOf: loop.exitingBlocks) + + // Collect as many recognizable store parent blocks as possible. It's ok if not all stores are collected. + storeBlocks.insert(contentsOf: stores + .filter({ $0.destination.accessPath == accessPath }) + .map(\.parentBlock)) + + // If a store is in the loop header, we already know that it's dominating all loop exits. + if storeBlocks.contains(loop.header) { + return true + } + + // Starting from the header, check whether all stores are alive. + worklist.pushIfNotVisited(loop.header) + while let block = worklist.pop() { + if storeBlocks.contains(block) { + continue + } + + if exitingBlocksSet.contains(block), + block.successors.filter({ $0.terminator is UnreachableInst }).count != block.successors.count { + return false + } + + worklist.pushIfNotVisited(contentsOf: block.successors) + } + + return true + } + + /// Returns true if `loopSideEffects` contains any memory writes which + /// may alias with the memory `address`. + func loopSideEffectsMayWriteTo(address: Value, _ aliasAnalysis: AliasAnalysis) -> Bool { + return loopSideEffects + .contains { sideEffect in + sideEffect.mayWrite(toAddress: address, aliasAnalysis) + } + } + + /// Find all loads that contain `accessPath`. Split them into a load with + /// identical `accessPath` and a set of non-overlapping loads. Add the new + /// non-overlapping loads to `loads`. + mutating func splitLoads( + storeAddr: Value, + accessPath: AccessPath, + _ context: FunctionPassContext + ) -> Bool { + var newLoads = Stack(context) + defer { + loads.append(contentsOf: newLoads) + newLoads.deinitialize() + } + var splitCounter = 0 + + while let loadInst = loads.pop() { + // Found a load wider than the store to accessPath. + // + // SplitLoads is called for each unique access path in the loop that is + // only loaded from and stored to and this loop takes time proportional to: + // num-wide-loads x num-fields x num-loop-memops + // + // For each load wider than the store, it creates a new load for each field + // in that type. Each new load is inserted in the LoadsAndStores vector. To + // avoid super-linear behavior for large types (e.g. giant tuples), limit + // growth of new loads to an arbitrary constant factor per access path. + guard splitCounter <= 6 else { + newLoads.push(loadInst) + return false + } + + guard !loadInst.isDeleted, loadInst.operand.value.accessPath.contains(accessPath) else { + newLoads.push(loadInst) + continue + } + + guard let projectionPath = loadInst.operand.value.accessPath.getProjection(to: accessPath), + let splitLoads = loadInst.trySplit(alongPath: projectionPath, context) else { + newLoads.push(loadInst) + return false + } + + splitCounter += splitLoads.count + newLoads.append(contentsOf: splitLoads) + } + + return true + } +} + +private extension MovableInstructions { + /// Hoist instructions speculatively. + /// + /// Contrary to `hoistInstructions`, it doesn't only go through instructions in blocks that dominate all exits. + mutating func speculativelyHoistInstructions(outOf loop: Loop, _ context: FunctionPassContext) -> Bool { + var changed = false + + for inst in speculativelyHoistable { + changed = inst.hoist(outOf: loop, context) || changed + } + + return changed + } + + /// Hoists and sinks loads with matching stores. Projects loads. + mutating func hoistAndSinkLoadsAndStores(outOf loop: Loop, _ context: FunctionPassContext) -> Bool { + var changed = false + + for accessPath in loadAndStoreAccessPaths { + changed = hoistAndSinkLoadAndStore(outOf: loop, accessPath: accessPath, context: context) || changed + } + + return changed + } + + /// Only hoists instructions in blocks that dominate all exit and latch blocks. + /// It doesn't hoist instructions speculatively. + mutating func hoistInstructions(outOf loop: Loop, _ context: FunctionPassContext) -> Bool { + let dominatingBlocks = loop.getBlocksThatDominateAllExitingAndLatchBlocks(context) + var changed = false + + for bb in dominatingBlocks { + for inst in bb.instructions where hoistUp.contains(inst) { + changed = inst.hoist(outOf: loop, context) || changed + } + } + + return changed + } + + /// Sink instructions. + mutating func sinkInstructions(outOf loop: Loop, _ context: FunctionPassContext) -> Bool { + let dominatingBlocks = loop.getBlocksThatDominateAllExitingAndLatchBlocks(context) + var changed = false + + for inst in sinkDown where dominatingBlocks.contains(inst.parentBlock) { + changed = inst.sink(outOf: loop, context) || changed + } + + return changed + } + + /// Hoist and sink scoped instructions. + mutating func hoistWithSinkScopedInstructions(outOf loop: Loop, _ context: FunctionPassContext) -> Bool { + // Since we don't sink scoped instructions to dead exit blocks, we need to check there's + // at least one exit block to which we can sink end instructions. Otherwise we could end up + // with partially hoisted scoped instruction that could lead to e.g. value overconsumption. + guard !loop.hasNoExitBlocks, loop.exitBlocks.contains(where: { !context.deadEndBlocks.isDeadEnd($0) }) else { + return false + } + + var changed = false + + for scopedInst in scopedInsts { + if let storeBorrowInst = scopedInst as? StoreBorrowInst { + _ = storeBorrowInst.allocStack.hoist(outOf: loop, context) + + var sankFirst = false + for deallocStack in storeBorrowInst.allocStack.deallocations { + if sankFirst { + context.erase(instruction: deallocStack) + } else { + sankFirst = deallocStack.sink(outOf: loop, context) + } + } + + context.notifyInvalidatedStackNesting() + } + + guard scopedInst.hoist(outOf: loop, context) else { + continue + } + + // We only want to sink the first end_access and erase the rest to not introduce duplicates. + var sankFirst = false + for endAccess in scopedInst.endInstructions { + if sankFirst { + context.erase(instruction: endAccess) + } else { + sankFirst = endAccess.sink(outOf: loop, context) + } + } + + changed = true + } + + return changed + } + + private mutating func hoistAndSinkLoadAndStore( + outOf loop: Loop, + accessPath: AccessPath, + context: FunctionPassContext + ) -> Bool { + + // If the memory is not initialized at all exits, it would be wrong to insert stores at exit blocks. + guard memoryIsInitializedAtAllExits(of: loop, accessPath: accessPath, context) else { + return false + } + + // Initially load the value in the loop pre header. + let builder = Builder(before: loop.preheader!.terminator, context) + var firstStore: StoreInst? + + // If there are multiple stores in a block, only the last one counts. + for case let storeInst as StoreInst in loadsAndStores where storeInst.storesTo(accessPath) { + // If a store just stores the loaded value, bail. The operand (= the load) + // will be removed later, so it cannot be used as available value. + // This corner case is surprisingly hard to handle, so we just give up. + if let srcLoadInst = storeInst.source as? LoadInst, + srcLoadInst.loadsFrom(accessPath) { + return false + } + + if firstStore == nil { + firstStore = storeInst + } else if storeInst.destination.type != firstStore!.destination.type { + // This transformation assumes that the values of all stores in the loop + // must be interchangeable. It won't work if stores different types + // because of casting or payload extraction even though they have the + // same access path. + return false + } + } + + guard let firstStore else { + return false + } + + // We currently don't support split `load [take]`, i.e. `load [take]` which does _not_ load all + // non-trivial fields of the initial value. + for case let load as LoadInst in loadsAndStores { + if load.loadOwnership == .take, + let path = accessPath.getProjection(to: load.address.accessPath), + !firstStore.destination.type.isProjectingEntireNonTrivialMembers(path: path, in: load.parentFunction) + { + return false + } + } + + var ssaUpdater = SSAUpdater( + function: firstStore.parentFunction, + type: firstStore.destination.type.objectType, + ownership: firstStore.source.ownership, + context + ) + + // Set all stored values as available values in the ssaUpdater. + for case let storeInst as StoreInst in loadsAndStores where storeInst.storesTo(accessPath) { + ssaUpdater.addAvailableValue(storeInst.source, in: storeInst.parentBlock) + } + + var cloner = Cloner(cloneBefore: loop.preheader!.terminator, context) + defer { cloner.deinitialize() } + + guard let initialAddr = (cloner.cloneRecursively(value: firstStore.destination) { srcAddr, cloner in + switch srcAddr { + case is AllocStackInst, is BeginBorrowInst, is MarkDependenceInst: + return .stopCloning + default: break + } + + // Clone projections until the address dominates preheader. + if srcAddr.parentBlock.dominates(loop.preheader!, context.dominatorTree) { + cloner.recordFoldedValue(srcAddr, mappedTo: srcAddr) + return .customValue(srcAddr) + } else { + // Return nil invalid to continue cloning. + return .defaultValue + } + }) else { + return false + } + + let ownership: LoadInst.LoadOwnership = firstStore.parentFunction.hasOwnership ? (firstStore.storeOwnership == .initialize ? .take : .trivial) : .unqualified + + let initialLoad = builder.createLoad(fromAddress: initialAddr, ownership: ownership) + ssaUpdater.addAvailableValue(initialLoad, in: loop.preheader!) + + var changed = false + var currentBlock: BasicBlock? + var currentVal: Value? + + // Remove all stores and replace the loads with the current value. + // + // This loop depends on loadsAndStores being in order the instructions appear in blocks. + for inst in loadsAndStores { + let block = inst.parentBlock + + if block != currentBlock { + currentBlock = block + currentVal = nil + } + + if let storeInst = inst as? StoreInst, storeInst.storesTo(accessPath) { + currentVal = storeInst.source + context.erase(instruction: storeInst) + changed = true + continue + } + + guard let loadInst = inst as? LoadInst, + loadInst.loadsFrom(accessPath) else { + continue + } + + // If we didn't see a store in this block yet, get the current value from the ssaUpdater. + let rootVal = currentVal ?? ssaUpdater.getValue(inMiddleOf: block) + + if loadInst.operand.value.accessPath == accessPath { + if loadInst.loadOwnership == .copy { + let builder = Builder(before: loadInst, context) + let copy = builder.createCopyValue(operand: rootVal) + loadInst.replace(with: copy, context) + } else { + loadInst.replace(with: rootVal, context) + } + changed = true + continue + } + + guard let projectionPath = accessPath.getProjection(to: loadInst.operand.value.accessPath) else { + continue + } + + let builder = Builder(before: loadInst, context) + let projection = if loadInst.loadOwnership == .copy { + rootVal.createProjectionAndCopy(path: projectionPath, builder: builder) + } else { + rootVal.createProjection(path: projectionPath, builder: builder) + } + loadInst.replace(with: projection, context) + + changed = true + } + + loadsAndStores.removeAll(where: { $0.isDeleted }) + + // Store back the value at all loop exits. + for exitBlock in loop.exitBlocks { + assert(exitBlock.hasSinglePredecessor, "Exiting edge should not be critical.") + + let builder = Builder(before: exitBlock.instructions.first!, context) + + builder.createStore( + source: ssaUpdater.getValue(inMiddleOf: exitBlock), + destination: initialAddr, + ownership: firstStore.storeOwnership + ) + changed = true + } + + // In case the value is only stored but never loaded in the loop. + if initialLoad.uses.isEmpty { + context.erase(instruction: initialLoad) + } + + return changed + } + + func memoryIsInitializedAtAllExits(of loop: Loop, accessPath: AccessPath, _ context: FunctionPassContext) -> Bool { + + // Perform a simple dataflow analysis which checks if there is a path from a `load [take]` + // (= the only kind of instruction which can de-initialize the memory) to a loop exit without + // a `store` in between. + + var stores = InstructionSet(context) + defer { stores.deinitialize() } + for case let store as StoreInst in loadsAndStores where store.storesTo(accessPath) { + stores.insert(store) + } + + var exitInsts = InstructionSet(context) + defer { exitInsts.deinitialize() } + exitInsts.insert(contentsOf: loop.exitBlocks.lazy.map { $0.instructions.first! }) + + var worklist = InstructionWorklist(context) + defer { worklist.deinitialize() } + for case let load as LoadInst in loadsAndStores where load.loadOwnership == .take && load.loadsFrom(accessPath) { + worklist.pushIfNotVisited(load) + } + + while let inst = worklist.pop() { + if stores.contains(inst) { + continue + } + if exitInsts.contains(inst) { + return false + } + worklist.pushSuccessors(of: inst) + } + return true + } +} + +private extension Type { + func isProjectingEntireNonTrivialMembers(path: SmallProjectionPath, in function: Function) -> Bool { + let (kind, index, subPath) = path.pop() + switch kind { + case .root: + return true + case .structField: + guard let fields = getNominalFields(in: function) else { + return false + } + for (fieldIdx, fieldType) in fields.enumerated() { + if fieldIdx != index && !fieldType.isTrivial(in: function) { + return false + } + } + return fields[index].isProjectingEntireNonTrivialMembers(path: subPath, in: function) + case .tupleField: + for (elementIdx, elementType) in tupleElements.enumerated() { + if elementIdx != index && !elementType.isTrivial(in: function) { + return false + } + } + return tupleElements[index].isProjectingEntireNonTrivialMembers(path: subPath, in: function) + default: + fatalError("path is not materializable") + } + } +} + +private extension Instruction { + /// Returns `true` if this instruction follows the default hoisting heuristic which means it + /// is not a terminator, allocation or deallocation and either a hoistable array semantics call or doesn't have memory effects. + func canBeHoisted(outOf loop: Loop, _ context: FunctionPassContext) -> Bool { + switch self { + case is TermInst, is Allocation, is Deallocation: + return false + case is ApplyInst: + switch arraySemanticsCallKind { + case .getCount, .getCapacity: + if canHoistArraySemanticsCall(to: loop.preheader!.terminator, context) { + return true + } + case .arrayPropsIsNativeTypeChecked: + return false + default: + break + } + default: + break + } + + if memoryEffects == .noEffects, + !results.contains(where: { $0.ownership == .owned }) { + return true + } + + return false + } + + func hoist(outOf loop: Loop, _ context: FunctionPassContext) -> Bool { + guard operands.allSatisfy({ !loop.contains(block: $0.value.parentBlock) }) else { + return false + } + + let terminator = loop.preheader!.terminator + if canHoistArraySemanticsCall(to: terminator, context) { + hoistArraySemanticsCall(before: terminator, context) + } else { + if let loadCopyInst = self as? LoadInst, loadCopyInst.loadOwnership == .copy { + hoist(loadCopyInst: loadCopyInst, outOf: loop, context) + return true + } else { + move(before: terminator, context) + } + } + + if let singleValueInst = self as? SingleValueInstruction, + !(self is ScopedInstruction || self is AllocStackInst), + let identicalInst = (loop.preheader!.instructions.first { otherInst in + return singleValueInst != otherInst && singleValueInst.isIdenticalTo(otherInst) + }) { + guard let identicalSingleValueInst = identicalInst as? SingleValueInstruction else { + return true + } + + singleValueInst.replace(with: identicalSingleValueInst, context) + } + + return true + } + + private func hoist(loadCopyInst: LoadInst, outOf loop: Loop, _ context: FunctionPassContext) { + if loop.hasNoExitBlocks { + return + } + + let preheaderBuilder = Builder(before: loop.preheader!.terminator, context) + let preheaderLoadBorrow = preheaderBuilder.createLoadBorrow(fromAddress: loadCopyInst.address) + + let headerBuilder = Builder(before: loadCopyInst, context) + let copyValue = headerBuilder.createCopyValue(operand: preheaderLoadBorrow) + loadCopyInst.replace(with: copyValue, context) + + for exitBlock in loop.exitBlocks where !context.deadEndBlocks.isDeadEnd(exitBlock) { + assert(exitBlock.hasSinglePredecessor, "Exiting edge should not be critical.") + + let exitBlockBuilder = Builder(before: exitBlock.instructions.first!, context) + exitBlockBuilder.createEndBorrow(of: preheaderLoadBorrow) + } + } + + func sink(outOf loop: Loop, _ context: FunctionPassContext) -> Bool { + var changed = false + + for exitBlock in loop.exitBlocks where !context.deadEndBlocks.isDeadEnd(exitBlock) { + assert(exitBlock.hasSinglePredecessor, "Exiting edge should not be critical.") + + if changed { + copy(before: exitBlock.instructions.first!, context) + } else { + move(before: exitBlock.instructions.first!, context) + changed = true + } + } + + return changed + } + + /// Returns `true` if `sideEffect` cannot be reordered with a call to this + /// global initializer. + private func globalInitMayConflictWith( + sideEffect: Instruction, + _ aliasAnalysis: AliasAnalysis + ) -> Bool { + switch sideEffect { + case let storeInst as StoreInst: + return mayReadOrWrite(address: storeInst.destinationOperand.value, aliasAnalysis) + case let loadInst as LoadInst: + return mayWrite(toAddress: loadInst.operand.value, aliasAnalysis) + case is CondFailInst: + return false + default: + return true + } + } + + /// Returns `true` if any of the instructions in `sideEffects` cannot be + /// reordered with a call to this global initializer (which is in the same basic + /// block). + func globalInitMayConflictWith( + blockSideEffectSegment: StackWithCount.Segment, + _ aliasAnalysis: AliasAnalysis + ) -> Bool { + return blockSideEffectSegment + .contains { sideEffect in + globalInitMayConflictWith( + sideEffect: sideEffect, + aliasAnalysis + ) + } + } + + /// Returns `true` if any of the instructions in `loopSideEffects` which are + /// post-dominated by a call to this global initializer cannot be reordered with + /// the call. + func globalInitMayConflictWith( + loopSideEffects: StackWithCount, + _ aliasAnalysis: AliasAnalysis, + _ postDomTree: PostDominatorTree + ) -> Bool { + return loopSideEffects + .contains { sideEffect in + // Only check instructions in blocks which are "before" (i.e. post-dominated + // by) the block which contains the init-call. + // Instructions which are before the call in the same block have already + // been checked. + parentBlock.strictlyPostDominates(sideEffect.parentBlock, postDomTree) && + globalInitMayConflictWith(sideEffect: sideEffect, aliasAnalysis) + } + } +} + +private extension StoreInst { + /// Returns a `true` if this store is a store to `accessPath`. + func storesTo(_ accessPath: AccessPath) -> Bool { + return accessPath == self.destination.accessPath + } +} + +private extension LoadInst { + /// Returns `true` if this load instruction loads from `accessPath` or a + /// projected address from `accessPath`. + func loadsFrom(_ accessPath: AccessPath) -> Bool { + return accessPath.getProjection(to: self.address.accessPath)?.isMaterializable ?? false + } + + func overlaps(accessPath: AccessPath) -> Bool { + if let path = accessPath.getProjection(to: self.operand.value.accessPath), + // If the accessPath is wider than load, it needs to be materializable. + // Otherwise we won't be able to project it. + path.isMaterializable { + // The load is narrower than the access path. + return true + } + + if self.operand.value.accessPath.isEqualOrContains(accessPath) { + // The load is wider than the access path. + return true + } + + return false + } +} + +private extension FullApplySite { + /// Returns `true` if this apply inst could be safely hoisted. + func isSafeReadOnlyApply(_ calleeAnalysis: CalleeAnalysis) -> Bool { + guard functionConvention.results.allSatisfy({ $0.convention == .unowned }) else { + return false + } + + if let callee = referencedFunction, + callee.hasSemanticsAttribute("array.props.isNativeTypeChecked") { + return false + } + + return !calleeAnalysis.getSideEffects(ofApply: self).memory.write + } + + /// Returns `true` if the `sideEffects` contain any memory writes which + /// may alias with any memory which is read by this `ApplyInst`. + /// - Note: This function should only be called on a read-only apply! + func isSafeReadOnlyApply( + for sideEffects: StackWithCount, + _ aliasAnalysis: AliasAnalysis, + _ calleeAnalysis: CalleeAnalysis + ) -> Bool { + if calleeAnalysis.getSideEffects(ofApply: self).memory == .noEffects { + return true + } + + // Check if the memory addressed by the argument may alias any writes. + for sideEffect in sideEffects { + switch sideEffect { + case let storeInst as StoreInst: + if storeInst.storeOwnership == .assign || + mayRead(fromAddress: storeInst.destination, aliasAnalysis) { + return false + } + case let copyAddrInst as CopyAddrInst: + if !copyAddrInst.isInitializationOfDestination || + mayRead(fromAddress: copyAddrInst.destination, aliasAnalysis) { + return false + } + case let fullApplySite as FullApplySite: + if calleeAnalysis.getSideEffects(ofApply: fullApplySite).memory.write { + return false + } + case is CondFailInst, is StrongRetainInst, is UnmanagedRetainValueInst, + is RetainValueInst, is StrongRetainUnownedInst, is FixLifetimeInst, + is KeyPathInst, is DeallocStackInst, is DeallocStackRefInst, + is DeallocRefInst: + break + case let endApply as EndApplyInst: + if endApply.beginApply != self { + return false + } + default: + if sideEffect.mayWriteToMemory { + return false + } + } + } + + return true + } +} + +private extension ScopedInstruction { + /// Returns `true` if this begin access is safe to hoist. + func canScopedInstructionBeHoisted( + outOf loop: Loop, + analyzedInstructions: AnalyzedInstructions, + _ context: FunctionPassContext + ) -> Bool { + guard endInstructions.allSatisfy({ loop.contains(block: $0.parentBlock) && !($0 is TermInst) }) else { + return false + } + + // Instruction specific preconditions + switch self { + case is BeginAccessInst, is LoadBorrowInst: + guard (analyzedInstructions.scopedInsts + .allSatisfy { otherScopedInst in + guard self != otherScopedInst else { return true } + + return operands.first!.value.accessPath.isDistinct(from: otherScopedInst.operand.value.accessPath) + }) else { + return false + } + default: + break + } + + var scope = InstructionRange(begin: self, ends: endInstructions, context) + defer { scope.deinitialize() } + + // Instruction specific range related conditions + switch self { + case is BeginApplyInst: + return true // Has already been checked with other full applies. + case let loadBorrowInst as LoadBorrowInst: + for sideEffectInst in analyzedInstructions.loopSideEffects { + if let endBorrow = sideEffectInst as? EndBorrowInst, + let begin = endBorrow.borrow as? LoadBorrowInst, + begin == self + { + continue + } + if sideEffectInst.mayWrite(toAddress: loadBorrowInst.address, context.aliasAnalysis), + !scope.contains(sideEffectInst) + { + return false + } + } + return true + + case let beginAccess as BeginAccessInst: + for fullApplyInst in analyzedInstructions.fullApplies { + guard mayWriteToMemory && fullApplyInst.mayReadOrWrite(address: beginAccess.address, context.aliasAnalysis) || + !mayWriteToMemory && fullApplyInst.mayWrite(toAddress: beginAccess.address, context.aliasAnalysis) else { + continue + } + + // After hoisting the begin/end_access the apply will be within the scope, so it must not have a conflicting access. + if !scope.contains(fullApplyInst) { + return false + } + } + + switch beginAccess.address.accessPath.base { + case .class, .global: + for sideEffect in analyzedInstructions.loopSideEffects where sideEffect.mayRelease { + // Since a class might have a deinitializer, hoisting begin/end_access pair could violate + // exclusive access if the deinitializer accesses address used by begin_access. + if !scope.contains(sideEffect) { + return false + } + } + + return true + default: + return true + } + case is BeginBorrowInst, is StoreBorrowInst: + // Ensure the value is produced outside the loop. + return !loop.contains(block: operands.first!.value.parentBlock) + default: + return false + } + } +} + +private extension AccessPath { + /// Returns `true` if this access path is invariant in `loop`. + func isLoopInvariant(loop: Loop) -> Bool { + switch base { + case .box(let inst as Instruction), .class(let inst as Instruction), + .index(let inst as Instruction), + .pointer(let inst as Instruction), .stack(let inst as Instruction), + .storeBorrow(let inst as Instruction), + .tail(let inst as Instruction): + if loop.contains(block: inst.parentBlock) { + return false + } + case .global, .argument: + break + case .yield(let beginApplyResult): + if loop.contains(block: beginApplyResult.parentBlock) { + return false + } + case .unidentified: + return false + } + + return projectionPath.isConstant + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/NamedReturnValueOptimization.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/NamedReturnValueOptimization.swift index 6fc0f342145fd..8720b4c057660 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/NamedReturnValueOptimization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/NamedReturnValueOptimization.swift @@ -78,7 +78,7 @@ private func findCopyForNRVO(for outArg: FunctionArgument) -> CopyAddrInst? { // %local = alloc_stack $T // store %in to %local : $*T // copy_addr %local to [init] %out : $*T - if !copyToArg.isTakeOfSrc { + if !copyToArg.isTakeOfSource { return nil } diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjCBridgingOptimization.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjCBridgingOptimization.swift index 96c9cd67efe64..aa41fce4829e6 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjCBridgingOptimization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjCBridgingOptimization.swift @@ -248,7 +248,7 @@ private func removeBridgingCodeInPredecessors(of block: BasicBlock, _ context: F /// ``` /// switch_enum %0 // returned instruction /// some_bb(%1): -/// %2 = enum #some(%1) // only in case of ObjC -> Swift briding +/// %2 = enum #some(%1) // only in case of ObjC -> Swift bridging /// %3 = apply %bridging(%2) // returned by `isBridging` /// %4 = enum #some(%3) /// br continue_bb(%4) @@ -338,7 +338,7 @@ func isBridgeToSwiftCall(_ value: Value) -> ApplyInst? { let funcName = bridgingFunc.name guard bridgingFunc.hasSemanticsAttribute("bridgeFromObjectiveC") || // Currently the semantics attribute is not used, so test for specific functions, too. - // TODO: remove those checks once the briding functions are annotate with "bridgeFromObjectiveC" + // TODO: remove those checks once the bridging functions are annotated with "bridgeFromObjectiveC" // in Foundation. // // String._unconditionallyBridgeFromObjectiveC(_:) diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjectOutliner.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjectOutliner.swift index ae70c9eb22787..f0a41bc89ce4f 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjectOutliner.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjectOutliner.swift @@ -109,7 +109,8 @@ private func optimizeObjectAllocation(allocRef: AllocRefInstBase, _ context: Fun type: allocRef.type, linkage: .private, // Only if it's a COW object we can be sure that the object allocated in the global is not mutated. // If someone wants to mutate it, it has to be copied first. - isLet: endOfInitInst is EndCOWMutationInst) + isLet: endOfInitInst is EndCOWMutationInst, + markedAsUsed: false) constructObject(of: allocRef, inInitializerOf: outlinedGlobal, storesToClassFields, storesToTailElements, context) context.erase(instructions: storesToClassFields) @@ -363,13 +364,13 @@ private func constructObject(of allocRef: AllocRefInstBase, inInitializerOf global: GlobalVariable, _ storesToClassFields: [StoreInst], _ storesToTailElements: [StoreInst], _ context: FunctionPassContext) { - var cloner = StaticInitCloner(cloneTo: global, context) + var cloner = Cloner(cloneToGlobal: global, context) defer { cloner.deinitialize() } // Create the initializers for the fields var objectArgs = [Value]() for store in storesToClassFields { - objectArgs.append(cloner.clone(store.source as! SingleValueInstruction)) + objectArgs.append(cloner.cloneRecursively(globalInitValue: store.source as! SingleValueInstruction)) } let globalBuilder = Builder(staticInitializerOf: global, context) @@ -381,7 +382,7 @@ private func constructObject(of allocRef: AllocRefInstBase, for elementIdx in 0.. Bool { + // FIXME: this skip is a hack for ManualOwnership prototyping, to workaround rdar://161359163 + if function.performanceConstraints == .manualOwnership && variant == .mandatory { + return false + } + // Avoid quadratic complexity by limiting the number of visited instructions. // This limit is sufficient for most "real-world" functions, by far. var complexityBudget = 50_000 @@ -99,7 +107,7 @@ func eliminateRedundantLoads(in function: Function, while let i = inst { defer { inst = i.previous } - if let load = inst as? LoadInst { + if let load = inst as? LoadingInstruction { if !context.continueWithNextSubpassRun(for: load) { return changed } @@ -116,7 +124,59 @@ func eliminateRedundantLoads(in function: Function, return changed } -private func tryEliminate(load: LoadInst, complexityBudget: inout Int, _ context: FunctionPassContext) -> Bool { +/// Either a `load` or a `copy_addr` (which is equivalent to a load+store). +private protocol LoadingInstruction: Instruction { + var address: Value { get } + var type: Type { get } + var ownership: Ownership { get } + var loadOwnership: LoadInst.LoadOwnership { get } + var canLoadValue: Bool { get } + func trySplit(_ context: FunctionPassContext) -> Bool + func materializeLoadForReplacement(_ context: FunctionPassContext) -> LoadInst +} + +extension LoadInst : LoadingInstruction { + // We know that the type is loadable because - well - this is a load. + var canLoadValue: Bool { true } + + // Nothing to materialize, because this is already a `load`. + func materializeLoadForReplacement(_ context: FunctionPassContext) -> LoadInst { return self } +} + +extension CopyAddrInst : LoadingInstruction { + var address: Value { source } + var type: Type { address.type.objectType } + var typeIsLoadable: Bool { type.isLoadable(in: parentFunction) } + + var ownership: Ownership { + if !parentFunction.hasOwnership || type.isTrivial(in: parentFunction) { + return .none + } + // Regardless of if the copy is taking or copying, the loaded value is an owned value. + return .owned + } + + var canLoadValue: Bool { + if !source.type.isLoadable(in: parentFunction) { + // Although the original load's type is loadable (obviously), it can be projected-out + // from the copy_addr's type which might be not loadable. + return false + } + if !parentFunction.hasOwnership { + if !isTakeOfSource || !isInitializationOfDestination { + // For simplicity, bail if we would have to insert compensating retains and releases. + return false + } + } + return true + } + + func materializeLoadForReplacement(_ context: FunctionPassContext) -> LoadInst { + return replaceWithLoadAndStore(context).load + } +} + +private func tryEliminate(load: LoadingInstruction, complexityBudget: inout Int, _ context: FunctionPassContext) -> Bool { switch load.isRedundant(complexityBudget: &complexityBudget, context) { case .notRedundant: return false @@ -136,9 +196,12 @@ private func tryEliminate(load: LoadInst, complexityBudget: inout Int, _ context } } -private extension LoadInst { +private extension LoadingInstruction { func isEligibleForElimination(in variant: RedundantLoadEliminationVariant, _ context: FunctionPassContext) -> Bool { + if !canLoadValue { + return false + } switch variant { case .mandatory, .mandatoryInGlobalInit: if loadOwnership == .take { @@ -171,20 +234,6 @@ private extension LoadInst { return true } - enum DataflowResult { - case notRedundant - case redundant([AvailableValue]) - case maybePartiallyRedundant(AccessPath) - - init(notRedundantWith subPath: AccessPath?) { - if let subPath = subPath { - self = .maybePartiallyRedundant(subPath) - } else { - self = .notRedundant - } - } - } - func isRedundant(complexityBudget: inout Int, _ context: FunctionPassContext) -> DataflowResult { return isRedundant(at: address.constantAccessPath, complexityBudget: &complexityBudget, context) } @@ -285,7 +334,7 @@ private extension LoadInst { } } -private func replace(load: LoadInst, with availableValues: [AvailableValue], _ context: FunctionPassContext) { +private func replace(load: LoadingInstruction, with availableValues: [AvailableValue], _ context: FunctionPassContext) { var ssaUpdater = SSAUpdater(function: load.parentFunction, type: load.type, ownership: load.ownership, context) @@ -318,14 +367,16 @@ private func replace(load: LoadInst, with availableValues: [AvailableValue], _ c newValue = ssaUpdater.getValue(inMiddleOf: load.parentBlock) } + let originalLoad = load.materializeLoadForReplacement(context) + // Make sure to keep dependencies valid after replacing the load - insertMarkDependencies(for: load, context) + insertMarkDependencies(for: originalLoad, context) - load.replace(with: newValue, context) + originalLoad.replace(with: newValue, context) } private func provideValue( - for load: LoadInst, + for load: LoadingInstruction, from availableValue: AvailableValue, _ context: FunctionPassContext ) -> Value { @@ -341,9 +392,9 @@ private func provideValue( builder: availableValue.getBuilderForProjections(context)) case .take: if projectionPath.isEmpty { - return shrinkMemoryLifetime(from: load, to: availableValue, context) + return shrinkMemoryLifetime(to: availableValue, context) } else { - return shrinkMemoryLifetimeAndSplit(from: load, to: availableValue, projectionPath: projectionPath, context) + return shrinkMemoryLifetimeAndSplit(to: availableValue, projectionPath: projectionPath, context) } } } @@ -366,7 +417,7 @@ private func insertMarkDependencies(for load: LoadInst, _ context: FunctionPassC private struct MarkDependenceInserter : AddressUseDefWalker { let load: LoadInst let context: FunctionPassContext - + mutating func walkUp(address: Value, path: UnusedWalkingPath) -> WalkResult { if let mdi = address as? MarkDependenceInst { let builder = Builder(after: load, context) @@ -375,7 +426,7 @@ private struct MarkDependenceInserter : AddressUseDefWalker { } return walkUpDefault(address: address, path: path) } - + mutating func rootDef(address: Value, path: UnusedWalkingPath) -> WalkResult { return .continueWalk } @@ -392,7 +443,7 @@ private struct MarkDependenceInserter : AddressUseDefWalker { /// ... /// // replace %2 with %1 /// -private func shrinkMemoryLifetime(from load: LoadInst, to availableValue: AvailableValue, _ context: FunctionPassContext) -> Value { +private func shrinkMemoryLifetime(to availableValue: AvailableValue, _ context: FunctionPassContext) -> Value { switch availableValue { case .viaLoad(let availableLoad): assert(availableLoad.loadOwnership == .copy) @@ -442,7 +493,7 @@ private func shrinkMemoryLifetime(from load: LoadInst, to availableValue: Availa /// ... /// // replace %3 with %1 /// -private func shrinkMemoryLifetimeAndSplit(from load: LoadInst, to availableValue: AvailableValue, projectionPath: SmallProjectionPath, _ context: FunctionPassContext) -> Value { +private func shrinkMemoryLifetimeAndSplit(to availableValue: AvailableValue, projectionPath: SmallProjectionPath, _ context: FunctionPassContext) -> Value { switch availableValue { case .viaLoad(let availableLoad): assert(availableLoad.loadOwnership == .copy) @@ -462,6 +513,20 @@ private func shrinkMemoryLifetimeAndSplit(from load: LoadInst, to availableValue } } +private enum DataflowResult { + case notRedundant + case redundant([AvailableValue]) + case maybePartiallyRedundant(AccessPath) + + init(notRedundantWith subPath: AccessPath?) { + if let subPath = subPath { + self = .maybePartiallyRedundant(subPath) + } else { + self = .notRedundant + } + } +} + /// Either a `load` or `store` which is preceding the original load and provides the loaded value. private enum AvailableValue { case viaLoad(LoadInst) @@ -505,7 +570,7 @@ private extension Array where Element == AvailableValue { func replaceCopyAddrsWithLoadsAndStores(_ context: FunctionPassContext) -> [AvailableValue] { return map { if case .viaCopyAddr(let copyAddr) = $0 { - return .viaStore(copyAddr.replaceWithLoadAndStore(context)) + return .viaStore(copyAddr.replaceWithLoadAndStore(context).store) } else { return $0 } @@ -514,7 +579,7 @@ private extension Array where Element == AvailableValue { } private struct InstructionScanner { - private let load: LoadInst + private let load: LoadingInstruction private let accessPath: AccessPath private let storageDefBlock: BasicBlock? private let aliasAnalysis: AliasAnalysis @@ -522,7 +587,7 @@ private struct InstructionScanner { private(set) var potentiallyRedundantSubpath: AccessPath? = nil private(set) var availableValues = Array() - init(load: LoadInst, accessPath: AccessPath, _ aliasAnalysis: AliasAnalysis) { + init(load: LoadingInstruction, accessPath: AccessPath, _ aliasAnalysis: AliasAnalysis) { self.load = load self.accessPath = accessPath self.storageDefBlock = accessPath.base.reference?.referenceRoot.parentBlock @@ -616,7 +681,7 @@ private struct InstructionScanner { potentiallyRedundantSubpath = precedingStorePath } - case let preceedingCopy as CopyAddrInst where preceedingCopy.canProvideValue: + case let preceedingCopy as CopyAddrInst where preceedingCopy.canLoadValue: let copyPath = preceedingCopy.destination.constantAccessPath if copyPath.getMaterializableProjection(to: accessPath) != nil { availableValues.append(.viaCopyAddr(preceedingCopy)) @@ -712,20 +777,3 @@ private struct Liverange { return false } } - -private extension CopyAddrInst { - var canProvideValue: Bool { - if !source.type.isLoadable(in: parentFunction) { - // Although the original load's type is loadable (obviously), it can be projected-out - // from the copy_addr's type which might be not loadable. - return false - } - if !parentFunction.hasOwnership { - if !isTakeOfSrc || !isInitializationOfDest { - // For simplicity, bail if we would have to insert compensating retains and releases. - return false - } - } - return true - } -} diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift index 678024ac9c9c2..07b0cac69f05f 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift @@ -110,7 +110,7 @@ func runSimplification(on function: Function, _ context: FunctionPassContext, } while !worklist.isEmpty if context.needFixStackNesting { - function.fixStackNesting(context) + context.fixStackNesting(in: function) } return changed diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/StackPromotion.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/StackPromotion.swift index a39b04eeb8efd..30994a76c5bf7 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/StackPromotion.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/StackPromotion.swift @@ -57,7 +57,7 @@ let stackPromotion = FunctionPass(name: "stack-promotion") { } if needFixStackNesting { // Make sure that all stack allocating instructions are nested correctly. - function.fixStackNesting(context) + context.fixStackNesting(in: function) } } @@ -286,7 +286,7 @@ private struct ComputeOuterBlockrange : EscapeVisitorWithResult { result.insert(operandsDefinitionBlock) // We need to explicitly add predecessor blocks of phis because they - // are not necesesarily visited during the down-walk in `isEscaping()`. + // are not necessarily visited during the down-walk in `isEscaping()`. // This is important for the special case where there is a back-edge from the // inner range to the inner rage's begin-block: // @@ -330,17 +330,3 @@ private extension BasicBlockRange { exits.contains { !deadEndBlocks.isDeadEnd($0) && !$0.hasSinglePredecessor } } } - -private func isInLoop(block startBlock: BasicBlock, _ context: FunctionPassContext) -> Bool { - var worklist = BasicBlockWorklist(context) - defer { worklist.deinitialize() } - - worklist.pushIfNotVisited(contentsOf: startBlock.successors) - while let block = worklist.pop() { - if block == startBlock { - return true - } - worklist.pushIfNotVisited(contentsOf: block.successors) - } - return false -} diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempLValueElimination.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempLValueElimination.swift new file mode 100644 index 0000000000000..83bfdb1411371 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempLValueElimination.swift @@ -0,0 +1,315 @@ +//===--- TempLValueElimination.swift ---------------------------------------==// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 AST +import SIL + +/// Eliminates copies from a temporary (an "l-value") to a destination. +/// +/// ``` +/// %temp = alloc_stack $T +/// ... -+ +/// store %x to %temp | no reads or writes to %destination +/// ... -+ +/// copy_addr [take] %temp to [init] %destination +/// dealloc_stack %temp +/// ``` +/// -> +/// ``` +/// ... +/// store %x to %destination +/// ... +/// ``` +/// +/// The name TempLValueElimination refers to the TempRValueElimination pass, which performs +/// a related transformation, just with the temporary on the "right" side. +/// +/// The pass also performs a peephole optimization on `copy_addr` - `destroy_addr` sequences. +/// It replaces +/// +/// ``` +/// copy_addr %source to %destination +/// destroy_addr %source +/// ``` +/// -> +/// ``` +/// copy_addr [take] %source to %destination +/// ``` +/// +let tempLValueElimination = FunctionPass(name: "temp-lvalue-elimination") { + (function: Function, context: FunctionPassContext) in + + for inst in function.instructions { + switch inst { + case let copy as CopyAddrInst: + combineWithDestroy(copy: copy, context) + tryEliminate(copy: copy, context) + case let store as StoreInst: + // Also handle `load`-`store` pairs which are basically the same thing as a `copy_addr`. + if let load = store.source as? LoadInst, load.uses.isSingleUse, load.parentBlock == store.parentBlock { + tryEliminate(copy: store, context) + } + default: + break + } + } +} + +private func tryEliminate(copy: CopyLikeInstruction, _ context: FunctionPassContext) { + guard let allocStack = copy.sourceAddress as? AllocStackInst, + allocStack.isDeallocatedInSameBlock(as: copy) + else { + return + } + let isTrivial = allocStack.type.isTrivial(in: copy.parentFunction) + guard copy.isTakeOfSource || isTrivial else { + return + } + + // We need to move all destination address projections at the begin of the alloc_stack liverange, + // because we are replacing the alloc_stack uses with the destination. + // ``` + // %destination = struct_element_addr %1 + // stores to %temp --> stores to %destination + // %destination = struct_element_addr %1 + // copy_addr [take] %temp to %destination + // ``` + var projections = InstructionSet(context) + defer { projections.deinitialize() } + let destinationRootAddress = collectMovableProjections(of: copy.destinationAddress, in: &projections) + + // If true we need to explicitly destroy the destination at the begin of the liverange. + // ``` + // destroy_addr %destination + // stores to %temp --> stores to %destination + // copy_addr [take] %temp to %destination + // ``` + let needDestroyEarly = !copy.isInitializationOfDestination && !isTrivial + + let firstUseOfAllocStack = InstructionList(first: allocStack).first(where: { $0.isUsing(allocStack) }) ?? + // The conservative default, if the fist use is not in the alloc_stack's block. + allocStack.parentBlock.terminator + + if firstUseOfAllocStack == copy.loadingInstruction { + // The alloc_stack is not written yet at the point of the copy. This is a very unusual corner case + // which can only happen if the alloc_stack has an empty type (e.g. `$()`). + return + } + + let aliasAnalysis = context.aliasAnalysis + let calleeAnalysis = context.calleeAnalysis + + if aliasAnalysis.mayAlias(allocStack, copy.destinationAddress) { + // Catch the very unusual corner case where the copy is writing back to it's source address - the alloc_stack. + return + } + + // If exclusivity is checked for the alloc_stack we must not replace it with the copy-destination. + // If the copy-destination is also in an access-scope this would result in an exclusivity violation + // which was not there before. + guard allocStack.uses.users(ofType: BeginAccessInst.self).isEmpty else { + return + } + + var worklist = InstructionWorklist(context) + defer { worklist.deinitialize() } + worklist.pushIfNotVisited(firstUseOfAllocStack) + + // Check instructions within the liverange of the alloc_stack. + while let inst = worklist.pop() { + // If the destination root address is within the liverange it would prevent moving the projections + // before the first use. Note that if the defining instruction of `destinationRootAddress` is nil + // it can only be a function argument. + if inst == destinationRootAddress.definingInstruction { + return + } + + // Check if the destination is not accessed within the liverange of the temporary. + // This is unlikely, because the destination is initialized at the copy. + // But still, the destination could contain an initialized value which is destroyed before the copy. + if inst.mayReadOrWrite(address: copy.destinationAddress, aliasAnalysis) && + // Needed to treat `init_existential_addr` as not-writing projection. + !projections.contains(inst) + { + return + } + + // Check if replacing the alloc_stack with destination would invalidate the alias rules of indirect arguments. + if let apply = inst as? FullApplySite, + apply.hasInvalidArgumentAliasing(between: allocStack, and: copy.destinationAddress, aliasAnalysis) + { + return + } + + // We must not shrink the liverange of an existing value in the destination. + if needDestroyEarly && inst.isDeinitBarrier(calleeAnalysis) { + return + } + + worklist.pushSuccessors(of: inst, ignoring: copy) + } + + if allocStack.isReadOrWritten(after: copy.loadingInstruction, aliasAnalysis) { + // Bail in the unlikely case of the alloc_stack is re-initialized after its value has been taken by `copy`. + return + } + + moveProjections(of: copy.destinationAddress, within: worklist, before: firstUseOfAllocStack, context) + + if needDestroyEarly { + // Make sure the destination is uninitialized before the liverange of the temporary. + let builder = Builder(before: firstUseOfAllocStack, context) + builder.createDestroyAddr(address: copy.destinationAddress) + } + + // Replace all uses of the temporary with the destination address. + for use in allocStack.uses { + switch use.instruction { + case let deallocStack as DeallocStackInst: + context.erase(instruction: deallocStack) + default: + use.set(to: copy.destinationAddress, context) + } + } + context.erase(instruction: allocStack) + context.erase(instructionIncludingAllUsers: copy.loadingInstruction) +} + +private extension FullApplySite { + /// Returns true if after replacing `addr1` with `addr2` the apply would have invalid aliasing of + /// indirect arguments. + /// An indirect argument (except `@inout_aliasable`) must not alias with another indirect argument. + /// For example, if we would replace `addr1` with `addr2` in + /// ``` + /// apply %f(%addr1, %addr2) : (@in T) -> @out T + /// ``` + /// we would invalidate this rule. + func hasInvalidArgumentAliasing(between addr1: Value, and addr2: Value, _ aliasAnalysis: AliasAnalysis) -> Bool { + var addr1Accessed = false + var addr2Accessed = false + var mutatingAccess = false + for argOp in argumentOperands { + let convention = convention(of: argOp)! + if convention.isExclusiveIndirect { + if aliasAnalysis.mayAlias(addr1, argOp.value) { + addr1Accessed = true + if !convention.isGuaranteed { + mutatingAccess = true + } + } else if aliasAnalysis.mayAlias(addr2, argOp.value) { + addr2Accessed = true + if !convention.isGuaranteed { + mutatingAccess = true + } + } + } + } + return mutatingAccess && addr1Accessed && addr2Accessed + } +} + +/// Replace +/// ``` +/// copy_addr %source to %destination --> copy_addr [take] %source to %destination +/// destroy_addr %source +/// ``` +private func combineWithDestroy(copy: CopyAddrInst, _ context: FunctionPassContext) { + guard !copy.isTakeOfSource else { + return + } + + // Check if the destroy_addr is after the copy_addr and if there are no memory accesses between them. + var debugInsts = Stack(context) + defer { debugInsts.deinitialize() } + + for inst in InstructionList(first: copy.next) { + switch inst { + case let destroy as DestroyAddrInst where destroy.destroyedAddress == copy.source: + copy.set(isTakeOfSource: true, context) + context.erase(instruction: destroy) + // Don't let debug info think that the value is still valid after the `copy [take]`. + context.erase(instructions: debugInsts) + return + case let debugInst as DebugValueInst where debugInst.operand.value == copy.source: + debugInsts.append(debugInst) + default: + if inst.mayReadOrWriteMemory { + return + } + } + } +} + +private extension Value { + var isMovableProjection: (SingleValueInstruction & UnaryInstruction)? { + switch self { + case let projectionInst as InitEnumDataAddrInst: return projectionInst + case let projectionInst as StructElementAddrInst: return projectionInst + case let projectionInst as TupleElementAddrInst: return projectionInst + case let projectionInst as UncheckedTakeEnumDataAddrInst: return projectionInst + case let projectionInst as InitExistentialAddrInst: return projectionInst + case let projectionInst as RefElementAddrInst: return projectionInst + case let projectionInst as RefTailAddrInst: return projectionInst + case let projectionInst as ProjectBoxInst: return projectionInst + default: return nil + } + } +} + +private func collectMovableProjections(of address: Value, in projections: inout InstructionSet) -> Value { + var a = address + while let projection = a.isMovableProjection { + projections.insert(projection) + a = projection.operand.value + } + return a +} + +private func moveProjections( + of address: Value, + within worklist: InstructionWorklist, + before insertionPoint: Instruction, + _ context: FunctionPassContext +) { + var a = address + var ip = insertionPoint + while let projection = a.isMovableProjection, + worklist.hasBeenPushed(projection) + { + projection.move(before: ip, context) + a = projection.operand.value + ip = projection + } +} + +private extension AllocStackInst { + func isReadOrWritten(after afterInst: Instruction, _ aliasAnalysis: AliasAnalysis) -> Bool { + for inst in InstructionList(first: afterInst.next) { + if let deallocStack = inst as? DeallocStackInst, deallocStack.allocatedValue == self { + return false + } + if inst.mayReadOrWrite(address: self, aliasAnalysis) { + return true + } + } + fatalError("dealloc_stack expected to be in same block as `afterInst`") + } + + func isDeallocatedInSameBlock(as inst: Instruction) -> Bool { + if let deallocStack = uses.users(ofType: DeallocStackInst.self).singleElement, + deallocStack.parentBlock == inst.parentBlock + { + return true + } + return false + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempRValueElimination.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempRValueElimination.swift new file mode 100644 index 0000000000000..b5cac210cb359 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/TempRValueElimination.swift @@ -0,0 +1,552 @@ +//===--- TempRValueElimination.swift ---------------------------------------==// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 AST +import SIL + +/// Eliminates copies to `alloc_stack` "temporaries" (r-values). +/// +/// ``` +/// %temp = alloc_stack $T +/// copy_addr %src to [init] %temp -+ +/// ... | +/// %l = load %temp | no writes to %src or %temp +/// ... | +/// destroy_addr %temp -+ +/// dealloc_stack %temp +/// ``` +/// -> +/// ``` +/// %l = load %src +/// ``` +/// +/// This differs from the copy forwarding algorithm because it handles copy source and dest lifetimes +/// that are unavoidably overlapping. Instead, 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". +/// +let tempRValueElimination = FunctionPass(name: "temp-rvalue-elimination") { + (function: Function, context: FunctionPassContext) in + removeTempRValues(in: function, keepDebugInfo: false, context) +} + +let mandatoryTempRValueElimination = FunctionPass(name: "mandatory-temp-rvalue-elimination") { + (function: Function, context: FunctionPassContext) in + removeTempRValues(in: function, keepDebugInfo: true, context) +} + +private func removeTempRValues(in function: Function, keepDebugInfo: Bool, _ context: FunctionPassContext) { + for inst in function.instructions { + switch inst { + case let copy as CopyAddrInst: + if copy.source == copy.destination { + // Remove identity copies which may have been created by an earlier iteration, where another `copy_addr` + // copied the `alloc_stack` back to the source location. + context.erase(instruction: copy) + } else { + tryEliminate(copy: copy, keepDebugInfo: keepDebugInfo, context) + } + case let store as StoreInst: + // Also handle `load`-`store` pairs which are basically the same thing as a `copy_addr`. + if let load = store.source as? LoadInst, load.uses.isSingleUse, load.parentBlock == store.parentBlock { + tryEliminate(copy: store, keepDebugInfo: keepDebugInfo, context) + } + default: + break + } + } +} + +private func tryEliminate(copy: CopyLikeInstruction, keepDebugInfo: Bool, _ context: FunctionPassContext) { + + guard let (allocStack, lastUseOfAllocStack) = + getRemovableAllocStackDestination(of: copy, keepDebugInfo: keepDebugInfo, context) else { + return + } + + guard extendAccessScopes(beyond: lastUseOfAllocStack, copy: copy, context) else { + return + } + + if needToInsertDestroy(copy: copy, lastUseOfAllocStack: lastUseOfAllocStack) { + Builder.insert(after: lastUseOfAllocStack, context) { builder in + builder.createDestroyAddr(address: copy.sourceAddress) + } + } + + for use in allocStack.uses { + switch use.instruction { + case is DestroyAddrInst, is DeallocStackInst: + context.erase(instruction: use.instruction) + case let cai as CopyAddrInst where cai != copy: + if cai.isTakeOfSource, !copy.isTakeOfSource { + // If the original copy is not taking its source, a `copy_addr` from the `allocStack` must + // not take its source either. + assert(cai == lastUseOfAllocStack && cai.source == allocStack) + cai.set(isTakeOfSource: false, context) + } + use.set(to: copy.sourceAddress, context) + case let load as LoadInst: + if load.loadOwnership == .take, !copy.isTakeOfSource { + // If the original copy is not taking its source, a `load` from the `allocStack` must + // not take its source either. + assert(load == lastUseOfAllocStack) + load.set(ownership: .copy, context) + } + use.set(to: copy.sourceAddress, context); + + default: + // Note that no operations that may be handled by this default clause can destroy the `allocStack`. + // This includes operations that load the value from memory and release it or cast the address + // before destroying it. + use.set(to: copy.sourceAddress, context); + } + } + context.erase(instruction: allocStack) + if keepDebugInfo { + Builder(before: copy, context).createDebugStep() + } + context.erase(instructionIncludingAllUsers: copy.loadingInstruction) +} + +/// Checks if the `copy` is copying into an `alloc_stack` which is removable: +/// ``` +/// %allocStack = alloc_stack $T +/// copy_addr %src to [init] %allocStack +/// ... +/// %lastUseOfAllocStack = load %allocStack +/// ``` +private func getRemovableAllocStackDestination( + of copy: CopyLikeInstruction, keepDebugInfo: Bool, _ context: FunctionPassContext +) -> (allocStack: AllocStackInst, lastUseOfAllocStack: Instruction)? { + guard copy.isInitializationOfDestination, + let allocStack = copy.destinationAddress as? AllocStackInst + else { + return nil + } + + if keepDebugInfo { + if allocStack.isFromVarDecl || allocStack.isLexical { + return nil + } + } else { + // If the `allocStack` is lexical we can eliminate it if the source of the copy is lexical and + // it is live for longer than the `allocStack`. + if allocStack.isLexical && !copy.sourceAddress.accessBase.storageIsLexical { + return nil + } + } + + var allocStackUses = UseCollector(copy: copy, context) + defer { allocStackUses.deinitialize() } + + // Scan all uses of the `allocStack` to verify only the `copy` is writing to it and that all uses + // (except `destroy_addr` and `dealloc_stack`) are in the same basic block. + guard allocStackUses.collectUses(of: allocStack) else { + return nil + } + + // Check that there are no uses of the `allocStack` that precede the copyInst. This can happen with projections. + // TODO: We can enable this case if we clone the projections at "load" uses. + if allocStack.hasUses(before: copy, context) { + return nil + } + + // Check if the source is modified within the lifetime of the 'alloc_stack'. + guard let lastUseOfAllocStack = getLastUseWhileSourceIsNotModified(of: copy, uses: allocStackUses.uses, context) else { + return nil + } + + // We cannot insert the destroy of `copy.source` after `lastUseOfAllocStack` if `copy.source` is + // re-initialized by exactly this instruction. + // This is a corner case, but can happen if `lastUseOfAllocStack` is a `copy_addr`. Example: + // ``` + // copy_addr [take] %src to [init] %allocStack // `copy` + // copy_addr [take] %allocStack to [init] %src // `lastUseOfAllocStack` + // ``` + if copy.isTakeOfSource, + lastUseOfAllocStack != copy, + !(lastUseOfAllocStack is DestroyAddrInst), + lastUseOfAllocStack.mayWrite(toAddress: copy.sourceAddress, context.aliasAnalysis) + { + return nil + } + + // Bail if in non-OSSA the `allocStack` is destroyed in a non-obvious way, e.g. by + // ``` + // %x = load %allocStack // looks like a load, but is a `load [take]` + // strong_release %x + // ``` + guard copy.parentFunction.hasOwnership || allocStack.isDestroyedOnAllPaths(context) else { + return nil + } + + return (allocStack, lastUseOfAllocStack) +} + +/// We need to insert a final destroy if the original `copy` is taking the source but the +/// `lastUseOfAllocStack` is not taking the `alloc_stack`. +private func needToInsertDestroy(copy: CopyLikeInstruction, lastUseOfAllocStack: Instruction) -> Bool { + if !copy.isTakeOfSource { + return false + } + switch lastUseOfAllocStack { + case copy: + return true + case let cai as CopyAddrInst: + if cai.isTakeOfSource { + assert(cai.source == copy.destinationAddress, "copy_addr must be not take a projected address") + return false + } + return true + case let li as LoadInst: + if li.loadOwnership == .take { + assert(li.address == copy.destinationAddress, "load must be not take a projected address") + return false + } + return true + default: + return true + } +} + +private extension AllocStackInst { + func hasUses(before instruction: Instruction, _ context: FunctionPassContext) -> Bool { + var useSet = InstructionSet(context) + defer { useSet.deinitialize() } + + useSet.insert(contentsOf: self.users) + for inst in InstructionList(first: self) { + if inst == instruction { + return false + } + if useSet.contains(inst) { + return true + } + } + return false + } + + /// In non-OSSA, check if the `alloc_stack` destroyed in an obvious way and not e.g. implicitly by + /// ``` + /// %x = load %allocStack // looks like a load, but is a `load [take]` + /// strong_release %x + /// ``` + func isDestroyedOnAllPaths(_ context: FunctionPassContext) -> Bool { + var liferange = InstructionRange(begin: self, context) + defer { liferange.deinitialize() } + + liferange.insert(contentsOf: uses.ignoreUses(ofType: DeallocStackInst.self).lazy.map { $0.instruction }) + + for use in uses { + switch use.instruction { + case is DeallocStackInst, is DestroyAddrInst: + break + case let c as CopyAddrInst where c.sourceOperand == use && c.isTakeOfSource: + break + default: + if !liferange.contains(use.instruction) { + // A non-destroying instruction is at the end of the liferange -> we are missing a "real" destroy. + return false + } + } + } + return true + } +} + +/// Tries to move an `end_access` down to extend the access scope over all uses of the `alloc_stack`. +/// For example: +/// ``` +/// %a = begin_access %src +/// copy_addr %a to [init] %temp : $*T +/// end_access %a +/// use %temp +/// ``` +/// We must not replace %temp with %a after the `end_access`. Instead we try to move the `end_access` +/// after the last use. +private func extendAccessScopes(beyond lastUse: Instruction, copy: CopyLikeInstruction, + _ context: FunctionPassContext) -> Bool +{ + var endAccessToMove: EndAccessInst? = nil + + for inst in InstructionList(first: copy.next) { + if let endAccess = inst as? EndAccessInst { + // To keep things simple, we can just move a single `end_access`. Also, we cannot move an + // `end_access` over a (non-aliasing) other `end_access`. + if endAccessToMove != nil { + return false + } + if context.aliasAnalysis.mayAlias(copy.sourceAddress, endAccess.beginAccess.address), + // There cannot be any aliasing modifying accesses within the liverange of the `alloc_stack`, + // because we would have cought this in `getLastUseWhileSourceIsNotModified`. + // But there are cases where `aliasAnalysis.mayAlias` is less precise than `Instruction.mayWrite`. + // Therefore, just ignore any non-read accesses. + endAccess.beginAccess.accessKind == .read + { + // We cannot move instructions beyond the block's terminator. + if lastUse is TermInst { + return false + } + endAccessToMove = endAccess + } + } else if let endAccessToMove { + // We cannot move an `end_access` over a `begin_access`. This would destroy the proper nesting of accesses. + if inst is BeginAccessInst || inst is BeginUnpairedAccessInst { + return false + } + // Don't extend a read-access scope over a (potential) write. + // Note that `inst` can be a function call containing other access scopes. But doing the `inst.mayWrite` + // check, we know that the function can only contain read accesses (to the same memory location). + // So it's fine to move `endAccessToMove` even over such a function call. + if inst.mayWrite(toAddress: endAccessToMove.beginAccess.address, context.aliasAnalysis) { + return false + } + } + if inst == lastUse { + break + } + } + if let endAccessToMove { + endAccessToMove.move(before: lastUse.next!, context) + } + + return true +} + +/// Checks if the source of `copy` is not modified within the `alloc_stack`'s lifetime, i.e. is not modified +/// before the last use of `uses`. +/// +/// If there are no source modifications with the lifetime, returns the last user. Otherwise returns nil. +/// +/// Unfortunately, we cannot simply use the destroy points as the lifetime end, because they can be in a +/// different basic block. Instead we look for the last non-destroy, non-dealloc use. +private func getLastUseWhileSourceIsNotModified(of copy: CopyLikeInstruction, + uses: InstructionSetWithCount, + _ context: FunctionPassContext) -> Instruction? +{ + if uses.isEmpty { + return copy + } + var numUsesFound = 0 + let aliasAnalysis = context.aliasAnalysis + + // We already checked that the useful lifetime of the `alloc_stack` ends in the same block as the `copy`. + // Therefore we can limit our search to the instructions of this block. + for inst in InstructionList(first: copy.loadingInstruction.next) { + if uses.contains(inst) { + numUsesFound += 1 + } + + // If this is the last use of the `alloc_stack` we are okay. After this point, modifications to the source + // don't matter anymore. + // Note that we are assuming here that if an instruction reads and writes to the source at the same time + // (like a `copy_addr` could do), the write takes effect after the read. + if numUsesFound == uses.count { + // Function calls are an exception: in a called function a potential modification of `copy.source` + // could occur _before_ the read of the `alloc_stack`. + switch inst { + case is FullApplySite, is YieldInst: + if inst.mayWrite(toAddress: copy.sourceAddress, aliasAnalysis) { + return nil + } + return inst + default: + return inst + } + } + + if inst.mayWrite(toAddress: copy.sourceAddress, aliasAnalysis) { + return nil + } + } + // 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 nil +} + +/// Collects all uses of the `alloc_stack`. +private struct UseCollector : AddressDefUseWalker { + private(set) var uses: InstructionSetWithCount + private let copy: CopyLikeInstruction + + init(copy: CopyLikeInstruction, _ context: FunctionPassContext) { + self.uses = InstructionSetWithCount(context) + self.copy = copy + } + + mutating func collectUses(of allocStack: AllocStackInst) -> Bool { + for use in allocStack.uses { + switch use.instruction { + case copy: + break + case is DeallocStackInst: + // Deallocations are allowed to be in a different block. + break + case let destroyAddr as DestroyAddrInst: + if !destroyAddr.parentFunction.hasOwnership && copy.isTakeOfSource { + // In non-OSSA mode, for the purpose of inserting the destroy of `copy.source`, we have to be + // conservative and assume that the lifetime of `allocStack` goes beyond it's last use - until + // the final `destroy_addr`. Otherwise we would risk inserting the destroy too early. Therefore we + // treat the `destroy_addr` as any other use of `allocStack` and require it to be in the same block. + if destroyAddr.parentBlock != copy.parentBlock { + return false + } + uses.insert(destroyAddr) + } + default: + if walkDown(address: use, path: UnusedWalkingPath()) == .abortWalk { + return false + } + } + } + return true + } + + public mutating func walkDown(address operand: Operand, path: UnusedWalkingPath) -> WalkResult { + if operand.instruction.parentBlock != copy.parentBlock { + // All normal uses (except destroys and `dealloc_stack`) must be in the initialization block. + return .abortWalk + } + switch operand.instruction { + case let openExistential as OpenExistentialAddrInst: + if !openExistential.isImmutable { + return.abortWalk + } + case let takeEnum as UncheckedTakeEnumDataAddrInst: + // In certain cases, `unchecked_take_enum_data_addr` invalidates the underlying memory. Therefore, + // by default` we can not look through it... but this is not true in the case of `Optional`. + // This is an important case for us to handle, so handle it here. + if !takeEnum.enum.type.isOptional { + return .abortWalk + } + case let beginAccess as BeginAccessInst: + if beginAccess.accessKind != .read { + return .abortWalk + } + // We don't have to walk down the `beginAccess` result, because the access kind "read" already + // guarantees that there are no writes to the `beginAccess` result address. But we have to register + // the `end_access`es as uses to correctly mark the end-of-lifetime of the `alloc_stack`. + // ``` + // %addr = begin_access [read] + // ... // there can be no writes to %addr here + // end_access %addr // <- This is where the use actually ends. + // ``` + for endAccess in beginAccess.endAccessInstructions { + if endAccess.parentBlock != copy.parentBlock { + return .abortWalk + } + uses.insert(endAccess) + } + return .continueWalk + default: + break + } + return walkDownDefault(address: operand, path: path) + } + + mutating func leafUse(address: Operand, path: UnusedWalkingPath) -> WalkResult { + if address.isTypeDependent { + return .continueWalk + } + + // Only allow uses that cannot destroy their operand. We need to be sure that replacing all the uses + // with the copy source doesn't destroy the source. + switch address.instruction { + case let beginApply as BeginApplyInst: + // Extend the lifetime of the `alloc_stack` to the 'end_apply'/'abort_apply'. + for tokenUse in beginApply.token.uses { + if tokenUse.instruction.parentBlock != copy.parentBlock { + return .abortWalk + } + uses.insert(tokenUse.instruction) + } + return visitApply(address: address, apply: beginApply) + + case let partialApply as PartialApplyInst: + if !partialApply.isOnStack { + return .abortWalk + } + return visitApply(address: address, apply: partialApply) + + case let apply as ApplySite: + // Remaining applies: `apply` and `try_apply` + return visitApply(address: address, apply: apply) + + case let yield as YieldInst: + if !yield.convention(of: address).isGuaranteed { + return .abortWalk + } + uses.insert(yield) + return .continueWalk + + case let addrCast as UncheckedAddrCastInst: + return walkDownUses(ofAddress: addrCast, path: UnusedWalkingPath()) + + case let load as LoadInst: + if load.loadOwnership == .take, + // Only accept `load [take]` if it takes the whole `alloc_stack`. A `load [take]` from + // a projection would destroy only a part of the `alloc_stack` and we don't handle this. + load.address != copy.destinationAddress + { + return .abortWalk + } + uses.insert(load) + return .continueWalk + + case let loadBorrow as LoadBorrowInst: + uses.insert(loadBorrow) + for end in loadBorrow.uses.endingLifetime.users { + if end.parentBlock != copy.parentBlock || end is BranchInst { + return .abortWalk + } + uses.insert(end) + } + return .continueWalk + + case let fixLifetime as FixLifetimeInst: + // After removing the `alloc_stack` the `fix_lifetime` will apply for the `copy.source`. + uses.insert(fixLifetime) + return .continueWalk + + case let copyFromStack as CopyAddrInst: + if copyFromStack.destinationOperand == address { + return .abortWalk + } + // As with `load [take]`, only accept `copy_addr [take]` if it takes the whole `alloc_stack`. + if copyFromStack.isTakeOfSource && copyFromStack.source != copy.destinationAddress { + return .abortWalk + } + uses.insert(copyFromStack) + return .continueWalk + + default: + return .abortWalk + } + } + + private mutating func visitApply(address: Operand, apply: ApplySite) -> WalkResult { + if !apply.convention(of: address)!.isGuaranteed { + return .abortWalk + } + uses.insert(apply) + return .continueWalk + } + + public mutating func unmatchedPath(address: Operand, path: UnusedWalkingPath) -> WalkResult { + return .abortWalk + } + + mutating func deinitialize() { + uses.deinitialize() + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index 1cb0130076f90..02a9a02e01f90 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -19,25 +19,31 @@ swift_compiler_sources(Optimizer SimplifyCondBranch.swift SimplifyCondFail.swift SimplifyConvertEscapeToNoEscape.swift + SimplifyCopyBlock.swift SimplifyCopyValue.swift SimplifyDebugStep.swift SimplifyDestroyValue.swift SimplifyDestructure.swift + SimplifyEndCOWMutationAddr.swift + SimplifyExplicitCopy.swift SimplifyFixLifetime.swift SimplifyGlobalValue.swift SimplifyInitEnumDataAddr.swift SimplifyKeyPath.swift SimplifyLoad.swift + SimplifyMarkDependence.swift SimplifyMisc.swift SimplifyPartialApply.swift SimplifyPointerToAddress.swift SimplifyRefCasts.swift SimplifyRetainReleaseValue.swift SimplifyStrongRetainRelease.swift + SimplifyStruct.swift SimplifyStructExtract.swift SimplifySwitchEnum.swift SimplifyTuple.swift SimplifyTupleExtract.swift + SimplifyUncheckedAddrCast.swift SimplifyUncheckedEnumData.swift SimplifyValueToBridgeObject.swift SimplifyWitnessMethod.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyAllocStack.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyAllocStack.swift index 85c6d297e6671..4af01ed30b8a7 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyAllocStack.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyAllocStack.swift @@ -179,7 +179,8 @@ private extension AllocStackInst { /// use %3 /// ``` func optimizeExistential(_ context: SimplifyContext) -> Bool { - guard type.isExistential || type.isExistentialArchetype, + // TODO: support non-root existential archetypes + guard type.isExistential || type.isRootExistentialArchetype, let concreteFormalType = getConcreteTypeOfExistential() else { return false @@ -206,21 +207,21 @@ private extension AllocStackInst { iea.replace(with: newAlloc, context) } case let oea as OpenExistentialAddrInst: - assert(oea.uses.ignoreUsers(ofType: DestroyAddrInst.self).isEmpty) + assert(oea.uses.ignoreUses(ofType: DestroyAddrInst.self).isEmpty) oea.replace(with: newAlloc, context) case let cab as CheckedCastAddrBranchInst: let builder = Builder(before: cab, context) builder.createCheckedCastAddrBranch( source: newAlloc, sourceFormalType: concreteFormalType, destination: cab.destination, targetFormalType: cab.targetFormalType, - isolatedConformances: cab.isolatedConformances, + options: cab.checkedCastOptions, consumptionKind: cab.consumptionKind, successBlock: cab.successBlock, failureBlock: cab.failureBlock) context.erase(instruction: cab) case let ucca as UnconditionalCheckedCastAddrInst: let builder = Builder(before: ucca, context) builder.createUnconditionalCheckedCastAddr( - isolatedConformances: ucca.isolatedConformances, + options: ucca.checkedCastOptions, source: newAlloc, sourceFormalType: concreteFormalType, destination: ucca.destination, targetFormalType: ucca.targetFormalType) context.erase(instruction: ucca) @@ -246,7 +247,7 @@ private extension AllocStackInst { is DebugValueInst: break case let oea as OpenExistentialAddrInst: - if !oea.uses.ignoreUsers(ofType: DestroyAddrInst.self).isEmpty { + if !oea.uses.ignoreUses(ofType: DestroyAddrInst.self).isEmpty { return nil } case let iea as InitExistentialAddrInst: @@ -257,11 +258,15 @@ private extension AllocStackInst { case is CheckedCastAddrBranchInst, is UnconditionalCheckedCastAddrInst: // To construct a new cast instruction we need a formal type. requiresLegalFormalType = true - fallthrough - case is UncheckedAddrCastInst: if use != use.instruction.operands[0] { return nil } + case is UncheckedAddrCastInst: + if self.type.isExistential { + // Bail if the address of the original existential escapes. + // This is not a problem if the alloc_stack already contains the opened existential. + return nil + } default: return nil } diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift index 7def68d638958..5755f8b998d01 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift @@ -28,6 +28,9 @@ extension ApplyInst : OnoneSimplifiable, SILCombineSimplifiable { if tryRemoveArrayCast(apply: self, context) { return } + if tryOptimizeEnumComparison(apply: self, context) { + return + } if !context.preserveDebugInfo { _ = tryReplaceExistentialArchetype(of: self, context) } @@ -110,6 +113,49 @@ private func tryRemoveArrayCast(apply: ApplyInst, _ context: SimplifyContext) -> return true } +/// Optimize (the very inefficient) RawRepresentable comparison to a simple compare of enum tags. +/// For example, +/// ``` +/// enum E: String { +/// case a, b, c +/// } +/// ``` +/// is compared by getting the raw values of both operands and doing a string compare. +/// This peephole optimizations replaces the call to such a comparison function with a direct compare of +/// the enum tags, which boils down to a single integer comparison instruction. +/// +private func tryOptimizeEnumComparison(apply: ApplyInst, _ context: SimplifyContext) -> Bool { + guard let callee = apply.referencedFunction, + apply.arguments.count == 2, + callee.hasSemanticsAttribute("rawrepresentable.is_equal"), + apply.type.isStruct + else { + return false + } + let lhs = apply.arguments[0] + let rhs = apply.arguments[1] + guard let enumDecl = lhs.type.nominal as? EnumDecl, + enumDecl.hasRawType, + !enumDecl.isResilient(in: apply.parentFunction), + !enumDecl.hasClangNode, + lhs.type.isAddress, + lhs.type == rhs.type + else { + return false + } + let builder = Builder(before: apply, context) + let tagType = context.getBuiltinIntegerType(bitWidth: 32) + let lhsTag = builder.createBuiltin(name: "getEnumTag", type: tagType, + substitutions: apply.substitutionMap, arguments: [lhs]) + let rhsTag = builder.createBuiltin(name: "getEnumTag", type: tagType, + substitutions: apply.substitutionMap, arguments: [rhs]) + let builtinBoolType = context.getBuiltinIntegerType(bitWidth: 1) + let cmp = builder.createBuiltin(name: "cmp_eq_Int32", type: builtinBoolType, arguments: [lhsTag, rhsTag]) + let booleanResult = builder.createStruct(type: apply.type, elements: [cmp]) + apply.replace(with: booleanResult, context) + return true +} + /// If the apply uses an existential archetype (`@opened("...")`) and the concrete type is known, /// replace the existential archetype with the concrete type /// 1. in the apply's substitution map @@ -131,7 +177,7 @@ private func tryReplaceExistentialArchetype(of apply: ApplyInst, _ context: Simp let newApply = builder.createApply( function: apply.callee, - apply.replaceOpenedArchetypeInSubstituations(withConcreteType: concreteType, context), + apply.replaceOpenedArchetypeInSubstitutions(withConcreteType: concreteType, context), arguments: apply.replaceExistentialArchetypeInArguments(withConcreteType: concreteType, context), isNonThrowing: apply.isNonThrowing, isNonAsync: apply.isNonAsync, specializationInfo: apply.specializationInfo) @@ -151,7 +197,7 @@ private func tryReplaceExistentialArchetype(of tryApply: TryApplyInst, _ context builder.createTryApply( function: tryApply.callee, - tryApply.replaceOpenedArchetypeInSubstituations(withConcreteType: concreteType, context), + tryApply.replaceOpenedArchetypeInSubstitutions(withConcreteType: concreteType, context), arguments: tryApply.replaceExistentialArchetypeInArguments(withConcreteType: concreteType, context), normalBlock: tryApply.normalBlock, errorBlock: tryApply.errorBlock, isNonAsync: tryApply.isNonAsync, @@ -170,8 +216,9 @@ private extension FullApplySite { // Make sure that existential archetype _is_ a replacement type and not e.g. _contained_ in a // replacement type, like // apply %1() - guard substitutionMap.replacementTypes.contains(where: { $0.isExistentialArchetype }), - substitutionMap.replacementTypes.allSatisfy({ $0.isExistentialArchetype || !$0.hasLocalArchetype }) + // TODO: support non-root existential archetypes + guard substitutionMap.replacementTypes.contains(where: { $0.isRootExistentialArchetype }), + substitutionMap.replacementTypes.allSatisfy({ $0.isRootExistentialArchetype || !$0.hasLocalArchetype }) else { return false } @@ -191,9 +238,9 @@ private extension FullApplySite { let type = value.type // Allow three cases: // case 1. the argument _is_ the existential archetype - return type.isExistentialArchetype || + return type.isRootExistentialArchetype || // case 2. the argument _is_ a metatype of the existential archetype - (type.isMetatype && type.canonicalType.instanceTypeOfMetatype.isExistentialArchetype) || + (type.isMetatype && type.canonicalType.instanceTypeOfMetatype.isRootExistentialArchetype) || // case 3. the argument has nothing to do with the existential archetype (or any other local archetype) !type.hasLocalArchetype } @@ -223,7 +270,7 @@ private extension FullApplySite { return Array(newArgs) } - func replaceOpenedArchetypeInSubstituations( + func replaceOpenedArchetypeInSubstitutions( withConcreteType concreteType: CanonicalType, _ context: SimplifyContext ) -> SubstitutionMap { diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift index 2bb3e1c21c289..0771dd69555d0 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift @@ -29,7 +29,7 @@ extension BeginBorrowInst : OnoneSimplifiable, SILCombineSimplifiable { extension LoadBorrowInst : Simplifiable, SILCombineSimplifiable { func simplify(_ context: SimplifyContext) { - if uses.ignoreDebugUses.ignoreUsers(ofType: EndBorrowInst.self).isEmpty { + if uses.ignoreDebugUses.ignoreUses(ofType: EndBorrowInst.self).isEmpty { context.erase(instructionIncludingAllUsers: self) return } @@ -52,7 +52,7 @@ extension LoadBorrowInst : Simplifiable, SILCombineSimplifiable { private func tryCombineWithCopy(_ context: SimplifyContext) { let forwardedValue = lookThroughSingleForwardingUses() - guard let singleUser = forwardedValue.uses.ignoreUsers(ofType: EndBorrowInst.self).singleUse?.instruction, + guard let singleUser = forwardedValue.uses.ignoreUses(ofType: EndBorrowInst.self).singleUse?.instruction, let copy = singleUser as? CopyValueInst, copy.parentBlock == self.parentBlock else { return @@ -81,13 +81,13 @@ private func tryReplaceBorrowWithOwnedOperand(beginBorrow: BeginBorrowInst, _ co private func removeBorrowOfThinFunction(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) { guard let thin2thickFn = beginBorrow.borrowedValue as? ThinToThickFunctionInst, // For simplicity don't go into the trouble of removing reborrow phi arguments. - beginBorrow.uses.filterUsers(ofType: BranchInst.self).isEmpty else + beginBorrow.uses.filterUses(ofType: BranchInst.self).isEmpty else { return } // `thin_to_thick_function` has "none" ownership and is compatible with guaranteed values. // Therefore the `begin_borrow` is not needed. - beginBorrow.uses.ignoreUsers(ofType: EndBorrowInst.self).replaceAll(with: thin2thickFn, context) + beginBorrow.uses.ignoreUses(ofType: EndBorrowInst.self).replaceAll(with: thin2thickFn, context) context.erase(instructionIncludingAllUsers: beginBorrow) } @@ -110,7 +110,7 @@ private func tryReplaceCopy( withCopiedOperandOf beginBorrow: BeginBorrowInst, _ context: SimplifyContext ) -> Bool { - guard let singleUser = forwardedValue.uses.ignoreUsers(ofType: EndBorrowInst.self).singleUse?.instruction, + guard let singleUser = forwardedValue.uses.ignoreUses(ofType: EndBorrowInst.self).singleUse?.instruction, let copy = singleUser as? CopyValueInst, copy.parentBlock == beginBorrow.parentBlock else { return false @@ -153,7 +153,7 @@ private extension Value { /// ``` /// Returns self if this value has no uses which are ForwardingInstructions. func lookThroughSingleForwardingUses() -> Value { - if let singleUse = uses.ignoreUsers(ofType: EndBorrowInst.self).singleUse, + if let singleUse = uses.ignoreUses(ofType: EndBorrowInst.self).singleUse, let fwdInst = singleUse.instruction as? (SingleValueInstruction & ForwardingInstruction), fwdInst.canConvertToOwned, fwdInst.isSingleForwardedOperand(singleUse), @@ -165,7 +165,7 @@ private extension Value { } var allUsesCanBeConvertedToOwned: Bool { - let relevantUses = uses.ignoreUsers(ofType: EndBorrowInst.self) + let relevantUses = uses.ignoreUses(ofType: EndBorrowInst.self) return relevantUses.allSatisfy { $0.canAccept(ownership: .owned) } } @@ -175,7 +175,7 @@ private extension Value { } func replaceAllDestroys(with replacement: Value, _ context: SimplifyContext) { - uses.filterUsers(ofType: DestroyValueInst.self).replaceAll(with: replacement, context) + uses.filterUses(ofType: DestroyValueInst.self).replaceAll(with: replacement, context) } } @@ -187,7 +187,7 @@ private extension Instruction { // The value and instruction are in the same block. All uses are dominated by both. return true } - let destroys = value.uses.filterUsers(ofType: DestroyValueInst.self) + let destroys = value.uses.filterUses(ofType: DestroyValueInst.self) return destroys.allSatisfy({ $0.instruction.parentBlock == parentBlock}) } } diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginCOWMutation.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginCOWMutation.swift index cd92ca9a3b9bd..5cad01b2e12e8 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginCOWMutation.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginCOWMutation.swift @@ -65,8 +65,8 @@ private extension BeginCOWMutationInst { return } let builder = Builder(before: self, location: location, context) - let zero = builder.createIntegerLiteral(0, type: uniquenessResult.type); - uniquenessResult.uses.replaceAll(with: zero, context) + let falseLiteral = builder.createBoolLiteral(false) + uniquenessResult.uses.replaceAll(with: falseLiteral, context) } func optimizeEmptyBeginEndPair(_ context: SimplifyContext) -> Bool { diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift index 0af3cc3d9ab02..c761086764164 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift @@ -12,7 +12,7 @@ import SIL -extension BuiltinInst : OnoneSimplifiable { +extension BuiltinInst : OnoneSimplifiable, SILCombineSimplifiable { func simplify(_ context: SimplifyContext) { switch id { case .IsConcrete: @@ -53,8 +53,10 @@ extension BuiltinInst : OnoneSimplifiable { constantFoldIntegerEquality(isEqual: true, context) case .ICMP_NE: constantFoldIntegerEquality(isEqual: false, context) + case .Xor: + simplifyNegation(context) default: - if let literal = constantFold(context) { + if let literal = context.constantFold(builtin: self) { uses.replaceAll(with: literal, context) } } @@ -79,7 +81,7 @@ private extension BuiltinInst { return } let builder = Builder(before: self, context) - let result = builder.createIntegerLiteral(hasArchetype ? 0 : 1, type: type) + let result = builder.createBoolLiteral(!hasArchetype) uses.replaceAll(with: result, context) context.erase(instruction: self) } @@ -92,7 +94,7 @@ private extension BuiltinInst { return } let builder = Builder(before: self, context) - let result = builder.createIntegerLiteral(equal ? 1 : 0, type: type) + let result = builder.createBoolLiteral(equal) uses.replaceAll(with: result, context) } @@ -165,6 +167,11 @@ private extension BuiltinInst { case .Sizeof: value = ty.getStaticSize(context: context) case .Strideof: + if isUsedAsStrideOfIndexRawPointer(context) { + // Constant folding `stride` would prevent index_raw_pointer simplification. + // See `simplifyIndexRawPointer` in SimplifyPointerToAddress.swift. + return + } value = ty.getStaticStride(context: context) case .Alignof: value = ty.getStaticAlignment(context: context) @@ -181,7 +188,33 @@ private extension BuiltinInst { uses.replaceAll(with: literal, context) context.erase(instruction: self) } - + + private func isUsedAsStrideOfIndexRawPointer(_ context: SimplifyContext) -> Bool { + var worklist = ValueWorklist(context) + defer { worklist.deinitialize() } + worklist.pushIfNotVisited(self) + while let v = worklist.pop() { + for use in v.uses { + switch use.instruction { + case let builtin as BuiltinInst: + switch builtin.id { + case .SMulOver, .TruncOrBitCast, .SExtOrBitCast, .ZExtOrBitCast: + worklist.pushIfNotVisited(builtin) + default: + break + } + case let tupleExtract as TupleExtractInst where tupleExtract.fieldIndex == 0: + worklist.pushIfNotVisited(tupleExtract) + case is IndexRawPointerInst: + return true + default: + break + } + } + } + return false + } + func optimizeArgumentToThinMetatype(argument: Int, _ context: SimplifyContext) { let type: Type @@ -207,7 +240,7 @@ private extension BuiltinInst { if constantFoldStringNullPointerCheck(isEqual: isEqual, context) { return } - if let literal = constantFold(context) { + if let literal = context.constantFold(builtin: self) { uses.replaceAll(with: literal, context) } } @@ -217,13 +250,60 @@ private extension BuiltinInst { operands[0].value.lookThroughScalarCasts is StringLiteralInst { let builder = Builder(before: self, context) - let result = builder.createIntegerLiteral(isEqual ? 0 : 1, type: type) + let result = builder.createBoolLiteral(!isEqual) uses.replaceAll(with: result, context) context.erase(instruction: self) return true } return false } + + /// Replaces a builtin "xor", which negates its operand comparison + /// ``` + /// %3 = builtin "cmp_slt_Int64"(%1, %2) : $Builtin.Int1 + /// %4 = integer_literal $Builtin.Int1, -1 + /// %5 = builtin "xor_Int1"(%3, %4) : $Builtin.Int1 + /// ``` + /// with the negated comparison + /// ``` + /// %5 = builtin "cmp_ge_Int64"(%1, %2) : $Builtin.Int1 + /// ``` + func simplifyNegation(_ context: SimplifyContext) { + assert(id == .Xor) + guard let one = arguments[1] as? IntegerLiteralInst, + let oneValue = one.value, + oneValue == -1, + let lhsBuiltin = arguments[0] as? BuiltinInst, + lhsBuiltin.type.isBuiltinInteger, + let negatedBuiltinName = lhsBuiltin.negatedComparisonBuiltinName + else { + return + } + + let builder = Builder(before: lhsBuiltin, context) + let negated = builder.createBuiltinBinaryFunction(name: negatedBuiltinName, + operandType: lhsBuiltin.arguments[0].type, + resultType: lhsBuiltin.type, + arguments: Array(lhsBuiltin.arguments)) + self.replace(with: negated, context) + } + + private var negatedComparisonBuiltinName: String? { + switch id { + case .ICMP_EQ: return "cmp_ne" + case .ICMP_NE: return "cmp_eq" + case .ICMP_SLE: return "cmp_sgt" + case .ICMP_SLT: return "cmp_sge" + case .ICMP_SGE: return "cmp_slt" + case .ICMP_SGT: return "cmp_sle" + case .ICMP_ULE: return "cmp_ugt" + case .ICMP_ULT: return "cmp_uge" + case .ICMP_UGE: return "cmp_ult" + case .ICMP_UGT: return "cmp_ule" + default: + return nil + } + } } private extension Value { diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCheckedCast.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCheckedCast.swift index fe922951bdac0..b7f45bf361351 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCheckedCast.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCheckedCast.swift @@ -81,7 +81,9 @@ private extension UnconditionalCheckedCastInst { return } let conformance = sourceFormalType.instanceTypeOfMetatype.checkConformance(to: proto) - guard conformance.isValid else { + guard conformance.isValid, + conformance.matchesActorIsolation(in: parentFunction) + else { return } diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyClassifyBridgeObject.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyClassifyBridgeObject.swift index ffe232bfb19bf..4232aba270ef8 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyClassifyBridgeObject.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyClassifyBridgeObject.swift @@ -23,7 +23,7 @@ extension ClassifyBridgeObjectInst : OnoneSimplifiable, SILCombineSimplifiable { } let builder = Builder(before: self, context) - let falseLiteral = builder.createIntegerLiteral(0, type: context.getBuiltinIntegerType(bitWidth: 1)) + let falseLiteral = builder.createBoolLiteral(false) let tp = builder.createTuple(type: self.type, elements: [falseLiteral, falseLiteral]) uses.replaceAll(with: tp, context) context.erase(instruction: self) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCondBranch.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCondBranch.swift index 222c07badadbb..08af5d25b6241 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCondBranch.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCondBranch.swift @@ -27,9 +27,9 @@ private extension CondBranchInst { } let builder = Builder(before: self, context) if conditionValue == 0 { - builder.createBranch(to: falseBlock, arguments: Array(falseOperands.map { $0.value })) + builder.createBranch(to: falseBlock, arguments: Array(falseOperands.values)) } else { - builder.createBranch(to: trueBlock, arguments: Array(trueOperands.map { $0.value })) + builder.createBranch(to: trueBlock, arguments: Array(trueOperands.values)) } context.erase(instruction: self) } diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCopyBlock.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCopyBlock.swift new file mode 100644 index 0000000000000..ecc0e635300be --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCopyBlock.swift @@ -0,0 +1,107 @@ +//===--- SimplifyCopyBlock.swift ------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL + +extension CopyBlockInst : Simplifiable, SILCombineSimplifiable { + + /// Removes a `copy_block` if its only uses, beside ownership instructions, are callees of function calls + /// ``` + /// %2 = copy_block %0 + /// %3 = begin_borrow [lexical] %2 + /// %4 = apply %3() : $@convention(block) @noescape () -> () + /// end_borrow %3 + /// destroy_value %2 + /// ``` + /// -> + /// ``` + /// %4 = apply %0() : $@convention(block) @noescape () -> () + /// ``` + /// + func simplify(_ context: SimplifyContext) { + if hasValidUses(block: self) { + replaceBlock(self, with: operand.value, context) + context.erase(instruction: self) + } + } +} + +private func hasValidUses(block: Value) -> Bool { + for use in block.uses { + switch use.instruction { + case let beginBorrow as BeginBorrowInst: + if !hasValidUses(block: beginBorrow) { + return false + } + case let apply as FullApplySite where apply.isCallee(operand: use): + break + case let partialApply as PartialApplyInst: + // If the block is passed to another function - either as closure argument or as closure capture - + // it's "converted" to a swift closure with the help of a thunk. The thunk just calls the block. + // If this is a non-escaping partial_apply and it's such a thunk, the block does not escape. + if partialApply.canClosureArgumentEscape(closure: use) { + return false + } + case is EndBorrowInst, is DestroyValueInst: + break + default: + return false + } + } + return true +} + +private func replaceBlock(_ block: Value, with original: Value, _ context: SimplifyContext) { + for use in block.uses { + switch use.instruction { + case let beginBorrow as BeginBorrowInst: + replaceBlock(beginBorrow, with: original, context) + context.erase(instruction: beginBorrow) + case is FullApplySite: + use.set(to: original, context) + case let partialApply as PartialApplyInst: + if original.ownership == .unowned { + let builder = Builder(before: partialApply, context) + let conv = builder.createUncheckedOwnershipConversion(operand: original, resultOwnership: .guaranteed) + use.set(to: conv, context) + } else { + use.set(to: original, context) + } + case is EndBorrowInst, is DestroyValueInst: + context.erase(instruction: use.instruction) + default: + fatalError("unhandled use") + } + } +} + +private extension PartialApplyInst { + func canClosureArgumentEscape(closure: Operand) -> Bool { + guard isOnStack, + let callee = referencedFunction, + callee.isDefinition, + let calleeArg = calleeArgument(of: closure, in: callee), + // If the callee only _calls_ the closure argument, it does not escape. + calleeArg.uses.allSatisfy(isCalleeOperandOfApply) + else { + return true + } + return false + } +} + +private func isCalleeOperandOfApply(_ operand: Operand) -> Bool { + if let apply = operand.instruction as? FullApplySite, apply.isCallee(operand: operand) { + return true + } + return false +} diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyEndCOWMutationAddr.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyEndCOWMutationAddr.swift new file mode 100644 index 0000000000000..b68aba70fc5bc --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyEndCOWMutationAddr.swift @@ -0,0 +1,32 @@ +//===--- SimplifyEndCOWMutationAddr.swift ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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 SIL + +// Simplify end_cow_mutation_addr to end_cow_mutation when it's operand is loadable +extension EndCOWMutationAddrInst: OnoneSimplifiable, SILCombineSimplifiable { + func simplify(_ context: SimplifyContext) { + let address = operand.value + if !address.type.isLoadable(in: parentFunction) { + return + } + if address.type.isTrivial(in: parentFunction) { + context.erase(instruction: self) + return + } + let builder = Builder(before: self, context) + let load = builder.createLoad(fromAddress: address, ownership: parentFunction.hasOwnership ? .take : .unqualified) + let endCOWMutation = builder.createEndCOWMutation(instance: load, keepUnique: false) + builder.createStore(source: endCOWMutation, destination: address, ownership: parentFunction.hasOwnership ? .initialize : .unqualified) + context.erase(instruction: self) + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyExplicitCopy.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyExplicitCopy.swift new file mode 100644 index 0000000000000..3afabf24f7754 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyExplicitCopy.swift @@ -0,0 +1,64 @@ +//===--- SimplifyExplicitCopy.swift ---------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL + +// The `explicit_copy_value` and `explicit_copy_addr` instructions are only used for non-copyable +// diagnostics in the mandatory pipeline. After that we can replace them by their non-explicit counterparts +// so that optimizations (which only know of `copy_value` and `copy_addr`) can do their work. + +/// Replaces +/// +/// ``` +/// %1 = explicit_copy_value %0 +/// ``` +/// -> +/// ``` +/// %1 = copy_value %0 +/// ``` +/// +extension ExplicitCopyValueInst : Simplifiable, SILCombineSimplifiable { + func simplify(_ context: SimplifyContext) { + if context.silStage == .raw { + // Make sure we don't remove `explicit_copy_value` in the diagnostic pipeline. + return + } + + let builder = Builder(before: self, context) + let copyValue = builder.createCopyValue(operand: fromValue) + replace(with: copyValue, context) + } +} + +/// Replaces +/// +/// ``` +/// explicit_copy_addr %0 to %1 +/// ``` +/// -> +/// ``` +/// copy_addr %0 to %1 +/// ``` +/// +extension ExplicitCopyAddrInst : Simplifiable, SILCombineSimplifiable { + func simplify(_ context: SimplifyContext) { + if context.silStage == .raw { + // Make sure we don't remove `explicit_copy_addr` in the diagnostic pipeline. + return + } + + let builder = Builder(before: self, context) + builder.createCopyAddr(from: source, to: destination, + takeSource: isTakeOfSource, initializeDest: isInitializationOfDestination) + context.erase(instruction: self) + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift index 40409265b6430..93c93ca6d94b2 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift @@ -75,7 +75,7 @@ extension LoadInst : OnoneSimplifiable, SILCombineSimplifiable { index < stringLiteral.value.count { let builder = Builder(before: self, context) - let charLiteral = builder.createIntegerLiteral(Int(stringLiteral.value[index]), type: type) + let charLiteral = builder.createIntegerLiteral(stringLiteral.value[index], type: type) uses.replaceAll(with: charLiteral, context) context.erase(instruction: self) return true @@ -104,10 +104,10 @@ extension LoadInst : OnoneSimplifiable, SILCombineSimplifiable { if !globalInitVal.canBeCopied(into: parentFunction, context) { return false } - var cloner = StaticInitCloner(cloneBefore: self, context) + var cloner = Cloner(cloneBefore: self, context) defer { cloner.deinitialize() } - let initVal = cloner.clone(globalInitVal) + let initVal = cloner.cloneRecursively(globalInitValue: globalInitVal) uses.replaceAll(with: initVal, context) // Also erases a builtin "once" on which the global_addr depends on. This is fine @@ -323,7 +323,7 @@ private extension Value { return false } if let fri = value as? FunctionRefInst { - if function.isAnySerialized, + if function.isAnySerialized, !fri.referencedFunction.hasValidLinkageForFragileRef(function.serializedKind) { return false @@ -376,3 +376,74 @@ private extension Instruction { return shiftValue > 0 } } + +/// Analyses the global initializer function and returns the `alloc_global` and `store` +/// instructions which initialize the global. +/// Returns nil if `function` has any side-effects beside initializing the global. +/// +/// The function's single basic block must contain following code pattern: +/// ``` +/// alloc_global @the_global +/// %a = global_addr @the_global +/// %i = some_const_initializer_insts +/// store %i to %a +/// ``` +/// +/// For all other instructions `handleUnknownInstruction` is called and such an instruction +/// is accepted if `handleUnknownInstruction` returns true. +private func getGlobalInitialization( + of function: Function, + _ context: some Context, + handleUnknownInstruction: (Instruction) -> Bool +) -> (allocInst: AllocGlobalInst, storeToGlobal: StoreInst)? { + guard let block = function.blocks.singleElement else { + return nil + } + + var allocInst: AllocGlobalInst? = nil + var globalAddr: GlobalAddrInst? = nil + var store: StoreInst? = nil + + for inst in block.instructions { + switch inst { + case is ReturnInst, + is DebugValueInst, + is DebugStepInst, + is BeginAccessInst, + is EndAccessInst: + continue + case let agi as AllocGlobalInst: + if allocInst == nil { + allocInst = agi + continue + } + case let ga as GlobalAddrInst: + if let agi = allocInst, agi.global == ga.global { + globalAddr = ga + } + continue + case let si as StoreInst: + if store == nil, + let ga = globalAddr, + si.destination == ga + { + store = si + continue + } + // Note that the initializer must not contain a `global_value` because `global_value` needs to + // initialize the class metadata at runtime. + default: + if inst.isValidInStaticInitializerOfGlobal(context) { + continue + } + } + if handleUnknownInstruction(inst) { + continue + } + return nil + } + if let store = store { + return (allocInst: allocInst!, storeToGlobal: store) + } + return nil +} diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMarkDependence.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMarkDependence.swift new file mode 100644 index 0000000000000..8d4ecc52daf43 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMarkDependence.swift @@ -0,0 +1,116 @@ +//===--- SimplifyMarkDependence.swift -------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL + +// Note: this simplification cannot run before dependency diagnostics. +// See `var isRedundant` below. + +extension MarkDependenceInst : OnoneSimplifiable, SILCombineSimplifiable { + func simplify(_ context: SimplifyContext) { + if isRedundant || + // A literal lives forever, so no mark_dependence is needed. + // This pattern can occur after StringOptimization when a utf8CString of a literal is replaced + // by the string_literal itself. + value.isLiteral + { + replace(with: value, context) + return + } + simplifyBaseOperand(context) + } +} + +extension MarkDependenceAddrInst : OnoneSimplifiable, SILCombineSimplifiable { + func simplify(_ context: SimplifyContext) { + if isRedundant { + context.erase(instruction: self) + return + } + simplifyBaseOperand(context) + } +} + +private extension MarkDependenceInstruction { + var isRedundant: Bool { + if base.type.isObject && base.type.isTrivial(in: base.parentFunction) + && !(base.definingInstruction is BeginApplyInst) { + // Sometimes due to specialization/builtins, we can get a mark_dependence whose base is a trivial + // typed object. Trivial values live forever. Therefore the mark_dependence does not have a meaning. + // begin_apply is a special case. A dependency on the token is limited to the coroutine scope (ideally, the token + // would have a non-trivial type like $Builtin.Token). + // + // Note: the mark_dependence is still needed for lifetime diagnostics. So it's important that this + // simplification does not run before the lifetime diagnostic pass. + return true + } + // If the value is an address projection from the base the mark_dependence is not needed because the + // base cannot be destroyed before the accessing the value, anyway. + if valueOrAddress.type.isAddress, base.type.isAddress, + // But we still need to keep the mark_dependence for non-escapable types because a non-escapable + // value can be copied and copies must not outlive the base. + valueOrAddress.type.isEscapable(in: parentFunction), + base.accessPath.isEqualOrContains(valueOrAddress.accessPath) + { + return true + } + return false + } + + func simplifyBaseOperand(_ context: SimplifyContext) { + /// In OSSA, the `base` is a borrow introducing operand. It is pretty complicated to change the base. + /// So, for simplicity, we only do this optimization when OSSA is already lowered. + if parentFunction.hasOwnership { + return + } + // Replace the base operand with the operand of the base value if it's a certain kind of forwarding + // instruction. + let rootBase = base.lookThroughEnumAndExistentialRef + if rootBase != base { + baseOperand.set(to: rootBase, context) + } + } +} + +private extension Value { + /// True, if this is a literal instruction or a struct of a literal instruction. + /// What we want to catch here is a `UnsafePointer` of a string literal. + var isLiteral: Bool { + switch self { + case let s as StructInst: + if let singleOperand = s.operands.singleElement { + return singleOperand.value.isLiteral + } + return false + case is IntegerLiteralInst, is FloatLiteralInst, is StringLiteralInst: + return true + default: + return false + } + } + + var lookThroughEnumAndExistentialRef: Value { + switch self { + case let e as EnumInst: + if let payload = e.payload { + return payload.lookThroughEnumAndExistentialRef + } + return self + case let ier as InitExistentialRefInst: + return ier.instance.lookThroughEnumAndExistentialRef + case let oer as OpenExistentialRefInst: + return oer.existential.lookThroughEnumAndExistentialRef + default: + return self + } + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMisc.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMisc.swift index 2aaaaeb80435d..2f571a61768c6 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMisc.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMisc.swift @@ -15,7 +15,7 @@ import SIL extension TypeValueInst: OnoneSimplifiable, SILCombineSimplifiable { func simplify(_ context: SimplifyContext) { // If our parameter is not known statically, then bail. - guard paramType.isInteger else { + guard let value = value else { return } @@ -30,3 +30,4 @@ extension TypeValueInst: OnoneSimplifiable, SILCombineSimplifiable { context.erase(instruction: self) } } + diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyPointerToAddress.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyPointerToAddress.swift index 0b2e8fde0fc10..e937659faa986 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyPointerToAddress.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyPointerToAddress.swift @@ -69,7 +69,7 @@ private func removeAddressToPointerToAddressPair( /// private func simplifyIndexRawPointer(of ptr2Addr: PointerToAddressInst, _ context: SimplifyContext) -> Bool { guard let indexRawPtr = ptr2Addr.pointer as? IndexRawPointerInst, - let tupleExtract = indexRawPtr.index.lookThroughTruncOrBitCast as? TupleExtractInst, + let tupleExtract = indexRawPtr.index.lookThroughIndexScalarCast as? TupleExtractInst, let strideMul = tupleExtract.tuple as? BuiltinInst, strideMul.id == .SMulOver, let (index, strideType) = strideMul.indexAndStrideOfMultiplication, strideType == ptr2Addr.type.objectType @@ -293,13 +293,6 @@ private extension Value { return self } } - - var lookThroughTruncOrBitCast: Value { - if let truncOrBitCast = self as? BuiltinInst, truncOrBitCast.id == .TruncOrBitCast { - return truncOrBitCast.arguments[0] - } - return self - } } private extension BuiltinInst { @@ -321,9 +314,9 @@ private extension BuiltinInst { private extension Builder { func createCastIfNeeded(of index: Value, toIndexTypeOf indexRawPtr: IndexRawPointerInst) -> Value { - if let truncOrBitCast = indexRawPtr.index as? BuiltinInst { - assert(truncOrBitCast.id == .TruncOrBitCast) - return createBuiltin(name: truncOrBitCast.name, type: truncOrBitCast.type, arguments: [index]) + if let cast = indexRawPtr.index as? BuiltinInst { + assert(cast.id == .TruncOrBitCast || cast.id == .SExtOrBitCast) + return createBuiltin(name: cast.name, type: cast.type, arguments: [index]) } return index } diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStruct.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStruct.swift new file mode 100644 index 0000000000000..90071e604af8f --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStruct.swift @@ -0,0 +1,112 @@ +//===--- SimplifyStruct.swift ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL + +extension StructInst : Simplifiable, SILCombineSimplifiable { + + /// Eliminates `struct_extract`s of an owned `struct` where the `struct_extract`s are inside a + /// a borrow scope. + /// This is done by splitting the `begin_borrow` of the whole struct into individual borrows of the fields + /// (for trivial fields no borrow is needed). And then sinking the `struct` to it's consuming use(s). + /// + /// ``` + /// %3 = struct $S(%nonTrivialField, %trivialField) // owned + /// ... + /// %4 = begin_borrow %3 + /// %5 = struct_extract %4, #S.nonTrivialField + /// %6 = struct_extract %4, #S.trivialField + /// use %5, %6 + /// end_borrow %4 + /// ... + /// end_of_lifetime %3 + /// ``` + /// -> + /// ``` + /// ... + /// %5 = begin_borrow %nonTrivialField + /// use %5, %trivialField + /// end_borrow %5 + /// ... + /// %3 = struct $S(%nonTrivialField, %trivialField) + /// end_of_lifetime %3 + /// ``` + func simplify(_ context: SimplifyContext) { + guard ownership == .owned, + hasOnlyStructExtractUsesInBorrowScopes() + else { + return + } + + for beginBorrow in uses.users(ofType: BeginBorrowInst.self) { + splitAndRemoveStructExtracts(beginBorrow: beginBorrow, context) + } + + self.sinkToEndOfLifetime(context) + + context.erase(instructionIncludingAllUsers: self) + } + + private func hasOnlyStructExtractUsesInBorrowScopes() -> Bool { + var hasStructExtract = false + + for use in uses.ignoreDebugUses { + switch use.instruction { + case let beginBorrow as BeginBorrowInst: + for borrowUse in beginBorrow.uses.ignoreDebugUses { + switch borrowUse.instruction { + case is EndBorrowInst: + break + case is StructExtractInst: + hasStructExtract = true + default: + return false + } + } + default: + guard use.endsLifetime else { + return false + } + } + } + return hasStructExtract + } + + private func splitAndRemoveStructExtracts(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) { + for structExtract in beginBorrow.uses.users(ofType: StructExtractInst.self) { + let field = self.operands[structExtract.fieldIndex].value + switch structExtract.ownership { + case .none: + structExtract.replace(with: field, context) + case .guaranteed: + let beginBuilder = Builder(before: beginBorrow, context) + let borrowedField = beginBuilder.createBeginBorrow(of: field, + isLexical: beginBorrow.isLexical, + hasPointerEscape: beginBorrow.hasPointerEscape) + structExtract.replace(with: borrowedField, context) + for endBorrow in beginBorrow.endInstructions { + let endBuilder = Builder(before: endBorrow, context) + endBuilder.createEndBorrow(of: borrowedField) + } + case .owned, .unowned: + fatalError("wrong ownership of struct_extract") + } + } + } + private func sinkToEndOfLifetime(_ context: SimplifyContext) { + for use in uses where use.endsLifetime { + let builder = Builder(before: use.instruction, context) + let delayedStruct = builder.createStruct(type: type, elements: Array(operands.values)) + use.set(to: delayedStruct, context) + } + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyUncheckedAddrCast.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyUncheckedAddrCast.swift new file mode 100644 index 0000000000000..d8455d555aef8 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyUncheckedAddrCast.swift @@ -0,0 +1,81 @@ +//===--- SimplifyUncheckedAddrCast.swift ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL + +extension UncheckedAddrCastInst : OnoneSimplifiable, SILCombineSimplifiable { + + func simplify(_ context: SimplifyContext) { + // ``` + // %1 = unchecked_addr_cast %0 : $*T to $*T + // ``` + // -> + // replace %1 with %0 + // + if optimizeSameTypeCast(context) { + return + } + + // ``` + // %1 = unchecked_addr_cast %0 : $*U to $*V + // %2 = unchecked_addr_cast %1 : $*V to $*T + // ``` + // -> + // ``` + // %2 = unchecked_addr_cast %0: $*U to $*T + // ``` + if optimizeDoubleCast(context) { + return + } + + // ``` + // %1 = unchecked_addr_cast %0 : $*Builtin.FixedArray to $*Element + // ``` + // -> + // ``` + // %1 = vector_base_addr %0 : $*Builtin.FixedArray + // ``` + _ = optimizeVectorBaseCast(context) + } +} + +private extension UncheckedAddrCastInst { + func optimizeSameTypeCast(_ context: SimplifyContext) -> Bool { + if fromAddress.type == type { + self.replace(with: fromAddress, context) + return true + } + return false + } + + func optimizeDoubleCast(_ context: SimplifyContext) -> Bool { + if let firstCast = fromAddress as? UncheckedAddrCastInst { + let builder = Builder(before: self, context) + let newCast = builder.createUncheckedAddrCast(from: firstCast.fromAddress, to: type) + self.replace(with: newCast, context) + return true + } + return false + } + + func optimizeVectorBaseCast(_ context: SimplifyContext) -> Bool { + if fromAddress.type.isBuiltinFixedArray, + fromAddress.type.builtinFixedArrayElementType(in: parentFunction, maximallyAbstracted: true).addressType == type + { + let builder = Builder(before: self, context) + let vectorBase = builder.createVectorBaseAddr(vector: fromAddress) + self.replace(with: vectorBase, context) + return true + } + return false + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/CMakeLists.txt index df7a020d3024e..5ad26d798bc0a 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/CMakeLists.txt @@ -8,6 +8,7 @@ swift_compiler_sources(Optimizer DiagnoseUnknownConstValues.swift + EmbeddedSwiftDiagnostics.swift MandatoryPerformanceOptimizations.swift ReadOnlyGlobalVariables.swift StackProtection.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/DiagnoseUnknownConstValues.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/DiagnoseUnknownConstValues.swift index b33dccdb1c865..caf6f145555c0 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/DiagnoseUnknownConstValues.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/DiagnoseUnknownConstValues.swift @@ -39,8 +39,8 @@ let diagnoseUnknownConstValues = ModulePass(name: "diagnose-unknown-const-values private func verifyGlobals(_ context: ModulePassContext) { for gv in context.globalVariables where gv.isConst { if gv.staticInitValue == nil { - context.diagnosticEngine.diagnose(gv.varDecl?.location.sourceLoc, - .require_const_initializer_for_const) + context.diagnosticEngine.diagnose(.require_const_initializer_for_const, + at: gv.varDecl?.location.sourceLoc) } } } @@ -66,8 +66,8 @@ private func verifyLocal(debugValueInst: DebugValueInst, } if !constExprState.isConstantValue(debugValueInst.operand.value) { - context.diagnosticEngine.diagnose(debugValueInst.location.sourceLoc, - .require_const_initializer_for_const) + context.diagnosticEngine.diagnose(.require_const_initializer_for_const, + at: debugValueInst.location) } } @@ -92,8 +92,8 @@ private func verifyCallArguments(apply: FullApplySite, for (paramIdx, param) in calleeFn.convention.parameters.enumerated() where param.hasOption(.const) { let matchingOperand = apply.parameterOperands[paramIdx] if !constExprState.isConstantValue(matchingOperand.value) { - context.diagnosticEngine.diagnose(apply.location.sourceLoc, - .require_const_arg_for_parameter) + context.diagnosticEngine.diagnose(.require_const_arg_for_parameter, + at: apply.location) } } } diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift new file mode 100644 index 0000000000000..4d99ca22ac7ba --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift @@ -0,0 +1,408 @@ +//===--- EmbeddedSwiftDiagnostics.swift -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 AST +import SIL + +/// Diagnoses violations of Embedded Swift language restrictions. +/// +let embeddedSwiftDiagnostics = ModulePass(name: "embedded-swift-diagnostics") { + (moduleContext: ModulePassContext) in + + guard moduleContext.options.enableEmbeddedSwift, + // Skip all embedded diagnostics if asked. This is used from SourceKit to avoid reporting + // false positives when WMO is turned off for indexing purposes. + moduleContext.enableWMORequiredDiagnostics + else { + return + } + + // Try to start with public and exported functions to get better caller information in the diagnostics. + let allFunctions = Array(moduleContext.functions.lazy.filter { !$0.isGeneric }) + .sorted(by: { $0.priority < $1.priority }) + + var checker = FunctionChecker(moduleContext) + defer { checker.deinitialize() } + + for function in allFunctions { + do { + assert(checker.callStack.isEmpty) + try checker.checkFunction(function) + } catch let error as Diagnostic { + checker.diagnose(error) + } catch { + fatalError("unknown error thrown") + } + } + + checkVTables(moduleContext) +} + +private struct FunctionChecker { + let context: ModulePassContext + var visitedFunctions = Set() + var visitedConformances = Set() + var callStack: Stack + + init(_ context: ModulePassContext) { + self.context = context + self.callStack = Stack(context) + } + + mutating func deinitialize() { + callStack.deinitialize() + } + + mutating func checkFunction(_ function: Function) throws { + guard function.isDefinition, + // Avoid infinite recursion + visitedFunctions.insert(function).inserted + else { + return + } + + for inst in function.instructions { + try checkInstruction(inst) + } + } + + mutating func checkInstruction(_ instruction: Instruction) throws { + switch instruction { + case is OpenExistentialMetatypeInst, + is InitExistentialMetatypeInst: + throw Diagnostic(.embedded_swift_metatype_type, instruction.operands[0].value.type, at: instruction.location) + + case is OpenExistentialBoxInst, + is OpenExistentialBoxValueInst, + is OpenExistentialValueInst, + is OpenExistentialAddrInst, + is InitExistentialAddrInst, + is InitExistentialValueInst, + is ExistentialMetatypeInst: + throw Diagnostic(.embedded_swift_existential_type, instruction.operands[0].value.type, at: instruction.location) + + case let aeb as AllocExistentialBoxInst: + throw Diagnostic(.embedded_swift_existential_type, aeb.type, at: instruction.location) + + case let ier as InitExistentialRefInst: + for conf in ier.conformances { + try checkConformance(conf, location: ier.location) + } + + case is ValueMetatypeInst, + is MetatypeInst: + let metaType = (instruction as! SingleValueInstruction).type + if metaType.representationOfMetatype != .thin { + let rawType = metaType.canonicalType.rawType.instanceTypeOfMetatype + let type = rawType.isDynamicSelf ? rawType.staticTypeOfDynamicSelf : rawType + if !type.isClass { + throw Diagnostic(.embedded_swift_metatype_type, type, at: instruction.location) + } + } + + case is KeyPathInst: + throw Diagnostic(.embedded_swift_keypath, at: instruction.location) + + case is CheckedCastAddrBranchInst, + is UnconditionalCheckedCastAddrInst: + throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location) + + case let abi as AllocBoxInst: + // It needs a bit of work to support alloc_box of generic non-copyable structs/enums with deinit, + // because we need to specialize the deinit functions, though they are not explicitly referenced in SIL. + // Until this is supported, give an error in such cases. Otherwise IRGen would crash. + if abi.allocsGenericValueTypeWithDeinit { + throw Diagnostic(.embedded_capture_of_generic_value_with_deinit, at: abi.location) + } + fallthrough + case is AllocRefInst, + is AllocRefDynamicInst: + if context.options.noAllocations { + throw Diagnostic(.embedded_swift_allocating_type, (instruction as! SingleValueInstruction).type, + at: instruction.location) + } + + case is ThunkInst: + if context.options.noAllocations { + throw Diagnostic(.embedded_swift_allocating, at: instruction.location) + } + + case let ba as BeginApplyInst: + if context.options.noAllocations { + throw Diagnostic(.embedded_swift_allocating_coroutine, at: instruction.location) + } + try checkApply(apply: ba) + + case let pai as PartialApplyInst: + if context.options.noAllocations && !pai.isOnStack { + throw Diagnostic(.embedded_swift_allocating_closure, at: instruction.location) + } + try checkApply(apply: pai) + + // Remaining apply instructions + case let apply as ApplySite: + try checkApply(apply: apply) + + case let bi as BuiltinInst: + switch bi.id { + case .AllocRaw: + if context.options.noAllocations { + throw Diagnostic(.embedded_swift_allocating, at: instruction.location) + } + case .BuildOrdinaryTaskExecutorRef, + .BuildOrdinarySerialExecutorRef, + .BuildComplexEqualitySerialExecutorRef: + // Those builtins implicitly create an existential. + try checkConformance(bi.substitutionMap.conformances[0], location: bi.location) + + default: + break + } + + default: + break + } + } + + mutating func checkApply(apply: ApplySite) throws { + if context.options.noAllocations && apply.isAsync { + throw Diagnostic(.embedded_swift_allocating_type, at: apply.location) + } + + if !apply.callee.type.hasValidSignatureForEmbedded, + // Some runtime functions have generic parameters in SIL, which are not used in IRGen. + // Therefore exclude runtime functions at all. + !apply.callsEmbeddedRuntimeFunction + { + switch apply.callee { + case let cmi as ClassMethodInst: + throw Diagnostic(.embedded_cannot_specialize_class_method, cmi.member, at: apply.location) + case let wmi as WitnessMethodInst: + throw Diagnostic(.embedded_cannot_specialize_witness_method, wmi.member, at: apply.location) + default: + if apply.substitutionMap.replacementTypes.contains(where: { $0.hasDynamicSelf }), + apply.calleeHasGenericSelfMetatypeParameter + { + throw Diagnostic(.embedded_call_generic_function_with_dynamic_self, at: apply.location) + } + throw Diagnostic(.embedded_call_generic_function, at: apply.location) + } + } + + // Although all (non-generic) functions are initially put into the worklist there are two reasons + // to call `checkFunction` recursively: + // * To get a better caller info in the diagnostics. + // * When passing an opened existential to a generic function, it's valid in Embedded swift even if the + // generic is not specialized. We need to check such generic functions, too. + if let callee = apply.referencedFunction { + callStack.push(CallSite(apply: apply, callee: callee)) + try checkFunction(callee) + _ = callStack.pop() + } + } + + // Check for any violations in witness tables for existentials. + mutating func checkConformance(_ conformance: Conformance, location: Location) throws { + guard conformance.isConcrete, + // Avoid infinite recursion + visitedConformances.insert(conformance).inserted, + let witnessTable = context.lookupWitnessTable(for: conformance) + else { + return + } + if !conformance.protocol.requiresClass { + throw Diagnostic(.embedded_swift_existential_protocol, conformance.protocol.name, at: location) + } + + for entry in witnessTable.entries { + switch entry { + case .invalid, .associatedType: + break + case .method(let requirement, let witness): + if let witness = witness { + callStack.push(CallSite(location: location, kind: .conformance)) + if witness.isGeneric { + throw Diagnostic(.embedded_cannot_specialize_witness_method, requirement, at: witness.location) + } + try checkFunction(witness) + _ = callStack.pop() + } + case .baseProtocol(_, let witness): + try checkConformance(witness, location: location) + case .associatedConformance(_, let assocConf): + // If it's not a class protocol, the associated type can never be used to create + // an existential. Therefore this witness entry is never used at runtime in embedded swift. + if assocConf.protocol.requiresClass { + try checkConformance(assocConf, location: location) + } + } + } + } + + mutating func diagnose(_ error: Diagnostic) { + var diagPrinted = false + if error.location.hasValidLineNumber { + context.diagnosticEngine.diagnose(error) + diagPrinted = true + } + + // If the original instruction doesn't have a location (e.g. because it's in a stdlib function), + // search the callstack and use the location from a call site. + while let callSite = callStack.pop() { + if !diagPrinted { + if callSite.location.hasValidLineNumber { + context.diagnosticEngine.diagnose(error.id, error.arguments, at: callSite.location) + diagPrinted = true + } + } else { + // Print useful callsite information as a note (see `CallSite`) + switch callSite.kind { + case .constructorCall: + context.diagnosticEngine.diagnose(.embedded_constructor_called, at: callSite.location) + case .specializedCall: + context.diagnosticEngine.diagnose(.embedded_specialization_called_from, at: callSite.location) + case .conformance: + context.diagnosticEngine.diagnose(.embedded_existential_created, at: callSite.location) + case .call: + break + } + } + } + if !diagPrinted { + context.diagnosticEngine.diagnose(error) + } + } +} + +// Print errors for generic functions in vtables, which is not allowed in embedded Swift. +private func checkVTables(_ context: ModulePassContext) { + for vTable in context.vTables { + if !vTable.class.isGenericAtAnyLevel || vTable.isSpecialized { + for entry in vTable.entries where entry.implementation.isGeneric { + context.diagnosticEngine.diagnose(.embedded_cannot_specialize_class_method, entry.methodDecl, + at: entry.methodDecl.location) + } + } + } +} + +/// Relevant call site information for diagnostics. +/// This information is printed as additional note(s) after the original diagnostic. +private struct CallSite { + enum Kind { + // A regular function call. Not every function call in the call stack is printed in diagnostics. + // This is only used if the original instruction doesn't have a location. + case call + + // If the error is in a constructor, this is the place where the object/value is created. + case constructorCall + + // If the error is in a specialized function, this is the place where the generic function is originally + // specialized with concrete types. This is useful if a specialized type is relevant for the error. + case specializedCall + + // If the error is in a protocol witness method, this is the place where the existential is created. + case conformance + } + + let location: Location + let kind: Kind + + init(apply: ApplySite, callee: Function) { + self.location = apply.location + if let d = callee.location.decl, d is ConstructorDecl { + self.kind = .constructorCall + } else if callee.isSpecialization && !apply.parentFunction.isSpecialization { + self.kind = .specializedCall + } else { + self.kind = .call + } + } + + init(location: Location, kind: Kind) { + self.location = location + self.kind = kind + } +} + +private extension Function { + // The priority (1 = highest) which defines the order in which functions are checked. + // This is important to get good caller information in diagnostics. + var priority: Int { + // There might be functions without a location, e.g. `swift_readAtKeyPath` generated by SILGen for keypaths. + // It's okay to skip the ctor/dtor/method detection logic for those. + if location.hasValidLineNumber { + if let decl = location.decl { + if decl is DestructorDecl || decl is ConstructorDecl { + return 4 + } + if let parent = decl.parent, parent is ClassDecl { + return 2 + } + } + } + if isPossiblyUsedExternally { + return 1 + } + return 3 + } +} + +private extension AllocBoxInst { + var allocsGenericValueTypeWithDeinit: Bool { + type.getBoxFields(in: parentFunction).contains { $0.hasGenericValueDeinit(in: parentFunction) } + } +} + +private extension ApplySite { + var callsEmbeddedRuntimeFunction: Bool { + if let callee = referencedFunction, + !callee.isDefinition, + !callee.name.startsWith("$e") + { + return true + } + return false + } + + var calleeHasGenericSelfMetatypeParameter: Bool { + let convention = FunctionConvention(for: callee.type.canonicalType, in: parentFunction) + guard convention.hasSelfParameter, let selfParam = convention.parameters.last else { + return false + } + let selfParamType = selfParam.type + return selfParamType.isMetatype && selfParamType.instanceTypeOfMetatype.isGenericTypeParameter + } +} + +private extension Type { + func hasGenericValueDeinit(in function: Function) -> Bool { + guard isMoveOnly, let nominal = nominal else { + return false + } + + if nominal.isGenericAtAnyLevel && nominal.valueTypeDestructor != nil { + return true + } + + if isStruct { + if let fields = getNominalFields(in: function) { + return fields.contains { $0.hasGenericValueDeinit(in: function) } + } + } else if isEnum { + if let enumCases = getEnumCases(in: function) { + return enumCases.contains { $0.payload?.hasGenericValueDeinit(in: function) ?? false } + } + } + return false + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift index ca4d1012b97fe..af12a0572d8e5 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import AST import SIL /// Performs mandatory optimizations for performance-annotated functions, and global @@ -40,9 +41,10 @@ let mandatoryPerformanceOptimizations = ModulePass(name: "mandatory-performance- optimizeFunctionsTopDown(using: &worklist, moduleContext) - if moduleContext.options.enableEmbeddedSwift { - // Print errors for generic functions in vtables, which is not allowed in embedded Swift. - checkVTablesForGenericFunctions(moduleContext) + // It's not required to set the perf_constraint flag on all functions in embedded mode. + // Embedded mode already implies that flag. + if !moduleContext.options.enableEmbeddedSwift { + setPerformanceConstraintFlags(moduleContext) } } @@ -50,17 +52,9 @@ private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist, _ moduleContext: ModulePassContext) { while let f = worklist.pop() { moduleContext.transform(function: f) { context in - if !context.loadFunction(function: f, loadCalleesRecursively: true) { - return - } - - // It's not required to set the perf_constraint flag on all functions in embedded mode. - // Embedded mode already implies that flag. - if !moduleContext.options.enableEmbeddedSwift { - f.set(isPerformanceConstraint: true, context) + if context.loadFunction(function: f, loadCalleesRecursively: true) { + optimize(function: f, context, moduleContext, &worklist) } - - optimize(function: f, context, moduleContext, &worklist) } // Generic specialization takes care of removing metatype arguments of generic functions. @@ -68,7 +62,18 @@ private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist, // We need handle this case with a function signature optimization. removeMetatypeArgumentsInCallees(of: f, moduleContext) - worklist.addCallees(of: f) + worklist.addCallees(of: f, moduleContext) + } +} + +private func setPerformanceConstraintFlags(_ moduleContext: ModulePassContext) { + var worklist = FunctionWorklist() + for f in moduleContext.functions where f.performanceConstraints != .none && f.isDefinition { + worklist.pushIfNotVisited(f) + } + while let f = worklist.pop() { + moduleContext.transform(function: f) { f.set(isPerformanceConstraint: true, $0) } + worklist.addCallees(of: f, moduleContext) } } @@ -112,7 +117,8 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu } } case let metatype as MetatypeInst: - if context.options.enableEmbeddedSwift { + if context.options.enableEmbeddedSwift, + metatype.type.representationOfMetatype == .thick { let instanceType = metatype.type.loweredInstanceTypeOfMetatype(in: function) if instanceType.isClass { specializeVTable(forClassType: instanceType, errorLocation: metatype.location, moduleContext) { @@ -131,17 +137,44 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu case let initExRef as InitExistentialRefInst: if context.options.enableEmbeddedSwift { - specializeWitnessTables(for: initExRef, moduleContext, &worklist) + for c in initExRef.conformances where c.isConcrete { + specializeWitnessTable(for: c, moduleContext) + worklist.addWitnessMethods(of: c, moduleContext) + } + } + + case let bi as BuiltinInst: + switch bi.id { + case .BuildOrdinaryTaskExecutorRef, + .BuildOrdinarySerialExecutorRef, + .BuildComplexEqualitySerialExecutorRef: + let conformance = bi.substitutionMap.conformances[0] + specializeWitnessTable(for: conformance, moduleContext) + worklist.addWitnessMethods(of: conformance, moduleContext) + + default: + if !devirtualizeDeinits(of: bi, simplifyCtxt) { + // If invoked from SourceKit avoid reporting false positives when WMO is turned off for indexing purposes. + if moduleContext.enableWMORequiredDiagnostics { + context.diagnosticEngine.diagnose(.deinit_not_visible, at: bi.location) + } + } } // We need to de-virtualize deinits of non-copyable types to be able to specialize the deinitializers. case let destroyValue as DestroyValueInst: if !devirtualizeDeinits(of: destroyValue, simplifyCtxt) { - context.diagnosticEngine.diagnose(destroyValue.location.sourceLoc, .deinit_not_visible) + // If invoked from SourceKit avoid reporting false positives when WMO is turned off for indexing purposes. + if moduleContext.enableWMORequiredDiagnostics { + context.diagnosticEngine.diagnose(.deinit_not_visible, at: destroyValue.location) + } } case let destroyAddr as DestroyAddrInst: if !devirtualizeDeinits(of: destroyAddr, simplifyCtxt) { - context.diagnosticEngine.diagnose(destroyAddr.location.sourceLoc, .deinit_not_visible) + // If invoked from SourceKit avoid reporting false positives when WMO is turned off for indexing purposes. + if moduleContext.enableWMORequiredDiagnostics { + context.diagnosticEngine.diagnose(.deinit_not_visible, at: destroyAddr.location) + } } case let iem as InitExistentialMetatypeInst: @@ -238,11 +271,13 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined return false } - if !callee.canBeInlinedIntoCaller(withSerializedKind: apply.parentFunction.serializedKind) && - // Even if the serialization kind doesn't match, we need to make sure to inline witness method thunks - // in embedded swift. - callee.thunkKind != .thunk - { + guard callee.canBeInlinedIntoCaller(withSerializedKind: apply.parentFunction.serializedKind) || + // Even if the serialization kind doesn't match, we need to make sure to inline witness method thunks + // in embedded swift. + callee.thunkKind == .thunk || + // Force inlining transparent co-routines. This might be necessary if `-enable-testing` is turned on. + (apply is BeginApplyInst && callee.isTransparent) + else { return false } @@ -267,7 +302,9 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined return false } - if apply.parentFunction.isGlobalInitOnceFunction && callee.inlineStrategy == .always { + if apply.parentFunction.isGlobalInitOnceFunction && ( + callee.inlineStrategy == .heuristicAlways || + callee.inlineStrategy == .always) { // Some arithmetic operations, like integer conversions, are not transparent but `inline(__always)`. // Force inlining them in global initializers so that it's possible to statically initialize the global. return true @@ -282,51 +319,9 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined return false } -private func specializeWitnessTables(for initExRef: InitExistentialRefInst, _ context: ModulePassContext, - _ worklist: inout FunctionWorklist) -{ - for c in initExRef.conformances where c.isConcrete { - let conformance = c.isInherited ? c.inheritedConformance : c - let origWitnessTable = context.lookupWitnessTable(for: conformance) - if conformance.isSpecialized { - if origWitnessTable == nil { - specializeWitnessTable(forConformance: conformance, errorLocation: initExRef.location, context) { - worklist.addWitnessMethods(of: $0) - } - } - } else if let origWitnessTable { - checkForGenericMethods(in: origWitnessTable, errorLocation: initExRef.location, context) - } - } -} - -private func checkForGenericMethods(in witnessTable: WitnessTable, - errorLocation: Location, - _ context: ModulePassContext) -{ - for entry in witnessTable.entries { - if case .method(let requirement, let witness) = entry, - let witness, - witness.isGeneric - { - context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_witness_method, requirement) - return - } - } -} - -private func checkVTablesForGenericFunctions(_ context: ModulePassContext) { - for vTable in context.vTables where !vTable.class.isGenericAtAnyLevel { - for entry in vTable.entries where entry.implementation.isGeneric { - context.diagnosticEngine.diagnose(entry.methodDecl.location.sourceLoc, .non_final_generic_class_function) - } - } -} - private extension FullApplySite { func resultIsUsedInGlobalInitialization() -> SmallProjectionPath? { - guard parentFunction.isGlobalInitOnceFunction, - let global = parentFunction.getInitializedGlobal() else { + guard let global = parentFunction.initializedGlobal else { return nil } @@ -411,7 +406,7 @@ private extension Value { // var p = Point(x: 10, y: 20) // let o = UnsafePointer(&p) // Therefore ignore the `end_access` use of a `begin_access`. - let relevantUses = singleUseValue.uses.ignoreDebugUses.ignoreUsers(ofType: EndAccessInst.self) + let relevantUses = singleUseValue.uses.ignoreDebugUses.ignoreUses(ofType: EndAccessInst.self) guard let use = relevantUses.singleUse else { return nil @@ -450,39 +445,7 @@ private extension Value { } } -private extension Function { - /// Analyzes the global initializer function and returns global it initializes (from `alloc_global` instruction). - func getInitializedGlobal() -> GlobalVariable? { - if !isDefinition { - return nil - } - for inst in self.entryBlock.instructions { - switch inst { - case let agi as AllocGlobalInst: - return agi.global - default: - break - } - } - - return nil - } -} - -fileprivate struct FunctionWorklist { - private(set) var functions = Array() - private var pushedFunctions = Set() - private var currentIndex = 0 - - mutating func pop() -> Function? { - if currentIndex < functions.count { - let f = functions[currentIndex] - currentIndex += 1 - return f - } - return nil - } - +extension FunctionWorklist { mutating func addAllMandatoryRequiredFunctions(of moduleContext: ModulePassContext) { for f in moduleContext.functions { // Performance annotated functions @@ -490,20 +453,10 @@ fileprivate struct FunctionWorklist { pushIfNotVisited(f) } - // Annotated global init-once functions - if f.isGlobalInitOnceFunction { - if let global = f.getInitializedGlobal(), - global.mustBeInitializedStatically { - pushIfNotVisited(f) - } - } - - // @const global init-once functions - if f.isGlobalInitOnceFunction { - if let global = f.getInitializedGlobal(), - global.mustBeInitializedStatically { - pushIfNotVisited(f) - } + // Initializers of globals which must be initialized statically. + if let global = f.initializedGlobal, + global.mustBeInitializedStatically { + pushIfNotVisited(f) } } } @@ -515,9 +468,15 @@ fileprivate struct FunctionWorklist { return } - mutating func addCallees(of function: Function) { + mutating func addCallees(of function: Function, _ context: ModulePassContext) { for inst in function.instructions { switch inst { + case let fri as FunctionRefInst: + // In embedded swift all reachable functions must be handled - even if they are not called, + // e.g. referenced by a global. + if context.options.enableEmbeddedSwift { + pushIfNotVisited(fri.referencedFunction) + } case let apply as ApplySite: if let callee = apply.referencedFunction { pushIfNotVisited(callee) @@ -528,32 +487,78 @@ fileprivate struct FunctionWorklist { if let fri = bi.operands[1].value as? FunctionRefInst { pushIfNotVisited(fri.referencedFunction) } - break; default: break } + case let alloc as AllocRefInst: + if context.options.enableEmbeddedSwift { + addVTableMethods(forClassType: alloc.type, context) + } + case let metatype as MetatypeInst: + if context.options.enableEmbeddedSwift { + let instanceType = metatype.type.loweredInstanceTypeOfMetatype(in: function) + if instanceType.isClass { + addVTableMethods(forClassType: instanceType, context) + } + } + default: break } } } - mutating func addWitnessMethods(of witnessTable: WitnessTable) { - for entry in witnessTable.entries { - if case .method(_, let witness) = entry, - let method = witness, - // A new witness table can still contain a generic function if the method couldn't be specialized for - // some reason and an error has been printed. Exclude generic functions to not run into an assert later. - !method.isGeneric - { - pushIfNotVisited(method) - } + mutating func addVTableMethods(forClassType classType: Type, _ context: ModulePassContext) { + guard let vtable = classType.isGenericAtAnyLevel ? + context.lookupSpecializedVTable(for: classType) : + context.lookupVTable(for: classType.nominal!) + else { + return } + for entry in vtable.entries where !entry.implementation.isGeneric { + pushIfNotVisited(entry.implementation) + } + } + + mutating func addWitnessMethods(of conformance: Conformance, _ context: ModulePassContext) { + var visited = Set() + addWitnessMethodsRecursively(of: conformance, visited: &visited, context) } - mutating func pushIfNotVisited(_ element: Function) { - if pushedFunctions.insert(element).inserted { - functions.append(element) + private mutating func addWitnessMethodsRecursively(of conformance: Conformance, + visited: inout Set, + _ context: ModulePassContext) + { + guard conformance.isConcrete, + visited.insert(conformance).inserted + else { + return + } + let witnessTable: WitnessTable + if let wt = context.lookupWitnessTable(for: conformance) { + witnessTable = wt + } else if let wt = context.lookupWitnessTable(for: conformance.rootConformance) { + witnessTable = wt + } else { + return + } + for entry in witnessTable.entries { + switch entry { + case .invalid, .associatedType: + break + case .method(_, let witness): + if let method = witness, + // A new witness table can still contain a generic function if the method couldn't be specialized for + // some reason and an error has been printed. Exclude generic functions to not run into an assert later. + !method.isGeneric + { + pushIfNotVisited(method) + } + case .baseProtocol(_, let baseConf): + addWitnessMethodsRecursively(of: baseConf, visited: &visited, context) + case .associatedConformance(_, let assocConf): + addWitnessMethodsRecursively(of: assocConf, visited: &visited, context) + } } } } diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/StackProtection.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/StackProtection.swift index 96b31bc5dc48a..586ebe6751486 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/StackProtection.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/StackProtection.swift @@ -113,7 +113,7 @@ private struct StackProtectionOptimization { process(instruction: inst, in: function, mustFixStackNesting: &mustFixStackNesting, context) } if mustFixStackNesting { - function.fixStackNesting(context) + context.fixStackNesting(in: function) } } @@ -332,20 +332,16 @@ private struct StackProtectionOptimization { let function = argument.parentFunction let entryBlock = function.entryBlock - let loc = entryBlock.instructions.first!.location.autoGenerated + let loc = entryBlock.instructions.first!.location.asAutoGenerated let builder = Builder(atBeginOf: entryBlock, location: loc, context) let temporary = builder.createAllocStack(argument.type) argument.uses.replaceAll(with: temporary, context) builder.createCopyAddr(from: argument, to: temporary, takeSource: true, initializeDest: true) - for block in function.blocks { - let terminator = block.terminator - if terminator.isFunctionExiting { - let exitBuilder = Builder(before: terminator, location: terminator.location.autoGenerated, context) - exitBuilder.createCopyAddr(from: temporary, to: argument, takeSource: true, initializeDest: true) - exitBuilder.createDeallocStack(temporary) - } + Builder.insertCleanupAtFunctionExits(of: function, context) { builder in + builder.createCopyAddr(from: temporary, to: argument, takeSource: true, initializeDest: true) + builder.createDeallocStack(temporary) } log("move addr protection in \(function.name): \(argument)") @@ -362,13 +358,13 @@ private struct StackProtectionOptimization { return } - let builder = Builder(after: beginAccess, location: beginAccess.location.autoGenerated, context) + let builder = Builder(after: beginAccess, location: beginAccess.location.asAutoGenerated, context) let temporary = builder.createAllocStack(beginAccess.type) - beginAccess.uses.ignoreUsers(ofType: EndAccessInst.self).replaceAll(with: temporary, context) + beginAccess.uses.ignoreUses(ofType: EndAccessInst.self).replaceAll(with: temporary, context) for endAccess in beginAccess.endInstructions { - let endBuilder = Builder(before: endAccess, location: endAccess.location.autoGenerated, context) + let endBuilder = Builder(before: endAccess, location: endAccess.location.asAutoGenerated, context) endBuilder.createCopyAddr(from: temporary, to: beginAccess, takeSource: true, initializeDest: true) endBuilder.createDeallocStack(temporary) } diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/PassManager/CMakeLists.txt index a1da2f6e96fa4..4a46ced58a617 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/CMakeLists.txt @@ -7,9 +7,10 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors swift_compiler_sources(Optimizer - Context.swift + ContextCommon.swift + FunctionPassContext.swift ModulePassContext.swift Options.swift Passes.swift - PassRegistration.swift) - + PassRegistration.swift + SimplifyContext.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift deleted file mode 100644 index 6803362b43395..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift +++ /dev/null @@ -1,803 +0,0 @@ -//===--- Context.swift - defines the context types ------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2022 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 AST -import SIL -import OptimizerBridging - -/// The base type of all contexts. -protocol Context { - var _bridged: BridgedPassContext { get } -} - -extension Context { - var options: Options { Options(_bridged: _bridged) } - - var diagnosticEngine: DiagnosticEngine { - return DiagnosticEngine(bridged: _bridged.getDiagnosticEngine()) - } - - // The calleeAnalysis is not specific to a function and therefore can be provided in - // all contexts. - var calleeAnalysis: CalleeAnalysis { - let bridgeCA = _bridged.getCalleeAnalysis() - return CalleeAnalysis(bridged: bridgeCA) - } - - var hadError: Bool { _bridged.hadError() } - - var silStage: SILStage { - switch _bridged.getSILStage() { - case .Raw: return .raw - case .Canonical: return .canonical - case .Lowered: return .lowered - default: fatalError("unhandled SILStage case") - } - } - - var currentModuleContext: ModuleDecl { - _bridged.getCurrentModuleContext().getAs(ModuleDecl.self) - } - - var moduleIsSerialized: Bool { _bridged.moduleIsSerialized() } - - /// Enable diagnostics requiring WMO (for @noLocks, @noAllocation - /// annotations, Embedded Swift, and class specialization). SourceKit is the - /// only consumer that has this disabled today (as it disables WMO - /// explicitly). - var enableWMORequiredDiagnostics: Bool { - _bridged.enableWMORequiredDiagnostics() - } - - func canMakeStaticObjectReadOnly(objectType: Type) -> Bool { - _bridged.canMakeStaticObjectReadOnly(objectType.bridged) - } - - func lookupDeinit(ofNominal: NominalTypeDecl) -> Function? { - _bridged.lookUpNominalDeinitFunction(ofNominal.bridged).function - } - - func getBuiltinIntegerType(bitWidth: Int) -> Type { _bridged.getBuiltinIntegerType(bitWidth).type } - - func lookupFunction(name: String) -> Function? { - name._withBridgedStringRef { - _bridged.lookupFunction($0).function - } - } - - func lookupWitnessTable(for conformance: Conformance) -> WitnessTable? { - return _bridged.lookupWitnessTable(conformance.bridged).witnessTable - } - - func lookupVTable(for classDecl: NominalTypeDecl) -> VTable? { - return _bridged.lookupVTable(classDecl.bridged).vTable - } - - func lookupSpecializedVTable(for classType: Type) -> VTable? { - return _bridged.lookupSpecializedVTable(classType.bridged).vTable - } - - func getSpecializedConformance(of genericConformance: Conformance, - for type: AST.`Type`, - substitutions: SubstitutionMap) -> Conformance - { - let c = _bridged.getSpecializedConformance(genericConformance.bridged, type.bridged, substitutions.bridged) - return Conformance(bridged: c) - } - - func notifyNewFunction(function: Function, derivedFrom: Function) { - _bridged.addFunctionToPassManagerWorklist(function.bridged, derivedFrom.bridged) - } -} - -/// A context which allows mutation of a function's SIL. -protocol MutatingContext : Context { - // Called by all instruction mutations, including inserted new instructions. - var notifyInstructionChanged: (Instruction) -> () { get } -} - -extension MutatingContext { - func notifyInvalidatedStackNesting() { _bridged.notifyInvalidatedStackNesting() } - var needFixStackNesting: Bool { _bridged.getNeedFixStackNesting() } - - func verifyIsTransforming(function: Function) { - precondition(_bridged.isTransforming(function.bridged), "pass modifies wrong function") - } - - /// Splits the basic block, which contains `inst`, before `inst` and returns the - /// new block. - /// - /// `inst` and all subsequent instructions are moved to the new block, while all - /// instructions _before_ `inst` remain in the original block. - func splitBlock(before inst: Instruction) -> BasicBlock { - notifyBranchesChanged() - return _bridged.splitBlockBefore(inst.bridged).block - } - - /// Splits the basic block, which contains `inst`, after `inst` and returns the - /// new block. - /// - /// All subsequent instructions after `inst` are moved to the new block, while `inst` and all - /// instructions _before_ `inst` remain in the original block. - func splitBlock(after inst: Instruction) -> BasicBlock { - notifyBranchesChanged() - return _bridged.splitBlockAfter(inst.bridged).block - } - - func createBlock(after block: BasicBlock) -> BasicBlock { - notifyBranchesChanged() - return _bridged.createBlockAfter(block.bridged).block - } - - func erase(instruction: Instruction) { - if !instruction.isInStaticInitializer { - verifyIsTransforming(function: instruction.parentFunction) - } - if instruction is FullApplySite { - notifyCallsChanged() - } - if instruction is TermInst { - notifyBranchesChanged() - } - notifyInstructionsChanged() - - _bridged.eraseInstruction(instruction.bridged) - } - - func erase(instructionIncludingAllUsers inst: Instruction) { - if inst.isDeleted { - return - } - for result in inst.results { - for use in result.uses { - erase(instructionIncludingAllUsers: use.instruction) - } - } - erase(instruction: inst) - } - - func erase(instructions: S) where S.Element: Instruction { - for inst in instructions { - erase(instruction: inst) - } - } - - func erase(instructionIncludingDebugUses inst: Instruction) { - precondition(inst.results.allSatisfy { $0.uses.ignoreDebugUses.isEmpty }) - erase(instructionIncludingAllUsers: inst) - } - - func erase(block: BasicBlock) { - _bridged.eraseBlock(block.bridged) - } - - func tryOptimizeApplyOfPartialApply(closure: PartialApplyInst) -> Bool { - if _bridged.tryOptimizeApplyOfPartialApply(closure.bridged) { - notifyInstructionsChanged() - notifyCallsChanged() - - for use in closure.callee.uses { - if use.instruction is FullApplySite { - notifyInstructionChanged(use.instruction) - } - } - return true - } - return false - } - - func tryDeleteDeadClosure(closure: SingleValueInstruction, needKeepArgsAlive: Bool = true) -> Bool { - if _bridged.tryDeleteDeadClosure(closure.bridged, needKeepArgsAlive) { - notifyInstructionsChanged() - return true - } - return false - } - - func tryDevirtualize(apply: FullApplySite, isMandatory: Bool) -> ApplySite? { - let result = _bridged.tryDevirtualizeApply(apply.bridged, isMandatory) - if let newApply = result.newApply.instruction { - erase(instruction: apply) - notifyInstructionsChanged() - notifyCallsChanged() - if result.cfgChanged { - notifyBranchesChanged() - } - notifyInstructionChanged(newApply) - return newApply as! FullApplySite - } - return nil - } - - func tryOptimizeKeypath(apply: FullApplySite) -> Bool { - return _bridged.tryOptimizeKeypath(apply.bridged) - } - - func inlineFunction(apply: FullApplySite, mandatoryInline: Bool) { - // This is only a best-effort attempt to notify the new cloned instructions as changed. - // TODO: get a list of cloned instructions from the `inlineFunction` - let instAfterInling: Instruction? - switch apply { - case is ApplyInst: - instAfterInling = apply.next - case let beginApply as BeginApplyInst: - let next = beginApply.next! - instAfterInling = (next is EndApplyInst ? nil : next) - case is TryApplyInst: - instAfterInling = apply.parentBlock.next?.instructions.first - default: - instAfterInling = nil - } - - _bridged.inlineFunction(apply.bridged, mandatoryInline) - - if let instAfterInling = instAfterInling { - notifyNewInstructions(from: apply, to: instAfterInling) - } - } - - func loadFunction(function: Function, loadCalleesRecursively: Bool) -> Bool { - if function.isDefinition { - return true - } - _bridged.loadFunction(function.bridged, loadCalleesRecursively) - return function.isDefinition - } - - private func notifyNewInstructions(from: Instruction, to: Instruction) { - var inst = from - while inst != to { - if !inst.isDeleted { - notifyInstructionChanged(inst) - } - if let next = inst.next { - inst = next - } else { - inst = inst.parentBlock.next!.instructions.first! - } - } - } - - func getContextSubstitutionMap(for type: Type) -> SubstitutionMap { - SubstitutionMap(bridged: _bridged.getContextSubstitutionMap(type.bridged)) - } - - func notifyInstructionsChanged() { - _bridged.asNotificationHandler().notifyChanges(.instructionsChanged) - } - - func notifyCallsChanged() { - _bridged.asNotificationHandler().notifyChanges(.callsChanged) - } - - func notifyBranchesChanged() { - _bridged.asNotificationHandler().notifyChanges(.branchesChanged) - } - - /// Notifies the pass manager that the optimization result of the current pass depends - /// on the body (i.e. SIL instructions) of another function than the currently optimized one. - func notifyDependency(onBodyOf otherFunction: Function) { - _bridged.notifyDependencyOnBodyOf(otherFunction.bridged) - } -} - -/// The context which is passed to the run-function of a FunctionPass. -struct FunctionPassContext : MutatingContext { - let _bridged: BridgedPassContext - - // A no-op. - var notifyInstructionChanged: (Instruction) -> () { return { inst in } } - - func continueWithNextSubpassRun(for inst: Instruction? = nil) -> Bool { - return _bridged.continueWithNextSubpassRun(inst.bridged) - } - - func createSimplifyContext(preserveDebugInfo: Bool, notifyInstructionChanged: @escaping (Instruction) -> ()) -> SimplifyContext { - SimplifyContext(_bridged: _bridged, notifyInstructionChanged: notifyInstructionChanged, preserveDebugInfo: preserveDebugInfo) - } - - var deadEndBlocks: DeadEndBlocksAnalysis { - let bridgeDEA = _bridged.getDeadEndBlocksAnalysis() - return DeadEndBlocksAnalysis(bridged: bridgeDEA) - } - - var dominatorTree: DominatorTree { - let bridgedDT = _bridged.getDomTree() - return DominatorTree(bridged: bridgedDT) - } - - var postDominatorTree: PostDominatorTree { - let bridgedPDT = _bridged.getPostDomTree() - return PostDominatorTree(bridged: bridgedPDT) - } - - var swiftArrayDecl: NominalTypeDecl { - _bridged.getSwiftArrayDecl().getAs(NominalTypeDecl.self) - } - - func loadFunction(name: StaticString, loadCalleesRecursively: Bool) -> Function? { - return name.withUTF8Buffer { (nameBuffer: UnsafeBufferPointer) in - let nameStr = BridgedStringRef(data: nameBuffer.baseAddress, count: nameBuffer.count) - return _bridged.loadFunction(nameStr, loadCalleesRecursively).function - } - } - - /// Looks up a function in the `Swift` module. - /// The `name` is the source name of the function and not the mangled name. - /// Returns nil if no such function or multiple matching functions are found. - func lookupStdlibFunction(name: StaticString) -> Function? { - return name.withUTF8Buffer { (nameBuffer: UnsafeBufferPointer) in - let nameStr = BridgedStringRef(data: nameBuffer.baseAddress, count: nameBuffer.count) - return _bridged.lookupStdlibFunction(nameStr).function - } - } - - func modifyEffects(in function: Function, _ body: (inout FunctionEffects) -> ()) { - notifyEffectsChanged() - function._modifyEffects(body) - } - - fileprivate func notifyEffectsChanged() { - _bridged.asNotificationHandler().notifyChanges(.effectsChanged) - } - - func eliminateDeadAllocations(in function: Function) -> Bool { - if _bridged.eliminateDeadAllocations(function.bridged) { - notifyInstructionsChanged() - return true - } - return false - } - - func specializeClassMethodInst(_ cm: ClassMethodInst) -> Bool { - if _bridged.specializeClassMethodInst(cm.bridged) { - notifyInstructionsChanged() - notifyCallsChanged() - return true - } - return false - } - - func specializeWitnessMethodInst(_ wm: WitnessMethodInst) -> Bool { - if _bridged.specializeWitnessMethodInst(wm.bridged) { - notifyInstructionsChanged() - notifyCallsChanged() - return true - } - return false - } - - func specializeApplies(in function: Function, isMandatory: Bool) -> Bool { - if _bridged.specializeAppliesInFunction(function.bridged, isMandatory) { - notifyInstructionsChanged() - notifyCallsChanged() - return true - } - return false - } - - func mangleOutlinedVariable(from function: Function) -> String { - return String(taking: _bridged.mangleOutlinedVariable(function.bridged)) - } - - func mangle(withClosureArguments closureArgs: [Value], closureArgIndices: [Int], from applySiteCallee: Function) -> String { - closureArgs.withBridgedValues { bridgedClosureArgsRef in - closureArgIndices.withBridgedArrayRef{bridgedClosureArgIndicesRef in - String(taking: _bridged.mangleWithClosureArgs( - bridgedClosureArgsRef, - bridgedClosureArgIndicesRef, - applySiteCallee.bridged - )) - } - } - } - - func createGlobalVariable(name: String, type: Type, linkage: Linkage, isLet: Bool) -> GlobalVariable { - let gv = name._withBridgedStringRef { - _bridged.createGlobalVariable($0, type.bridged, linkage.bridged, isLet) - } - return gv.globalVar - } - - func createFunctionForClosureSpecialization(from applySiteCallee: Function, withName specializedFunctionName: String, - withParams specializedParameters: [ParameterInfo], - withSerialization isSerialized: Bool) -> Function - { - return specializedFunctionName._withBridgedStringRef { nameRef in - let bridgedParamInfos = specializedParameters.map { $0._bridged } - - return bridgedParamInfos.withUnsafeBufferPointer { paramBuf in - _bridged.ClosureSpecializer_createEmptyFunctionWithSpecializedSignature(nameRef, paramBuf.baseAddress, - paramBuf.count, - applySiteCallee.bridged, - isSerialized).function - } - } - } - - func buildSpecializedFunction(specializedFunction: Function, buildFn: (Function, FunctionPassContext) -> T) -> T { - let nestedFunctionPassContext = - FunctionPassContext(_bridged: _bridged.initializeNestedPassContext(specializedFunction.bridged)) - - defer { _bridged.deinitializedNestedPassContext() } - - return buildFn(specializedFunction, nestedFunctionPassContext) - } - - /// Makes sure that the lifetime of `value` ends at all control flow paths, even in dead-end blocks. - /// Inserts destroys in dead-end blocks if those are missing. - func completeLifetime(of value: Value) { - if _bridged.completeLifetime(value.bridged) { - notifyInstructionsChanged() - } - } -} - -struct SimplifyContext : MutatingContext { - let _bridged: BridgedPassContext - let notifyInstructionChanged: (Instruction) -> () - let preserveDebugInfo: Bool -} - -extension Type { - func getStaticSize(context: SimplifyContext) -> Int? { - let v = context._bridged.getStaticSize(self.bridged) - return v == -1 ? nil : v - } - - func getStaticAlignment(context: SimplifyContext) -> Int? { - let v = context._bridged.getStaticAlignment(self.bridged) - return v == -1 ? nil : v - } - - func getStaticStride(context: SimplifyContext) -> Int? { - let v = context._bridged.getStaticStride(self.bridged) - return v == -1 ? nil : v - } -} - -//===----------------------------------------------------------------------===// -// Builder initialization -//===----------------------------------------------------------------------===// - -private extension Instruction { - /// Returns self, unless self is a meta instruction, in which case the next - /// non-meta instruction is returned. Returns nil if there are no non-meta - /// instructions in the basic block. - var nextNonMetaInstruction: Instruction? { - for inst in InstructionList(first: self) where !(inst is MetaInstruction) { - return inst - } - return nil - } - - /// Returns the next interesting location. As it is impossible to set a - /// breakpoint on a meta instruction, those are skipped. - /// However, we don't want to take a location with different inlining - /// information than this instruction, so in that case, we will return the - /// location of the meta instruction. If the meta instruction is the only - /// instruction in the basic block, we also take its location. - var locationOfNextNonMetaInstruction: Location { - let location = self.location - guard !location.isInlined, - let nextLocation = nextNonMetaInstruction?.location, - !nextLocation.isInlined else { - return location - } - return nextLocation - } -} - -extension Builder { - /// Creates a builder which inserts _before_ `insPnt`, using a custom `location`. - init(before insPnt: Instruction, location: Location, _ context: some MutatingContext) { - context.verifyIsTransforming(function: insPnt.parentFunction) - self.init(insertAt: .before(insPnt), location: location, - context.notifyInstructionChanged, context._bridged.asNotificationHandler()) - } - - /// Creates a builder which inserts before `insPnt`, using `insPnt`'s next - /// non-meta instruction's location. - /// This function should be used when moving code to an unknown insert point, - /// when we want to inherit the location of the closest non-meta instruction. - /// For replacing an existing meta instruction with another, use - /// ``Builder.init(replacing:_:)``. - init(before insPnt: Instruction, _ context: some MutatingContext) { - context.verifyIsTransforming(function: insPnt.parentFunction) - self.init(insertAt: .before(insPnt), - location: insPnt.locationOfNextNonMetaInstruction, - context.notifyInstructionChanged, context._bridged.asNotificationHandler()) - } - - /// Creates a builder which inserts _before_ `insPnt`, using the exact location of `insPnt`, - /// for the purpose of replacing that meta instruction with an equivalent instruction. - /// This function does not delete `insPnt`. - init(replacing insPnt: MetaInstruction, _ context: some MutatingContext) { - context.verifyIsTransforming(function: insPnt.parentFunction) - self.init(insertAt: .before(insPnt), location: insPnt.location, - context.notifyInstructionChanged, context._bridged.asNotificationHandler()) - } - - /// Creates a builder which inserts _after_ `insPnt`, using a custom `location`. - /// - /// TODO: this is usually incorrect for terminator instructions. Instead use - /// `Builder.insert(after:location:_:insertFunc)` from OptUtils.swift. Rename this to afterNonTerminator. - init(after insPnt: Instruction, location: Location, _ context: some MutatingContext) { - context.verifyIsTransforming(function: insPnt.parentFunction) - guard let nextInst = insPnt.next else { - fatalError("cannot insert an instruction after a block terminator.") - } - self.init(insertAt: .before(nextInst), location: location, - context.notifyInstructionChanged, context._bridged.asNotificationHandler()) - } - - /// Creates a builder which inserts _after_ `insPnt`, using `insPnt`'s next - /// non-meta instruction's location. - /// - /// TODO: this is incorrect for terminator instructions. Instead use `Builder.insert(after:location:_:insertFunc)` - /// from OptUtils.swift. Rename this to afterNonTerminator. - init(after insPnt: Instruction, _ context: some MutatingContext) { - self.init(after: insPnt, location: insPnt.locationOfNextNonMetaInstruction, context) - } - - /// Creates a builder which inserts at the end of `block`, using a custom `location`. - init(atEndOf block: BasicBlock, location: Location, _ context: some MutatingContext) { - context.verifyIsTransforming(function: block.parentFunction) - self.init(insertAt: .atEndOf(block), location: location, - context.notifyInstructionChanged, context._bridged.asNotificationHandler()) - } - - /// Creates a builder which inserts at the begin of `block`, using a custom `location`. - init(atBeginOf block: BasicBlock, location: Location, _ context: some MutatingContext) { - context.verifyIsTransforming(function: block.parentFunction) - let firstInst = block.instructions.first! - self.init(insertAt: .before(firstInst), location: location, - context.notifyInstructionChanged, context._bridged.asNotificationHandler()) - } - - /// Creates a builder which inserts at the begin of `block`, using the location of the first - /// non-meta instruction of `block`. - init(atBeginOf block: BasicBlock, _ context: some MutatingContext) { - context.verifyIsTransforming(function: block.parentFunction) - let firstInst = block.instructions.first! - self.init(insertAt: .before(firstInst), - location: firstInst.locationOfNextNonMetaInstruction, - context.notifyInstructionChanged, context._bridged.asNotificationHandler()) - } - - /// Creates a builder which inserts instructions into an empty function, using the location of the function itself. - init(atStartOf function: Function, _ context: some MutatingContext) { - context.verifyIsTransforming(function: function) - self.init(insertAt: .atStartOf(function), location: function.location, - context.notifyInstructionChanged, context._bridged.asNotificationHandler()) - } - - init(staticInitializerOf global: GlobalVariable, _ context: some MutatingContext) { - self.init(insertAt: .staticInitializer(global), - location: Location.artificialUnreachableLocation, - { _ in }, context._bridged.asNotificationHandler()) - } -} - -//===----------------------------------------------------------------------===// -// Modifying the SIL -//===----------------------------------------------------------------------===// - -extension Undef { - static func get(type: Type, _ context: some MutatingContext) -> Undef { - context._bridged.getSILUndef(type.bridged).value as! Undef - } -} - -extension BasicBlock { - func addArgument(type: Type, ownership: Ownership, _ context: some MutatingContext) -> Argument { - context.notifyInstructionsChanged() - return bridged.addBlockArgument(type.bridged, ownership._bridged).argument - } - - func addFunctionArgument(type: Type, _ context: some MutatingContext) -> FunctionArgument { - context.notifyInstructionsChanged() - return bridged.addFunctionArgument(type.bridged).argument as! FunctionArgument - } - - func eraseArgument(at index: Int, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.eraseArgument(index) - } - - func moveAllInstructions(toBeginOf otherBlock: BasicBlock, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - context.notifyBranchesChanged() - bridged.moveAllInstructionsToBegin(otherBlock.bridged) - } - - func moveAllInstructions(toEndOf otherBlock: BasicBlock, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - context.notifyBranchesChanged() - bridged.moveAllInstructionsToEnd(otherBlock.bridged) - } - - func eraseAllArguments(_ context: some MutatingContext) { - // Arguments are stored in an array. We need to erase in reverse order to avoid quadratic complexity. - for argIdx in (0 ..< arguments.count).reversed() { - eraseArgument(at: argIdx, context) - } - } - - func moveAllArguments(to otherBlock: BasicBlock, _ context: some MutatingContext) { - bridged.moveArgumentsTo(otherBlock.bridged) - } -} - -extension Argument { - func set(reborrow: Bool, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.setReborrow(reborrow) - } -} - -extension AllocRefInstBase { - func setIsStackAllocatable(_ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.AllocRefInstBase_setIsStackAllocatable() - context.notifyInstructionChanged(self) - } -} - -extension Sequence where Element == Operand { - func replaceAll(with replacement: Value, _ context: some MutatingContext) { - for use in self { - use.set(to: replacement, context) - } - } -} - -extension Operand { - func set(to value: Value, _ context: some MutatingContext) { - instruction.setOperand(at: index, to: value, context) - } - - func changeOwnership(from: Ownership, to: Ownership, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.changeOwnership(from._bridged, to._bridged) - context.notifyInstructionChanged(instruction) - } -} - -extension Instruction { - func setOperand(at index : Int, to value: Value, _ context: some MutatingContext) { - if let apply = self as? FullApplySite, apply.isCallee(operand: operands[index]) { - context.notifyCallsChanged() - } - context.notifyInstructionsChanged() - bridged.setOperand(index, value.bridged) - context.notifyInstructionChanged(self) - } - - func move(before otherInstruction: Instruction, _ context: some MutatingContext) { - BridgedPassContext.moveInstructionBefore(bridged, otherInstruction.bridged) - context.notifyInstructionsChanged() - } -} - -extension BuiltinInst { - func constantFold(_ context: some MutatingContext) -> Value? { - context._bridged.constantFoldBuiltin(bridged).value - } -} - -extension RefCountingInst { - func setAtomicity(isAtomic: Bool, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.RefCountingInst_setIsAtomic(isAtomic) - context.notifyInstructionChanged(self) - } -} - -extension AllocRefInst { - func setIsBare(_ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.AllocRefInst_setIsBare() - context.notifyInstructionChanged(self) - } -} - -extension RefElementAddrInst { - func set(isImmutable: Bool, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.RefElementAddrInst_setImmutable(isImmutable) - context.notifyInstructionChanged(self) - } -} - -extension GlobalAddrInst { - func clearToken(_ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.GlobalAddrInst_clearToken() - context.notifyInstructionChanged(self) - } -} - -extension GlobalValueInst { - func setIsBare(_ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.GlobalValueInst_setIsBare() - context.notifyInstructionChanged(self) - } -} - -extension LoadInst { - func set(ownership: LoadInst.LoadOwnership, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.LoadInst_setOwnership(ownership.rawValue) - context.notifyInstructionChanged(self) - } -} - -extension PointerToAddressInst { - func set(alignment: Int?, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.PointerToAddressInst_setAlignment(UInt64(alignment ?? 0)) - context.notifyInstructionChanged(self) - } -} - -extension TermInst { - func replaceBranchTarget(from fromBlock: BasicBlock, to toBlock: BasicBlock, _ context: some MutatingContext) { - context.notifyBranchesChanged() - bridged.TermInst_replaceBranchTarget(fromBlock.bridged, toBlock.bridged) - } -} - -extension ForwardingInstruction { - func setForwardingOwnership(to ownership: Ownership, _ context: some MutatingContext) { - context.notifyInstructionsChanged() - bridged.ForwardingInst_setForwardingOwnership(ownership._bridged) - } -} - -extension Function { - func set(needStackProtection: Bool, _ context: FunctionPassContext) { - context.notifyEffectsChanged() - bridged.setNeedStackProtection(needStackProtection) - } - - func set(thunkKind: ThunkKind, _ context: FunctionPassContext) { - context.notifyEffectsChanged() - switch thunkKind { - case .noThunk: bridged.setThunk(.IsNotThunk) - case .thunk: bridged.setThunk(.IsThunk) - case .reabstractionThunk: bridged.setThunk(.IsReabstractionThunk) - case .signatureOptimizedThunk: bridged.setThunk(.IsSignatureOptimizedThunk) - } - } - - func set(isPerformanceConstraint: Bool, _ context: FunctionPassContext) { - context.notifyEffectsChanged() - bridged.setIsPerformanceConstraint(isPerformanceConstraint) - } - - func fixStackNesting(_ context: FunctionPassContext) { - context._bridged.fixStackNesting(bridged) - } - - func appendNewBlock(_ context: FunctionPassContext) -> BasicBlock { - context.notifyBranchesChanged() - return context._bridged.appendBlock(bridged).block - } -} - -extension DeclRef { - func calleesAreStaticallyKnowable(_ context: some Context) -> Bool { - context._bridged.calleesAreStaticallyKnowable(bridged) - } -} diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/ContextCommon.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/ContextCommon.swift new file mode 100644 index 0000000000000..2d131b3ac20e2 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/ContextCommon.swift @@ -0,0 +1,168 @@ +//===--- Context.swift - defines the context types ------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 AST +import SIL +import OptimizerBridging + +extension Context { + var bridgedPassContext: BridgedPassContext { + BridgedPassContext(_bridged) + } + + var options: Options { Options(_bridged: bridgedPassContext) } + + var diagnosticEngine: DiagnosticEngine { + return DiagnosticEngine(bridged: bridgedPassContext.getDiagnosticEngine()) + } + + // The calleeAnalysis is not specific to a function and therefore can be provided in + // all contexts. + var calleeAnalysis: CalleeAnalysis { + let bridgeCA = bridgedPassContext.getCalleeAnalysis() + return CalleeAnalysis(bridged: bridgeCA) + } + + var hadError: Bool { bridgedPassContext.hadError() } + + /// Enable diagnostics requiring WMO (for @noLocks, @noAllocation + /// annotations, Embedded Swift, and class specialization). SourceKit is the + /// only consumer that has this disabled today (as it disables WMO + /// explicitly). + var enableWMORequiredDiagnostics: Bool { + bridgedPassContext.enableWMORequiredDiagnostics() + } + + func canMakeStaticObjectReadOnly(objectType: Type) -> Bool { + bridgedPassContext.canMakeStaticObjectReadOnly(objectType.bridged) + } +} + +extension MutatingContext { + func notifyInvalidatedStackNesting() { bridgedPassContext.notifyInvalidatedStackNesting() } + var needFixStackNesting: Bool { bridgedPassContext.getNeedFixStackNesting() } + + func tryOptimizeApplyOfPartialApply(closure: PartialApplyInst) -> Bool { + if bridgedPassContext.tryOptimizeApplyOfPartialApply(closure.bridged) { + notifyInstructionsChanged() + notifyCallsChanged() + + for use in closure.callee.uses { + if use.instruction is FullApplySite { + notifyInstructionChanged(use.instruction) + } + } + return true + } + return false + } + + func tryDeleteDeadClosure(closure: SingleValueInstruction, needKeepArgsAlive: Bool = true) -> Bool { + if bridgedPassContext.tryDeleteDeadClosure(closure.bridged, needKeepArgsAlive) { + notifyInstructionsChanged() + return true + } + return false + } + + func tryDevirtualize(apply: FullApplySite, isMandatory: Bool) -> ApplySite? { + let result = bridgedPassContext.tryDevirtualizeApply(apply.bridged, isMandatory) + if let newApply = result.newApply.instruction { + erase(instruction: apply) + notifyInstructionsChanged() + notifyCallsChanged() + if result.cfgChanged { + notifyBranchesChanged() + } + notifyInstructionChanged(newApply) + return newApply as! FullApplySite + } + return nil + } + + func tryOptimizeKeypath(apply: FullApplySite) -> Bool { + return bridgedPassContext.tryOptimizeKeypath(apply.bridged) + } + + func inlineFunction(apply: FullApplySite, mandatoryInline: Bool) { + // This is only a best-effort attempt to notify the new cloned instructions as changed. + // TODO: get a list of cloned instructions from the `inlineFunction` + let instBeforeInlining = apply.previous + let instAfterInlining: Instruction? + switch apply { + case is ApplyInst: + instAfterInlining = apply.next + case let beginApply as BeginApplyInst: + let next = beginApply.next! + instAfterInlining = (next is EndApplyInst ? nil : next) + case is TryApplyInst: + instAfterInlining = apply.parentBlock.next?.instructions.first + default: + instAfterInlining = nil + } + + bridgedPassContext.inlineFunction(apply.bridged, mandatoryInline) + + if let instBeforeInlining = instBeforeInlining?.next, + let instAfterInlining = instAfterInlining, + !instAfterInlining.isDeleted { + notifyNewInstructions(from: instBeforeInlining, to: instAfterInlining) + } + } + + func loadFunction(function: Function, loadCalleesRecursively: Bool) -> Bool { + if function.isDefinition { + return true + } + _bridged.loadFunction(function.bridged, loadCalleesRecursively) + return function.isDefinition + } + + private func notifyNewInstructions(from: Instruction, to: Instruction) { + var inst = from + while inst != to { + if !inst.isDeleted { + notifyInstructionChanged(inst) + } + if let next = inst.next { + inst = next + } else { + inst = inst.parentBlock.next!.instructions.first! + } + } + } + + func getContextSubstitutionMap(for type: Type) -> SubstitutionMap { + SubstitutionMap(bridged: _bridged.getContextSubstitutionMap(type.bridged)) + } + + /// Notifies the pass manager that the optimization result of the current pass depends + /// on the body (i.e. SIL instructions) of another function than the currently optimized one. + func notifyDependency(onBodyOf otherFunction: Function) { + bridgedPassContext.notifyDependencyOnBodyOf(otherFunction.bridged) + } +} + +extension Instruction { + var arraySemanticsCallKind: ArrayCallKind { + return BridgedPassContext.getArraySemanticsCallKind(self.bridged) + } + + func canHoistArraySemanticsCall(to toInst: Instruction, _ context: FunctionPassContext) -> Bool { + return context.bridgedPassContext.canHoistArraySemanticsCall(self.bridged, toInst.bridged) + } + + func hoistArraySemanticsCall(before toInst: Instruction, _ context: some MutatingContext) { + context.bridgedPassContext.hoistArraySemanticsCall(self.bridged, toInst.bridged) // Internally updates dom tree. + context.notifyInstructionsChanged() + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/FunctionPassContext.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/FunctionPassContext.swift new file mode 100644 index 0000000000000..f2ec4f144b889 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/FunctionPassContext.swift @@ -0,0 +1,197 @@ +//===--- FunctionPassContext.swift ----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 AST +import SIL +import OptimizerBridging + +/// The context which is passed to the run-function of a FunctionPass. +struct FunctionPassContext : MutatingContext { + let _bridged: BridgedContext + + // A no-op. + var notifyInstructionChanged: (Instruction) -> () { return { inst in } } + + func continueWithNextSubpassRun(for inst: Instruction? = nil) -> Bool { + return bridgedPassContext.continueWithNextSubpassRun(inst.bridged) + } + + func createSimplifyContext(preserveDebugInfo: Bool, + notifyInstructionChanged: @escaping (Instruction) -> () + ) -> SimplifyContext { + SimplifyContext(_bridged: _bridged, notifyInstructionChanged: notifyInstructionChanged, + preserveDebugInfo: preserveDebugInfo) + } + + var deadEndBlocks: DeadEndBlocksAnalysis { + let bridgeDEA = bridgedPassContext.getDeadEndBlocksAnalysis() + return DeadEndBlocksAnalysis(bridged: bridgeDEA) + } + + var dominatorTree: DominatorTree { + let bridgedDT = bridgedPassContext.getDomTree() + return DominatorTree(bridged: bridgedDT) + } + + var postDominatorTree: PostDominatorTree { + let bridgedPDT = bridgedPassContext.getPostDomTree() + return PostDominatorTree(bridged: bridgedPDT) + } + + var loopTree: LoopTree { + let bridgedLT = bridgedPassContext.getLoopTree() + return LoopTree(bridged: bridgedLT, context: self) + } + + func notifyNewFunction(function: Function, derivedFrom: Function) { + bridgedPassContext.addFunctionToPassManagerWorklist(function.bridged, derivedFrom.bridged) + } + + func loadFunction(name: StaticString, loadCalleesRecursively: Bool) -> Function? { + return name.withUTF8Buffer { (nameBuffer: UnsafeBufferPointer) in + let nameStr = BridgedStringRef(data: nameBuffer.baseAddress, count: nameBuffer.count) + return _bridged.loadFunction(nameStr, loadCalleesRecursively).function + } + } + + func eliminateDeadAllocations(in function: Function) -> Bool { + if bridgedPassContext.eliminateDeadAllocations(function.bridged) { + notifyInstructionsChanged() + return true + } + return false + } + + func specializeClassMethodInst(_ cm: ClassMethodInst) -> Bool { + if bridgedPassContext.specializeClassMethodInst(cm.bridged) { + notifyInstructionsChanged() + notifyCallsChanged() + return true + } + return false + } + + func specializeWitnessMethodInst(_ wm: WitnessMethodInst) -> Bool { + if bridgedPassContext.specializeWitnessMethodInst(wm.bridged) { + notifyInstructionsChanged() + notifyCallsChanged() + return true + } + return false + } + + func specializeApplies(in function: Function, isMandatory: Bool) -> Bool { + if bridgedPassContext.specializeAppliesInFunction(function.bridged, isMandatory) { + notifyInstructionsChanged() + notifyCallsChanged() + return true + } + return false + } + + func specialize(function: Function, + for substitutions: SubstitutionMap, + convertIndirectToDirect: Bool, + isMandatory: Bool + ) -> Function? { + return bridgedPassContext.specializeFunction(function.bridged, substitutions.bridged, + convertIndirectToDirect, isMandatory).function + } + + func mangleOutlinedVariable(from function: Function) -> String { + return String(taking: bridgedPassContext.mangleOutlinedVariable(function.bridged)) + } + + enum ClosureArgumentMangling { + case closure(SingleValueInstruction) + + /// The argument specializes for the same closure as a previous argument, e.g. + /// ``` + /// %1 = partial_apply %closure + /// apply %f(%1, %1) // first argument: `.closure(%1)` + /// // second argument: `.previousArgumentIndex(0)` + case previousArgumentIndex(Int) + } + + func mangle(withClosureArguments closureArgs: [(argumentIndex: Int, argumentValue: ClosureArgumentMangling)], + from applySiteCallee: Function + ) -> String { + let bridgedArgManglings = closureArgs.map { + switch $0.argumentValue { + case .closure(let closure): + return BridgedPassContext.ClosureArgMangling(argIdx: $0.argumentIndex, + inst: Optional(closure).bridged, + otherArgIdx: -1) + case .previousArgumentIndex(let idx): + return BridgedPassContext.ClosureArgMangling(argIdx: $0.argumentIndex, + inst: OptionalBridgedInstruction(), + otherArgIdx: idx) + } + } + + return bridgedArgManglings.withBridgedArrayRef{ bridgedClosureArgs in + String(taking: bridgedPassContext.mangleWithClosureArgs(bridgedClosureArgs, applySiteCallee.bridged)) + } + } + + func mangle(withConstantCaptureArguments constArgs: [(argumentIndex: Int, argument: Value)], + from applySiteCallee: Function + ) -> String { + let bridgedConstArgs = constArgs.map { ($0.argumentIndex, $0.argument.bridged) } + return bridgedConstArgs.withBridgedArrayRef{ bridgedConstArgsArray in + String(taking: bridgedPassContext.mangleWithConstCaptureArgs(bridgedConstArgsArray, applySiteCallee.bridged)) + } + } + + func mangle(withBoxToStackPromotedArguments argIndices: [Int], from original: Function) -> String { + argIndices.withBridgedArrayRef { bridgedArgIndices in + String(taking: bridgedPassContext.mangleWithBoxToStackPromotedArgs(bridgedArgIndices, original.bridged)) + } + } + + func createSpecializedFunctionDeclaration(from original: Function, withName specializedFunctionName: String, + withParams specializedParameters: [ParameterInfo], + makeThin: Bool = false, + makeBare: Bool = false, + preserveGenericSignature: Bool = true) -> Function + { + return specializedFunctionName._withBridgedStringRef { nameRef in + let bridgedParamInfos = specializedParameters.map { $0._bridged } + + return bridgedParamInfos.withUnsafeBufferPointer { paramBuf in + bridgedPassContext.createSpecializedFunctionDeclaration(nameRef, paramBuf.baseAddress, paramBuf.count, + original.bridged, makeThin, makeBare, + preserveGenericSignature).function + } + } + } + + func buildSpecializedFunction(specializedFunction: Function, buildFn: (Function, FunctionPassContext) -> T) -> T { + let nestedBridgedContext = bridgedPassContext.initializeNestedPassContext(specializedFunction.bridged) + let nestedContext = FunctionPassContext(_bridged: nestedBridgedContext) + defer { bridgedPassContext.deinitializedNestedPassContext() } + + return buildFn(specializedFunction, nestedContext) + } + + /// Makes sure that the lifetime of `value` ends at all control flow paths, even in dead-end blocks. + /// Inserts destroys in dead-end blocks if those are missing. + func completeLifetime(of value: Value) { + if bridgedPassContext.completeLifetime(value.bridged) { + notifyInstructionsChanged() + } + } + + func fixStackNesting(in function: Function) { + bridgedPassContext.fixStackNesting(function.bridged) + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/ModulePassContext.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/ModulePassContext.swift index 7a1e9f39354f8..9042bbbb1ce5b 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/ModulePassContext.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/ModulePassContext.swift @@ -20,7 +20,7 @@ import OptimizerBridging /// but it doesn't provide any APIs to modify functions. /// In order to modify a function, a module pass must use `transform(function:)`. struct ModulePassContext : Context, CustomStringConvertible { - let _bridged: BridgedPassContext + let _bridged: BridgedContext var description: String { return String(taking: _bridged.getModuleDescription()) @@ -95,21 +95,21 @@ struct ModulePassContext : Context, CustomStringConvertible { } var functions: FunctionList { - FunctionList(first: _bridged.getFirstFunctionInModule().function) + FunctionList(first: bridgedPassContext.getFirstFunctionInModule().function) } var globalVariables: GlobalVariableList { - GlobalVariableList(first: _bridged.getFirstGlobalInModule().globalVar) + GlobalVariableList(first: bridgedPassContext.getFirstGlobalInModule().globalVar) } - var vTables: VTableArray { VTableArray(bridgedCtxt: _bridged) } + var vTables: VTableArray { VTableArray(bridgedCtxt: bridgedPassContext) } var witnessTables: WitnessTableList { - WitnessTableList(first: _bridged.getFirstWitnessTableInModule().witnessTable) + WitnessTableList(first: bridgedPassContext.getFirstWitnessTableInModule().witnessTable) } var defaultWitnessTables: DefaultWitnessTableList { - DefaultWitnessTableList(first: _bridged.getFirstDefaultWitnessTableInModule().defaultWitnessTable) + DefaultWitnessTableList(first: bridgedPassContext.getFirstDefaultWitnessTableInModule().defaultWitnessTable) } /// Run a closure with a `PassContext` for a function, which allows to modify that function. @@ -117,9 +117,10 @@ struct ModulePassContext : Context, CustomStringConvertible { /// Only a single `transform` can be alive at the same time, i.e. it's not allowed to nest /// calls to `transform`. func transform(function: Function, _ runOnFunction: (FunctionPassContext) -> ()) { - _bridged.beginTransformFunction(function.bridged) - runOnFunction(FunctionPassContext(_bridged: _bridged)) - _bridged.endTransformFunction(); + let nestedBridgedContext = bridgedPassContext.initializeNestedPassContext(function.bridged) + let nestedContext = FunctionPassContext(_bridged: nestedBridgedContext) + runOnFunction(nestedContext) + bridgedPassContext.deinitializedNestedPassContext() } func loadFunction(function: Function, loadCalleesRecursively: Bool) -> Bool { @@ -130,8 +131,13 @@ struct ModulePassContext : Context, CustomStringConvertible { return function.isDefinition } - func specialize(function: Function, for substitutions: SubstitutionMap) -> Function? { - return _bridged.specializeFunction(function.bridged, substitutions.bridged).function + func specialize(function: Function, + for substitutions: SubstitutionMap, + convertIndirectToDirect: Bool, + isMandatory: Bool + ) -> Function? { + return bridgedPassContext.specializeFunction(function.bridged, substitutions.bridged, + convertIndirectToDirect, isMandatory).function } enum DeserializationMode { @@ -140,18 +146,18 @@ struct ModulePassContext : Context, CustomStringConvertible { } func deserializeAllCallees(of function: Function, mode: DeserializationMode) { - _bridged.deserializeAllCallees(function.bridged, mode == .allFunctions ? true : false) + bridgedPassContext.deserializeAllCallees(function.bridged, mode == .allFunctions ? true : false) } @discardableResult - func createWitnessTable(entries: [WitnessTable.Entry], + func createSpecializedWitnessTable(entries: [WitnessTable.Entry], conformance: Conformance, linkage: Linkage, serialized: Bool) -> WitnessTable { let bridgedEntries = entries.map { $0.bridged } let bridgedWitnessTable = bridgedEntries.withBridgedArrayRef { - _bridged.createWitnessTable(linkage.bridged, serialized, conformance.bridged, $0) + _bridged.createSpecializedWitnessTable(linkage.bridged, serialized, conformance.bridged, $0) } return WitnessTable(bridged: bridgedWitnessTable) } @@ -197,21 +203,21 @@ struct ModulePassContext : Context, CustomStringConvertible { } func mangleAsyncRemoved(from function: Function) -> String { - return String(taking: _bridged.mangleAsyncRemoved(function.bridged)) + return String(taking: bridgedPassContext.mangleAsyncRemoved(function.bridged)) } func mangle(withDeadArguments: [Int], from function: Function) -> String { - withDeadArguments.withUnsafeBufferPointer { bufPtr in - bufPtr.withMemoryRebound(to: Int.self) { valPtr in - String(taking: _bridged.mangleWithDeadArgs(valPtr.baseAddress, - withDeadArguments.count, - function.bridged)) - } + withDeadArguments.withBridgedArrayRef { bridgedArgIndices in + String(taking: bridgedPassContext.mangleWithDeadArgs(bridgedArgIndices, function.bridged)) } } + func erase(function: Function) { + bridgedPassContext.eraseFunction(function.bridged) + } + func notifyFunctionTablesChanged() { - _bridged.asNotificationHandler().notifyChanges(.functionTablesChanged) + _bridged.notifyChanges(.FunctionTables) } } diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/Options.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/Options.swift index 81bd6a999cdb3..2c4b9d031f3d2 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/Options.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/Options.swift @@ -36,8 +36,12 @@ struct Options { _bridged.enableAddressDependencies() } + var noAllocations: Bool { + _bridged.noAllocations() + } + var enableEmbeddedSwift: Bool { - _bridged.hasFeature(.Embedded) + hasFeature(.Embedded) } var enableMergeableTraps: Bool { diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift index 48c7482a59a56..e92268b384e49 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift @@ -17,16 +17,15 @@ import OptimizerBridging @_cdecl("initializeSwiftModules") public func initializeSwiftModules() { registerAST() - registerSILClasses() + registerSIL() registerSwiftAnalyses() - registerUtilities() registerSwiftPasses() registerOptimizerTests() } private func registerPass( _ pass: ModulePass, - _ runFn: @escaping (@convention(c) (BridgedPassContext) -> ())) { + _ runFn: @escaping (@convention(c) (BridgedContext) -> ())) { pass.name._withBridgedStringRef { nameStr in SILPassManager_registerModulePass(nameStr, runFn) } @@ -63,16 +62,20 @@ private func registerForSILCombine( private func registerSwiftPasses() { // Module passes + registerPass(mandatoryAllocBoxToStack, { mandatoryAllocBoxToStack.run($0) }) registerPass(mandatoryPerformanceOptimizations, { mandatoryPerformanceOptimizations.run($0) }) registerPass(diagnoseUnknownConstValues, { diagnoseUnknownConstValues.run($0)}) registerPass(readOnlyGlobalVariablesPass, { readOnlyGlobalVariablesPass.run($0) }) registerPass(stackProtection, { stackProtection.run($0) }) + registerPass(embeddedSwiftDiagnostics, { embeddedSwiftDiagnostics.run($0) }) // Function passes + registerPass(allocBoxToStack, { allocBoxToStack.run($0) }) registerPass(asyncDemotion, { asyncDemotion.run($0) }) registerPass(booleanLiteralFolding, { booleanLiteralFolding.run($0) }) registerPass(letPropertyLowering, { letPropertyLowering.run($0) }) registerPass(mergeCondFailsPass, { mergeCondFailsPass.run($0) }) + registerPass(constantCapturePropagation, { constantCapturePropagation.run($0) }) registerPass(computeEscapeEffects, { computeEscapeEffects.run($0) }) registerPass(computeSideEffects, { computeSideEffects.run($0) }) registerPass(diagnoseInfiniteRecursion, { diagnoseInfiniteRecursion.run($0) }) @@ -99,14 +102,20 @@ private func registerSwiftPasses() { registerPass(lifetimeDependenceInsertionPass, { lifetimeDependenceInsertionPass.run($0) }) registerPass(lifetimeDependenceScopeFixupPass, { lifetimeDependenceScopeFixupPass.run($0) }) registerPass(copyToBorrowOptimization, { copyToBorrowOptimization.run($0) }) - registerPass(generalClosureSpecialization, { generalClosureSpecialization.run($0) }) + registerPass(tempRValueElimination, { tempRValueElimination.run($0) }) + registerPass(mandatoryTempRValueElimination, { mandatoryTempRValueElimination.run($0) }) + registerPass(tempLValueElimination, { tempLValueElimination.run($0) }) + registerPass(closureSpecialization, { closureSpecialization.run($0) }) registerPass(autodiffClosureSpecialization, { autodiffClosureSpecialization.run($0) }) + registerPass(loopInvariantCodeMotionPass, { loopInvariantCodeMotionPass.run($0) }) // Instruction passes registerForSILCombine(BeginBorrowInst.self, { run(BeginBorrowInst.self, $0) }) registerForSILCombine(BeginCOWMutationInst.self, { run(BeginCOWMutationInst.self, $0) }) + registerForSILCombine(BuiltinInst.self, { run(BuiltinInst.self, $0) }) registerForSILCombine(FixLifetimeInst.self, { run(FixLifetimeInst.self, $0) }) registerForSILCombine(GlobalValueInst.self, { run(GlobalValueInst.self, $0) }) + registerForSILCombine(StructInst.self, { run(StructInst.self, $0) }) registerForSILCombine(StrongRetainInst.self, { run(StrongRetainInst.self, $0) }) registerForSILCombine(StrongReleaseInst.self, { run(StrongReleaseInst.self, $0) }) registerForSILCombine(RetainValueInst.self, { run(RetainValueInst.self, $0) }) @@ -114,18 +123,23 @@ private func registerSwiftPasses() { registerForSILCombine(LoadInst.self, { run(LoadInst.self, $0) }) registerForSILCombine(LoadBorrowInst.self, { run(LoadBorrowInst.self, $0) }) registerForSILCombine(CopyValueInst.self, { run(CopyValueInst.self, $0) }) + registerForSILCombine(CopyBlockInst.self, { run(CopyBlockInst.self, $0) }) registerForSILCombine(DestroyValueInst.self, { run(DestroyValueInst.self, $0) }) registerForSILCombine(DestructureStructInst.self, { run(DestructureStructInst.self, $0) }) registerForSILCombine(DestructureTupleInst.self, { run(DestructureTupleInst.self, $0) }) registerForSILCombine(TypeValueInst.self, { run(TypeValueInst.self, $0) }) registerForSILCombine(ClassifyBridgeObjectInst.self, { run(ClassifyBridgeObjectInst.self, $0) }) + registerForSILCombine(MarkDependenceInst.self, { run(MarkDependenceInst.self, $0) }) + registerForSILCombine(MarkDependenceAddrInst.self, { run(MarkDependenceAddrInst.self, $0) }) registerForSILCombine(PointerToAddressInst.self, { run(PointerToAddressInst.self, $0) }) registerForSILCombine(UncheckedEnumDataInst.self, { run(UncheckedEnumDataInst.self, $0) }) registerForSILCombine(WitnessMethodInst.self, { run(WitnessMethodInst.self, $0) }) + registerForSILCombine(UncheckedAddrCastInst.self, { run(UncheckedAddrCastInst.self, $0) }) registerForSILCombine(UnconditionalCheckedCastInst.self, { run(UnconditionalCheckedCastInst.self, $0) }) registerForSILCombine(AllocStackInst.self, { run(AllocStackInst.self, $0) }) registerForSILCombine(ApplyInst.self, { run(ApplyInst.self, $0) }) registerForSILCombine(TryApplyInst.self, { run(TryApplyInst.self, $0) }) + registerForSILCombine(EndCOWMutationAddrInst.self, { run(EndCOWMutationAddrInst.self, $0) }) // Test passes registerPass(aliasInfoDumper, { aliasInfoDumper.run($0) }) @@ -137,7 +151,6 @@ private func registerSwiftPasses() { registerPass(deadEndBlockDumper, { deadEndBlockDumper.run($0) }) registerPass(memBehaviorDumper, { memBehaviorDumper.run($0) }) registerPass(rangeDumper, { rangeDumper.run($0) }) - registerPass(runUnitTests, { runUnitTests.run($0) }) registerPass(testInstructionIteration, { testInstructionIteration.run($0) }) registerPass(updateBorrowedFromPass, { updateBorrowedFromPass.run($0) }) } @@ -146,8 +159,3 @@ private func registerSwiftAnalyses() { AliasAnalysis.register() CalleeAnalysis.register() } - -private func registerUtilities() { - registerVerifier() - registerPhiUpdater() -} diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/Passes.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/Passes.swift index 0e9841bdf55cf..87541053e962b 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/Passes.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/Passes.swift @@ -39,7 +39,7 @@ struct ModulePass { self.runFunction = runFunction } - func run(_ bridgedCtxt: BridgedPassContext) { + func run(_ bridgedCtxt: BridgedContext) { let context = ModulePassContext(_bridged: bridgedCtxt) runFunction(context) } diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/SimplifyContext.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/SimplifyContext.swift new file mode 100644 index 0000000000000..2c9b133f4d7e7 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/SimplifyContext.swift @@ -0,0 +1,43 @@ +//===--- SimplifyContext.swift --------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL +import OptimizerBridging + +struct SimplifyContext : MutatingContext { + let _bridged: BridgedContext + let notifyInstructionChanged: (Instruction) -> () + let preserveDebugInfo: Bool + + func constantFold(builtin: BuiltinInst) -> Value? { + bridgedPassContext.constantFoldBuiltin(builtin.bridged).value + } +} + +// Those Type APIs get information from IRGenModule, so they are formally not part of SIL and +// therefore need to be defined here, in the Optimizer module. +extension Type { + func getStaticSize(context: SimplifyContext) -> Int? { + let v = context.bridgedPassContext.getStaticSize(self.bridged) + return v == -1 ? nil : v + } + + func getStaticAlignment(context: SimplifyContext) -> Int? { + let v = context.bridgedPassContext.getStaticAlignment(self.bridged) + return v == -1 ? nil : v + } + + func getStaticStride(context: SimplifyContext) -> Int? { + let v = context.bridgedPassContext.getStaticStride(self.bridged) + return v == -1 ? nil : v + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt index 6596426f53102..2aed2909f8708 100644 --- a/SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt @@ -15,6 +15,6 @@ swift_compiler_sources(Optimizer MemBehaviorDumper.swift SILPrinter.swift RangeDumper.swift - RunUnitTests.swift + UpdateBorrowedFrom.swift TestInstructionIteration.swift ) diff --git a/SwiftCompilerSources/Sources/Optimizer/TestPasses/EscapeInfoDumper.swift b/SwiftCompilerSources/Sources/Optimizer/TestPasses/EscapeInfoDumper.swift index 873ba9bac330e..d13427a94c5c9 100644 --- a/SwiftCompilerSources/Sources/Optimizer/TestPasses/EscapeInfoDumper.swift +++ b/SwiftCompilerSources/Sources/Optimizer/TestPasses/EscapeInfoDumper.swift @@ -109,7 +109,7 @@ let addressEscapeInfoDumper = FunctionPass(name: "dump-addr-escape-info") { for value in valuesToCheck { print("value:\(value)") for apply in applies { - if value.allContainedAddresss.isEscaping(using: Visitor(apply: apply), context) { + if value.allContainedAddresses.isEscaping(using: Visitor(apply: apply), context) { print(" ==> \(apply)") } else { print(" - \(apply)") @@ -127,8 +127,8 @@ let addressEscapeInfoDumper = FunctionPass(name: "dump-addr-escape-info") { print(lhs) print(rhs) - let projLhs = lhs.allContainedAddresss - let projRhs = rhs.allContainedAddresss + let projLhs = lhs.allContainedAddresses + let projRhs = rhs.allContainedAddresses let mayAlias = projLhs.canAddressAlias(with: projRhs, context) if mayAlias != projRhs.canAddressAlias(with: projLhs, context) { fatalError("canAddressAlias(with:) must be symmetric") diff --git a/SwiftCompilerSources/Sources/Optimizer/TestPasses/MemBehaviorDumper.swift b/SwiftCompilerSources/Sources/Optimizer/TestPasses/MemBehaviorDumper.swift index d7b352454d9fa..7def70e95934d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/TestPasses/MemBehaviorDumper.swift +++ b/SwiftCompilerSources/Sources/Optimizer/TestPasses/MemBehaviorDumper.swift @@ -63,6 +63,7 @@ private extension Instruction { is BeginAccessInst, is EndAccessInst, is EndCOWMutationInst, + is EndCOWMutationAddrInst, is CopyValueInst, is DestroyValueInst, is StrongReleaseInst, diff --git a/SwiftCompilerSources/Sources/Optimizer/TestPasses/RunUnitTests.swift b/SwiftCompilerSources/Sources/Optimizer/TestPasses/RunUnitTests.swift deleted file mode 100644 index 9e995aba5a300..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/TestPasses/RunUnitTests.swift +++ /dev/null @@ -1,24 +0,0 @@ -//===--- UnitTests.swift - A pseudo pass for running the unit tests -------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2021 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 SIL - -/// This pass should only be used by sil-opt to run all the unit tests. -/// -let runUnitTests = ModulePass(name: "run-unit-tests") { - (context: ModulePassContext) in - - print("--- Run unit tests ---") - - print("test ProjectionPath") - SmallProjectionPath.runUnitTests() -} diff --git a/SwiftCompilerSources/Sources/Optimizer/TestPasses/UpdateBorrowedFrom.swift b/SwiftCompilerSources/Sources/Optimizer/TestPasses/UpdateBorrowedFrom.swift new file mode 100644 index 0000000000000..e6fd49f408fca --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/TestPasses/UpdateBorrowedFrom.swift @@ -0,0 +1,22 @@ +//===--- PhiUpdater.swift -------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SIL + +/// This pass is only used for testing. +/// In the regular pipeline it's not needed because optimization passes must make sure that borrowed-from +/// instructions are updated once the pass finishes. +let updateBorrowedFromPass = FunctionPass(name: "update-borrowed-from") { + (function: Function, context: FunctionPassContext) in + + updateBorrowedFrom(in: function, context) +} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/AccessUtilsTest.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/AccessUtilsTest.swift deleted file mode 100644 index 99014c26243ee..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/AccessUtilsTest.swift +++ /dev/null @@ -1,23 +0,0 @@ -//===--- AccessUtils.swift - Utilities for analyzing memory accesses ------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2024 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 -// -//===----------------------------------------------------------------------===// -// -// TODO: Move this to AccessUtils.swift when FunctionTest is available. -// -//===----------------------------------------------------------------------===// - -let getAccessBaseTest = FunctionTest("swift_get_access_base") { - function, arguments, context in - let address = arguments.takeValue() - print("Address: \(address)") - let base = address.accessBase - print("Base: \(base)") -} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift index 40bd631f5d0b2..3f1b5b50cea8b 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift @@ -125,13 +125,13 @@ extension AddressUseVisitor { case is SwitchEnumAddrInst, is CheckedCastAddrBranchInst, is SelectEnumAddrInst, is InjectEnumAddrInst, is StoreInst, is StoreUnownedInst, is StoreWeakInst, - is AssignInst, is AssignByWrapperInst, is AssignOrInitInst, + is AssignInst, is AssignOrInitInst, is TupleAddrConstructorInst, is InitBlockStorageHeaderInst, is RetainValueAddrInst, is ReleaseValueAddrInst, is DestroyAddrInst, is DeallocStackInst, is DeinitExistentialAddrInst, is IsUniqueInst, is MarkFunctionEscapeInst, - is PackElementSetInst: + is PackElementSetInst, is EndCOWMutationAddrInst: return leafAddressUse(of: operand) case is LoadInst, is LoadUnownedInst, is LoadWeakInst, is ValueMetatypeInst, is ExistentialMetatypeInst, @@ -159,7 +159,7 @@ extension AddressUseVisitor { .GenericFDiv, .GenericMul, .GenericFMul, .GenericSDiv, .GenericExactSDiv, .GenericShl, .GenericSRem, .GenericSub, .GenericFSub, .GenericUDiv, .GenericExactUDiv, .GenericURem, - .GenericFRem, .GenericXor, .TaskRunInline, .ZeroInitializer, + .GenericFRem, .GenericXor, .TaskRunInline, .ZeroInitializer, .PrepareInitialization, .GetEnumTag, .InjectEnumTag: return leafAddressUse(of: operand) default: @@ -501,6 +501,11 @@ enum AddressOwnershipLiveRange : CustomStringConvertible { } } + /// Return the live range of the addressable value that reaches 'begin', not including 'begin', which may itself be an + /// access of the address. + /// + /// The range ends at the destroy or reassignment of the addressable value. + /// /// Return nil if the live range is unknown. static func compute(for address: Value, at begin: Instruction, _ localReachabilityCache: LocalVariableReachabilityCache, @@ -624,7 +629,7 @@ extension AddressOwnershipLiveRange { var reachableUses = Stack(context) defer { reachableUses.deinitialize() } - localReachability.gatherKnownReachableUses(from: assignment, in: &reachableUses) + localReachability.gatherKnownLifetimeUses(from: assignment, in: &reachableUses) let assignmentInst = assignment.instruction ?? allocation.parentFunction.entryBlock.instructions.first! var range = InstructionRange(begin: assignmentInst, context) @@ -643,12 +648,10 @@ extension AddressOwnershipLiveRange { let addressOwnershipLiveRangeTest = FunctionTest("address_ownership_live_range") { function, arguments, context in let address = arguments.takeValue() + let begin = arguments.takeInstruction() print("Address: \(address)") print("Base: \(address.accessBase)") - let begin = address.definingInstructionOrTerminator ?? { - assert(address is FunctionArgument) - return function.instructions.first! - }() + print("Begin: \(begin)") let localReachabilityCache = LocalVariableReachabilityCache() guard var ownershipRange = AddressOwnershipLiveRange.compute(for: address, at: begin, localReachabilityCache, context) else { diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt index bac67d8c12f3a..35ca4d97aae75 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt @@ -7,23 +7,14 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors swift_compiler_sources(Optimizer - AccessUtilsTest.swift AddressUtils.swift - BorrowUtils.swift - SpecializationCloner.swift - DiagnosticEngine.swift Devirtualization.swift EscapeUtils.swift - ForwardingUtils.swift FunctionSignatureTransforms.swift + FunctionTest.swift GenericSpecialization.swift LifetimeDependenceUtils.swift LocalVariableUtils.swift OptUtils.swift OwnershipLiveness.swift - PhiUpdater.swift - SSAUpdater.swift - StaticInitCloner.swift - Test.swift - Verifier.swift ) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/Devirtualization.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/Devirtualization.swift index 6d05399758d53..1eae780d8252d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/Devirtualization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/Devirtualization.swift @@ -28,6 +28,15 @@ func devirtualizeDeinits(of destroy: DestroyAddrInst, _ context: some MutatingCo return devirtualize(destroy: destroy, context) } +func devirtualizeDeinits(of builtin: BuiltinInst, _ context: some MutatingContext) -> Bool { + switch builtin.id { + case .DestroyArray: + return devirtualize(builtinDestroyArray: builtin, context) + default: + return true + } +} + private func devirtualize(destroy: some DevirtualizableDestroy, _ context: some MutatingContext) -> Bool { let type = destroy.type if !type.isMoveOnly { @@ -254,6 +263,59 @@ extension DestroyAddrInst : DevirtualizableDestroy { } } +private func devirtualize(builtinDestroyArray: BuiltinInst, _ context: some MutatingContext) -> Bool { + let function = builtinDestroyArray.parentFunction + let elementType = builtinDestroyArray.substitutionMap.replacementType.loweredType(in: function) + guard elementType.isMoveOnly, + // This avoids lowering the loop if the element is a non-copyable generic type. + (elementType.isStruct || elementType.isEnum) + else { + return true + } + + // Lower the `builtin "destroyArray" to a loop which destroys all elements + + let basePointer = builtinDestroyArray.arguments[1] + let arrayCount = builtinDestroyArray.arguments[2] + let indexType = arrayCount.type + let boolType = context.getBuiltinIntegerType(bitWidth: 1) + + let preheaderBlock = builtinDestroyArray.parentBlock + let exitBlock = context.splitBlock(after: builtinDestroyArray) + let headerBlock = context.createBlock(after: preheaderBlock) + let bodyBlock = context.createBlock(after: headerBlock) + + let preheaderBuilder = Builder(atEndOf: preheaderBlock, location: builtinDestroyArray.location, context) + let zero = preheaderBuilder.createIntegerLiteral(0, type: indexType) + let one = preheaderBuilder.createIntegerLiteral(1, type: indexType) + let falseValue = preheaderBuilder.createBoolLiteral(false); + let baseAddress = preheaderBuilder.createPointerToAddress(pointer: basePointer, + addressType: elementType.addressType, + isStrict: true, isInvariant: false) + preheaderBuilder.createBranch(to: headerBlock, arguments: [zero]) + + let inductionVariable = headerBlock.addArgument(type: indexType, ownership: .none, context) + let headerBuilder = Builder(atEndOf: headerBlock, location: builtinDestroyArray.location, context) + let cmp = headerBuilder.createBuiltinBinaryFunction(name: "cmp_slt", operandType: indexType, resultType: boolType, + arguments: [inductionVariable, arrayCount]) + headerBuilder.createCondBranch(condition: cmp, trueBlock: bodyBlock, falseBlock: exitBlock) + + let bodyBuilder = Builder(atEndOf: bodyBlock, location: builtinDestroyArray.location, context) + let elementAddr = bodyBuilder.createIndexAddr(base: baseAddress, index: inductionVariable, + needStackProtection: false) + let destroy = bodyBuilder.createDestroyAddr(address: elementAddr) + let resultType = context.getTupleType(elements: [indexType, boolType]).loweredType(in: function) + let increment = bodyBuilder.createBuiltinBinaryFunction(name: "sadd_with_overflow", operandType: indexType, + resultType: resultType, + arguments: [inductionVariable, one, falseValue]) + let incrResult = bodyBuilder.createTupleExtract(tuple: increment, elementIndex: 0) + bodyBuilder.createBranch(to: headerBlock, arguments: [incrResult]) + + context.erase(instruction: builtinDestroyArray) + + return devirtualize(destroy: destroy, context) +} + private extension EnumCases { func allPayloadsAreTrivial(in function: Function) -> Bool { allSatisfy({ $0.payload?.isTrivial(in: function) ?? true }) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/DiagnosticEngine.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/DiagnosticEngine.swift deleted file mode 100644 index 2d31c4808aa39..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/DiagnosticEngine.swift +++ /dev/null @@ -1,146 +0,0 @@ -//===--- DiagnosticEngine.swift -------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022 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 ASTBridging - -import Basic -import SIL - -typealias DiagID = BridgedDiagID - -protocol DiagnosticArgument { - func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) -} -extension String: DiagnosticArgument { - func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { - _withBridgedStringRef { fn(BridgedDiagnosticArgument($0)) } - } -} -extension StringRef: DiagnosticArgument { - func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { - fn(BridgedDiagnosticArgument(_bridged)) - } -} -extension Int: DiagnosticArgument { - func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { - fn(BridgedDiagnosticArgument(self)) - } -} -extension Type: DiagnosticArgument { - func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { - fn(bridged.asDiagnosticArgument()) - } -} -extension DeclRef: DiagnosticArgument { - func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { - fn(bridged.asDiagnosticArgument()) - } -} - -struct DiagnosticFixIt { - let start: SourceLoc - let byteLength: Int - let text: String - - init(start: SourceLoc, byteLength: Int, replacement text: String) { - self.start = start - self.byteLength = byteLength - self.text = text - } - - func withBridgedDiagnosticFixIt(_ fn: (BridgedDiagnosticFixIt) -> Void) { - text._withBridgedStringRef { bridgedTextRef in - let bridgedDiagnosticFixIt = BridgedDiagnosticFixIt( - start.bridged, UInt32(byteLength), - bridgedTextRef) - fn(bridgedDiagnosticFixIt) - } - } -} - -struct DiagnosticEngine { - private let bridged: BridgedDiagnosticEngine - - init(bridged: BridgedDiagnosticEngine) { - self.bridged = bridged - } - init?(bridged: BridgedNullableDiagnosticEngine) { - guard let raw = bridged.raw else { - return nil - } - self.bridged = BridgedDiagnosticEngine(raw: raw) - } - - func diagnose(_ position: SourceLoc?, - _ id: DiagID, - _ args: [DiagnosticArgument], - highlight: CharSourceRange? = nil, - fixIts: [DiagnosticFixIt] = []) { - - let bridgedSourceLoc: BridgedSourceLoc = position.bridged - let highlightStart: BridgedSourceLoc - let highlightLength: UInt32 - if let highlight = highlight { - highlightStart = highlight.start.bridged - highlightLength = highlight.byteLength - } else { - highlightStart = BridgedSourceLoc() - highlightLength = 0 - } - var bridgedArgs: [BridgedDiagnosticArgument] = [] - var bridgedFixIts: [BridgedDiagnosticFixIt] = [] - - // Build a higher-order function to wrap every 'withBridgedXXX { ... }' - // calls, so we don't escape anything from the closure. 'bridgedArgs' and - // 'bridgedFixIts' are temporary storage to store bridged values. So they - // should not be used after the closure is executed. - - var closure: () -> Void = { - bridgedArgs.withBridgedArrayRef { bridgedArgsRef in - bridgedFixIts.withBridgedArrayRef { bridgedFixItsRef in - bridged.diagnose(at: bridgedSourceLoc, id, bridgedArgsRef, - highlightAt: highlightStart, - highlightLength: highlightLength, - fixIts: bridgedFixItsRef) - } - } - } - // 'reversed()' because the closure should be wrapped in that order. - for arg in args.reversed() { - closure = { [closure, arg] in - arg._withBridgedDiagnosticArgument { bridgedArg in - bridgedArgs.append(bridgedArg) - closure() - } - } - } - // 'reversed()' because the closure should be wrapped in that order. - for fixIt in fixIts.reversed() { - closure = { [closure, fixIt] in - fixIt.withBridgedDiagnosticFixIt { bridgedFixIt in - bridgedFixIts.append(bridgedFixIt) - closure() - } - } - } - - closure() - } - - func diagnose(_ position: SourceLoc?, - _ id: DiagID, - _ args: DiagnosticArgument..., - highlight: CharSourceRange? = nil, - fixIts: DiagnosticFixIt...) { - diagnose(position, id, args, highlight: highlight, fixIts: fixIts) - } -} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift index 125d26fca4ab9..278c26c12cd4b 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift @@ -519,7 +519,7 @@ fileprivate struct EscapeWalker : ValueDefUseWalker, if operand == copyAddr.sourceOperand { return walkUp(address: copyAddr.destination, path: path) } else { - if !copyAddr.isInitializationOfDest { + if !copyAddr.isInitializationOfDestination { if handleDestroy(of: operand.value, path: path.with(knownType: nil)) == .abortWalk { return .abortWalk } @@ -640,12 +640,14 @@ fileprivate struct EscapeWalker : ValueDefUseWalker, } // Indirect arguments cannot escape the function, but loaded values from such can. - if !followLoads(at: path) { - guard let beginApply = apply as? BeginApplyInst else { - return .continueWalk - } - // Except for begin_apply: it can yield an address value. - if !indirectResultEscapes(of: beginApply, path: path) { + if argOp.value.type.isAddress && !followLoads(at: path) { + if let beginApply = apply as? BeginApplyInst { + // begin_apply can yield an address value. + if !indirectResultEscapes(of: beginApply, path: path) { + return .continueWalk + } + } else if !apply.isAddressable(operand: argOp) { + // The result does not depend on the argument's address. return .continueWalk } } diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/FunctionSignatureTransforms.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/FunctionSignatureTransforms.swift index 5b8575a198a07..310dec2c9f9c9 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/FunctionSignatureTransforms.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/FunctionSignatureTransforms.swift @@ -12,6 +12,29 @@ import SIL +private extension ArgumentConventions { + func isLifetimeSourceOrTarget(index argIndex: Int) -> Bool { + // Check if `argIndex` is a lifetime target + if self[parameterDependencies: argIndex] != nil { + return true + } + + // Check if `argIndex` is a lifetime source in resultDependencies + if self[resultDependsOn: argIndex] != nil { + return true + } + + // Check if `argIndex` is a lifetime source in parameterDependencies + for targetIndex in firstParameterIndex..= deadArgIndices.first! { + if callee.argumentConventions.isLifetimeSourceOrTarget(index: argIndex) { + return + } + } + } + let specializedFuncName = context.mangle(withDeadArguments: deadArgIndices, from: callee) let specializedCallee: Function @@ -88,7 +120,7 @@ private func createSpecializedFunction( hasSelfParameter: hasSelfParameter, fromOriginal: originalFunction) - let thunkLoc = originalFunction.entryBlock.instructions.first!.location.autoGenerated + let thunkLoc = originalFunction.entryBlock.instructions.first!.location.asAutoGenerated context.moveFunctionBody(from: originalFunction, to: specializedFunction) // originalFunction is now empty and used as the thunk. diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/FunctionTest.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/FunctionTest.swift new file mode 100644 index 0000000000000..701fe2819e86d --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/FunctionTest.swift @@ -0,0 +1,102 @@ +//===----------- FunctionTest.swift ---------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Optimizer test infrastructure. +// +// For general documentation on how to write tests see `Test.swift` in the +// SIL module. +// To add an optimizer test, use `FunctionTest` instead of `Test` and register +// it in `registerOptimizerTests`. +// +//===----------------------------------------------------------------------===// + +import SIL +import OptimizerBridging + +/// Like `SIL.Test`, but provides a `FunctionPass` to the invocation closure. +struct FunctionTest { + let name: String + let invocation: FunctionTestInvocation + + public init(_ name: String, invocation: @escaping FunctionTestInvocation) { + self.name = name + self.invocation = invocation + } +} + +/// The type of the closure passed to a FunctionTest. +typealias FunctionTestInvocation = @convention(thin) (Function, TestArguments, FunctionPassContext) -> () + +public func registerOptimizerTests() { + SIL.registerTests() + + // Register each test. + registerFunctionTests( + addressOwnershipLiveRangeTest, + argumentConventionsTest, + interiorLivenessTest, + lifetimeDependenceRootTest, + lifetimeDependenceScopeTest, + lifetimeDependenceUseTest, + linearLivenessTest, + localVariableReachableUsesTest, + localVariableReachingAssignmentsTest, + rangeOverlapsPathTest, + variableIntroducerTest + ) + + // Finally register the thunk they all call through. + registerFunctionTestThunk(functionTestThunk) +} + +private func registerFunctionTests(_ tests: FunctionTest...) { + tests.forEach { registerFunctionTest($0) } +} + +private func registerFunctionTest(_ test: FunctionTest) { + test.name._withBridgedStringRef { ref in + registerFunctionTest(ref, castToOpaquePointer(fromInvocation: test.invocation)) + } +} + +/// The function called by the swift::test::FunctionTest which invokes the +/// actual test function. +/// +/// This function is necessary because tests need to be written in terms of +/// native Swift types (Function, TestArguments, FunctionPassContext) +/// rather than their bridged variants, but such a function isn't representable +/// in C++. This thunk unwraps the bridged types and invokes the real function. +private func functionTestThunk( + _ erasedInvocation: UnsafeMutableRawPointer, + _ function: BridgedFunction, + _ arguments: BridgedTestArguments, + _ bridgedContext: BridgedContext) { + let invocation = castToInvocation(fromOpaquePointer: erasedInvocation) + let context = FunctionPassContext(_bridged: bridgedContext) + invocation(function.function, arguments.native, context) +} + +/// Bitcast a thin test closure to void *. +/// +/// Needed so that the closure can be represented in C++ for storage in the test +/// registry. +private func castToOpaquePointer(fromInvocation invocation: FunctionTestInvocation) -> UnsafeMutableRawPointer { + return unsafeBitCast(invocation, to: UnsafeMutableRawPointer.self) +} + +/// Bitcast a void * to a thin test closure. +/// +/// Needed so that the closure stored in the C++ test registry can be invoked +/// via the functionTestThunk. +private func castToInvocation(fromOpaquePointer erasedInvocation: UnsafeMutableRawPointer) -> FunctionTestInvocation { + return unsafeBitCast(erasedInvocation, to: FunctionTestInvocation.self) +} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift index e5f8dae6214a8..ef8dbc679afc8 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift @@ -50,7 +50,7 @@ private struct VTableSpecializer { let classDecl = classType.nominal! as! ClassDecl guard let origVTable = context.lookupVTable(for: classDecl) else { if context.enableWMORequiredDiagnostics { - context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_class, classType) + context.diagnosticEngine.diagnose(.cannot_specialize_class, classType, at: errorLocation) } return } @@ -79,7 +79,7 @@ private struct VTableSpecializer { } private func specializeEntries(of vTable: VTable, _ notifyNewFunction: (Function) -> ()) -> [VTable.Entry] { - return vTable.entries.compactMap { entry in + return vTable.entries.map { entry in if !entry.implementation.isGeneric { return entry } @@ -89,10 +89,10 @@ private struct VTableSpecializer { guard !methodSubs.conformances.contains(where: {!$0.isValid}), context.loadFunction(function: entry.implementation, loadCalleesRecursively: true), - let specializedMethod = context.specialize(function: entry.implementation, for: methodSubs) else - { - context.diagnosticEngine.diagnose(entry.methodDecl.location.sourceLoc, .non_final_generic_class_function) - return nil + let specializedMethod = context.specialize(function: entry.implementation, for: methodSubs, + convertIndirectToDirect: true, isMandatory: true) + else { + return entry } notifyNewFunction(specializedMethod) @@ -106,16 +106,32 @@ private struct VTableSpecializer { } } -func specializeWitnessTable(forConformance conformance: Conformance, - errorLocation: Location, - _ context: ModulePassContext, - _ notifyNewWitnessTable: (WitnessTable) -> ()) -{ - let genericConformance = conformance.genericConformance - guard let witnessTable = context.lookupWitnessTable(for: genericConformance) else { +/// Specializes a witness table of `conformance` for the concrete type of the conformance. +func specializeWitnessTable(for conformance: Conformance, _ context: ModulePassContext) { + if let existingSpecialization = context.lookupWitnessTable(for: conformance), + existingSpecialization.isSpecialized + { + return + } + + guard conformance.isConcrete else { + // If the conformance is abstract the witness table is specialized elsewhere - at the + // place where the concrete conformance is referenced. + return + } + + let baseConf = conformance.isInherited ? conformance.inheritedConformance: conformance + if !baseConf.isSpecialized { + var visited = Set() + specializeDefaultMethods(for: conformance, visited: &visited, context) + return + } + + guard let witnessTable = context.lookupWitnessTable(for: baseConf.genericConformance) else { fatalError("no witness table found") } assert(witnessTable.isDefinition, "No witness table available") + let substitutions = baseConf.specializedSubstitutions let newEntries = witnessTable.entries.map { origEntry in switch origEntry { @@ -125,13 +141,15 @@ func specializeWitnessTable(forConformance conformance: Conformance, guard let origMethod = witness else { return origEntry } - let methodSubs = conformance.specializedSubstitutions.getMethodSubstitutions(for: origMethod) + let methodSubs = substitutions.getMethodSubstitutions(for: origMethod, + // Generic self types need to be handled specially (see `getMethodSubstitutions`) + selfType: origMethod.hasGenericSelf(context) ? conformance.type.canonical : nil) guard !methodSubs.conformances.contains(where: {!$0.isValid}), context.loadFunction(function: origMethod, loadCalleesRecursively: true), - let specializedMethod = context.specialize(function: origMethod, for: methodSubs) else - { - context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_witness_method, requirement) + let specializedMethod = context.specialize(function: origMethod, for: methodSubs, + convertIndirectToDirect: true, isMandatory: true) + else { return origEntry } return .method(requirement: requirement, witness: specializedMethod) @@ -139,25 +157,113 @@ func specializeWitnessTable(forConformance conformance: Conformance, let baseConf = context.getSpecializedConformance(of: witness, for: conformance.type, substitutions: conformance.specializedSubstitutions) - specializeWitnessTable(forConformance: baseConf, errorLocation: errorLocation, context, notifyNewWitnessTable) + specializeWitnessTable(for: baseConf, context) return .baseProtocol(requirement: requirement, witness: baseConf) case .associatedType(let requirement, let witness): let substType = witness.subst(with: conformance.specializedSubstitutions) return .associatedType(requirement: requirement, witness: substType) - case .associatedConformance(let requirement, let substType, let assocConf): - // FIXME: let concreteAssociateConf = assocConf.subst(with: conformance.specializedSubstitutions) - let concreteAssociateConf = conformance.getAssociatedConformance(ofAssociatedType: requirement.rawType, to: assocConf.proto) + case .associatedConformance(let requirement, let assocConf): + // TODO: once we have the API, replace this with: + // let concreteAssociateConf = assocConf.subst(with: conformance.specializedSubstitutions) + let concreteAssociateConf = conformance.getAssociatedConformance(ofAssociatedType: requirement.rawType, + to: assocConf.protocol) if concreteAssociateConf.isSpecialized { - specializeWitnessTable(forConformance: concreteAssociateConf, - errorLocation: errorLocation, - context, notifyNewWitnessTable) + specializeWitnessTable(for: concreteAssociateConf, context) } return .associatedConformance(requirement: requirement, - substType: substType.subst(with: conformance.specializedSubstitutions), witness: concreteAssociateConf) } } - let newWT = context.createWitnessTable(entries: newEntries,conformance: conformance, - linkage: .shared, serialized: false) - notifyNewWitnessTable(newWT) + context.createSpecializedWitnessTable(entries: newEntries,conformance: conformance, + linkage: .shared, serialized: false) +} + +/// Specializes the default methods of a non-generic witness table. +/// Default implementations (in protocol extensions) of non-generic protocol methods have a generic +/// self argument. Specialize such methods with the concrete type. Note that it is important to also +/// specialize inherited conformances so that the concrete self type is correct, even for derived classes. +private func specializeDefaultMethods(for conformance: Conformance, + visited: inout Set, + _ context: ModulePassContext) +{ + // Avoid infinite recursion, which may happen if an associated conformance is the conformance itself. + guard visited.insert(conformance).inserted, + let witnessTable = context.lookupWitnessTable(for: conformance.rootConformance) + else { + return + } + + assert(witnessTable.isDefinition, "No witness table available") + + var specialized = false + + let newEntries = witnessTable.entries.map { origEntry in + switch origEntry { + case .invalid: + return WitnessTable.Entry.invalid + case .method(let requirement, let witness): + guard let origMethod = witness, + // Is it a generic method where only self is generic (= a default witness method)? + origMethod.isGeneric, origMethod.isNonGenericWitnessMethod(context) + else { + return origEntry + } + // Replace the generic self type with the concrete type. + let methodSubs = SubstitutionMap(genericSignature: origMethod.genericSignature, + replacementTypes: [conformance.type]) + + guard !methodSubs.conformances.contains(where: {!$0.isValid}), + context.loadFunction(function: origMethod, loadCalleesRecursively: true), + let specializedMethod = context.specialize(function: origMethod, for: methodSubs, + convertIndirectToDirect: true, isMandatory: true) + else { + return origEntry + } + specialized = true + return .method(requirement: requirement, witness: specializedMethod) + case .baseProtocol(_, let witness): + specializeDefaultMethods(for: witness, visited: &visited, context) + return origEntry + case .associatedType: + return origEntry + case .associatedConformance(_, let assocConf): + specializeDefaultMethods(for: assocConf, visited: &visited, context) + return origEntry + } + } + // If the witness table does not contain any default methods, there is no need to create a + // specialized witness table. + if specialized { + context.createSpecializedWitnessTable(entries: newEntries,conformance: conformance, + linkage: .shared, serialized: false) + } +} + +private extension Function { + // True, if this is a non-generic method which might have a generic self argument. + // Default implementations (in protocol extensions) of non-generic protocol methods have a generic + // self argument. + func isNonGenericWitnessMethod(_ context: some Context) -> Bool { + switch loweredFunctionType.invocationGenericSignatureOfFunction.genericParameters.count { + case 0: + return true + case 1: + return hasGenericSelf(context) + default: + return false + } + } + + // True, if the self argument is a generic parameter. + func hasGenericSelf(_ context: some Context) -> Bool { + let convention = FunctionConvention(for: loweredFunctionType, + hasLoweredAddresses: context.moduleHasLoweredAddresses) + if convention.hasSelfParameter, + let selfParam = convention.parameters.last, + selfParam.type.isGenericTypeParameter + { + return true + } + return false + } } diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift index d992481da692b..21de12feaf86d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift @@ -161,20 +161,16 @@ extension LifetimeDependence { // Construct a LifetimeDependence from a return value. This only // constructs a dependence for ~Escapable results that do not have a - // lifetime dependence (@_unsafeNonescapableResult). + // lifetime dependence (@lifetime(immortal), @_unsafeNonescapableResult). // // This is necessary because inserting a mark_dependence placeholder for such an unsafe dependence would illegally // have the same base and value operand. - // - // TODO: handle indirect results - init?(unsafeApplyResult value: Value, _ context: some Context) { + init?(unsafeApplyResult value: Value, apply: FullApplySite, _ context: some Context) { if value.isEscapable { return nil } - if (value.definingInstructionOrTerminator as! FullApplySite).hasResultDependence { - return nil - } - assert(value.ownership == .owned, "unsafe apply result must be owned") + assert(!apply.hasResultDependence, "mark_dependence should be used instead") + assert(value.ownership == .owned || value.type.isAddress, "unsafe apply result must be owned") self.scope = Scope(base: value, context) self.dependentValue = value self.markDepInst = nil @@ -226,9 +222,9 @@ extension LifetimeDependence { return scope.computeRange(context) } - func resolve(_ context: some Context) { + func resolve(_ context: some MutatingContext) { if let mdi = markDepInst { - mdi.resolveToNonEscaping() + mdi.resolveToNonEscaping(context) } } } @@ -279,8 +275,18 @@ extension LifetimeDependence.Scope { case let .box(projectBox): // Note: the box may be in a borrow scope. self.init(base: projectBox.operand.value, context) - case .stack, .class, .tail, .pointer, .index, .unidentified: + case .class, .tail, .pointer, .index: self = .unknown(accessBase.address!) + case .unidentified: + self = .unknown(address) + case let .stack(allocStack): + if let initializer = accessBase.findSingleInitializer(context) { + if case let .store(store, _) = initializer { + self = .initialized(.store(initializingStore: store, initialAddress: allocStack)) + return + } + } + self = .unknown(allocStack) case .global: // TODO: When AccessBase directly stores GlobalAccessBase, we don't need a check here and don't need to pass // 'address' in to this function. @@ -375,8 +381,30 @@ extension LifetimeDependence.Scope { } } +// Scope helpers. extension LifetimeDependence.Scope { - /// Ignore "irrelevent" borrow scopes: load_borrow or begin_borrow without [var_decl] + // If the LifetimeDependenceScope is .initialized, then return the alloc_stack. + var allocStackInstruction: AllocStackInst? { + switch self { + case let .initialized(initializer): + switch initializer { + case let .store(initializingStore: store, initialAddress): + if let allocStackInst = initialAddress as? AllocStackInst { + return allocStackInst + } + if let sb = store as? StoreBorrowInst { + return sb.allocStack + } + return nil + default: + return nil + } + default: + return nil + } + } + + /// Ignore "irrelevant" borrow scopes: load_borrow or begin_borrow without [var_decl] func ignoreBorrowScope(_ context: some Context) -> LifetimeDependence.Scope? { guard case let .borrowed(beginBorrowVal) = self else { return nil @@ -400,6 +428,21 @@ extension LifetimeDependence.Scope { /// /// Returns nil if the dependence scope covers the entire function. Returns an empty range for an unknown scope. /// + /// When this Scope is live on unreachable paths, the returned range may include blocks that are not dominated by the + /// scope introducer. Even though 'range.isValid == false' for such a range, it is still valid for checking that + /// dependencies are in scope since we already know that the Scope introducer dominates all dependent uses. + /// + /// Ignore the lifetime of temporary trivial values (with .initialized and .unknown scopes). Temporaries have an + /// unknown Scope, which means that LifetimeDependence.Scope did not recognize a VariableScopeInstruction. This is + /// important to promote mark_dependence instructions emitted by SILGen to [nonescaping] (e.g. unsafeAddressor). It + /// also allows trivial value to be "extended" without actually tracking their scope, which is expected behavior. For + /// example: + /// + /// let span = Span(buffer.baseAddress) + /// + /// If 'baseAddress' is an opaque getter, then the temporary pointer is effectively valid + /// until the end of the function. + /// /// Note: The caller must deinitialize the returned range. func computeRange(_ context: Context) -> InstructionRange? { switch self { @@ -437,18 +480,13 @@ extension LifetimeDependence.Scope { if case let .argument(arg) = initializer, arg.convention.isInout { return nil } + let address = initializer.initialAddress + if address.type.objectType.isTrivial(in: address.parentFunction) { + return LifetimeDependence.Scope.computeAddressableRange(initializer: initializer, context) + } return LifetimeDependence.Scope.computeInitializedRange(initializer: initializer, context) case let .unknown(value): - // Ignore the lifetime of temporary trivial values. Temporaries have an unknown Scope, which means that - // LifetimeDependence.Scope did not recognize a VariableScopeInstruction. This is important to promote - // mark_dependence instructions emitted by SILGen to [nonescaping] (e.g. unsafeAddressor). It also allows trivial - // value to be "extended" without actually tracking their scope, which is expected behavior. For example: - // - // let span = Span(buffer.baseAddress) - // - // If 'baseAddress' is an opaque getter, then the temporary pointer is effectively valid - // until the end of the function. - if value.type.isTrivial(in: value.parentFunction) { + if value.type.objectType.isTrivial(in: value.parentFunction) { return nil } // Return an empty range. @@ -479,7 +517,7 @@ extension LifetimeDependence.Scope { case is DestroyAddrInst: range.insert(inst) case let sdai as SourceDestAddrInstruction - where sdai.sourceOperand == use && sdai.isTakeOfSrc: + where sdai.sourceOperand == use && sdai.isTakeOfSource: range.insert(inst) case let li as LoadInst where li.loadOwnership == .take: range.insert(inst) @@ -491,6 +529,68 @@ extension LifetimeDependence.Scope { } return range } + + // For trivial values, compute the range in which the value may have its address taken. + // Returns nil unless the address has both an addressable parameter use and a dealloc_stack use. + // + // This extended range is convervative. In theory, it can lead to a "false positive" diagnostic if this scope was + // computed for a apply site that takes this address as *non-addressable*, as opposed to the apply site discovered + // here that takes the alloc_stack as an *addressable* argument. This won't normally happen because addressability + // depends on the value's type (via @_addressableForDepenencies), so all other lifetime-dependent applies should be + // addressable. Furthermore, SILGen only creates temporary stack locations for addressable arguments for a single + // argument. + private static func computeAddressableRange(initializer: AccessBase.Initializer, _ context: Context) + -> InstructionRange? { + switch initializer { + case .argument, .yield: + return nil + case let .store(initializingStore, initialAddr): + guard initialAddr is AllocStackInst else { + return nil + } + var isAddressable = false + var deallocInsts = SingleInlineArray() + for use in initialAddr.uses { + let inst = use.instruction + switch inst { + case let apply as ApplySite: + isAddressable = isAddressable || apply.isAddressable(operand: use) + case let dealloc as DeallocStackInst: + deallocInsts.append(dealloc) + default: + break + } + } + guard isAddressable, !deallocInsts.isEmpty else { + // Valid on all paths to function exit. + return nil + } + var range = InstructionRange(begin: initializingStore, context) + range.insert(contentsOf: deallocInsts) + + // Insert unreachable paths with no dealloc_stack. + var forwardUnreachableWalk = BasicBlockWorklist(context) + defer { forwardUnreachableWalk.deinitialize() } + + // TODO: ensure complete dealloc_stack on all paths in SIL verification, then assert exitBlock.isEmpty. + for exitBlock in range.exitBlocks { + forwardUnreachableWalk.pushIfNotVisited(exitBlock) + } + while let b = forwardUnreachableWalk.pop() { + if let unreachableInst = b.terminator as? UnreachableInst { + // Note: 'unreachableInst' is not necessarilly dominated by 'initializingStore'. This marks the range invalid, + // but leaves it in a usable state that includes all blocks covered by the temporary allocation. The extra + // blocks (backward up to the function entry) are irrelevant becase we already know that 'initializingStore' + // dominates dependent uses. + range.insert(unreachableInst) + } + for succBlock in b.successors { + forwardUnreachableWalk.pushIfNotVisited(succBlock) + } + } + return range + } + } } // ============================================================================= @@ -548,7 +648,7 @@ protocol LifetimeDependenceDefUseWalker : ForwardingDefUseWalker, } extension LifetimeDependenceDefUseWalker { - // Use a distict context name to avoid rdar://123424566 (Unable to open existential) + // Use a distinct context name to avoid rdar://123424566 (Unable to open existential) var walkerContext: Context { context } } @@ -562,8 +662,8 @@ extension LifetimeDependenceDefUseWalker { } let root = dependence.dependentValue if root.type.isAddress { - // The root address may be an escapable mark_dependence that guards its address uses (unsafeAddress), or an - // allocation or incoming argument. In all these cases, it is sufficient to walk down the address uses. + // The root address may be an escapable mark_dependence that guards its address uses (unsafeAddress), an + // allocation, an incoming argument, or an outgoing argument. In all these cases, walk down the address uses. return walkDownAddressUses(of: root) } return walkDownUses(of: root, using: nil) @@ -585,7 +685,7 @@ extension LifetimeDependenceDefUseWalker { // Override ForwardingDefUseWalker. mutating func walkDown(operand: Operand) -> WalkResult { // Initially delegate all uses to OwnershipUseVisitor. - // walkDownDefault will be called for uses that forward ownership. + // forwardingUse() will be called for uses that forward ownership, which then delegates to walkDownDefault(). return classify(operand: operand) } @@ -638,7 +738,7 @@ extension LifetimeDependenceDefUseWalker { is DestroyNotEscapedClosureInst, is ClassMethodInst, is SuperMethodInst, is ClassifyBridgeObjectInst, is DebugValueInst, is ObjCMethodInst, is ObjCSuperMethodInst, is UnmanagedRetainValueInst, - is UnmanagedReleaseValueInst, is SelectEnumInst: + is UnmanagedReleaseValueInst, is SelectEnumInst, is IgnoredUseInst: // Catch .instantaneousUse operations that are dependence leaf uses. return leafUse(of: operand) @@ -663,6 +763,11 @@ extension LifetimeDependenceDefUseWalker { mutating func forwardingUse(of operand: Operand, isInnerLifetime: Bool) -> WalkResult { + // Lifetime dependence is only interested in dominated uses. Treat a returned phi like a dominated use by stopping + // at the phi operand rather than following the forwarded value to the ReturnInst. + if let phi = Phi(using: operand), phi.isReturnValue { + return returnedDependence(result: operand) + } // Delegate ownership forwarding operations to the ForwardingDefUseWalker. return walkDownDefault(forwarding: operand) } @@ -796,7 +901,10 @@ extension LifetimeDependenceDefUseWalker { } private mutating func walkDownAddressUses(of address: Value) -> WalkResult { - address.uses.ignoreTypeDependence.walk { + if !needWalk(for: address) { + return .continueWalk + } + return address.uses.ignoreTypeDependence.walk { return classifyAddress(operand: $0) } } @@ -804,7 +912,7 @@ extension LifetimeDependenceDefUseWalker { // Helpers extension LifetimeDependenceDefUseWalker { - // Visit uses of borrowing instruction (operandOwnerhip == .borrow). + // Visit uses of borrowing instruction (operandOwnership == .borrow). private mutating func visitAllBorrowUses( of operand: Operand, by borrowInst: BorrowingInstruction) -> WalkResult { switch borrowInst { @@ -814,9 +922,15 @@ extension LifetimeDependenceDefUseWalker { return walkDownUses(of: bfi, using: operand) case let .storeBorrow(sbi): return walkDownAddressUses(of: sbi) - case .beginApply: - // Skip the borrow scope; the type system enforces non-escapable - // arguments. + case let .beginApply(bai): + // First, visit the uses of any non-Escapable yields. The yielded value may be copied and used outside the + // coroutine scope. Now, visit the uses of the begin_apply token. This adds the coroutine scope itself to the + for yield in bai.yieldedValues { + if walkDownUses(of: yield, using: operand) == .abortWalk { + return .abortWalk + } + } + // lifetime to account for the scope of any arguments. return visitInnerBorrowUses(of: borrowInst, operand: operand) case .partialApply, .markDependence: fatalError("OwnershipUseVisitor should bypass partial_apply [on_stack] " @@ -829,7 +943,7 @@ extension LifetimeDependenceDefUseWalker { } } - // Visit a dependent local variable (alloc_box), or temporary storage (alloc_stack). The depenedency is typically from + // Visit a dependent local variable (alloc_box), or temporary storage (alloc_stack). The dependency is typically from // storing a dependent value at `address`, but may be from an outright `mark_dependence_addr`. // // This handles stores of the entire value and stores into a member. Storing into a member makes the entire aggregate @@ -910,6 +1024,10 @@ extension LifetimeDependenceDefUseWalker { return loadedAddressUse(of: localAccess.operand!, intoValue: load) case let copyAddr as SourceDestAddrInstruction: return loadedAddressUse(of: localAccess.operand!, intoAddress: copyAddr.destinationOperand) + case is SwitchEnumAddrInst: + // switch_enum_addr does not produce any values. Subsequent uses of the address (unchecked_enum_data_addr) + // directly use the original address. + return .continueWalk default: return .abortWalk } @@ -929,9 +1047,9 @@ extension LifetimeDependenceDefUseWalker { // Simply a marker that indicates the start of an in-memory dependent value. If this was a mark_dependence, uses // of its forwarded address has were visited by LocalVariableAccessWalker and recorded as separate local accesses. return .continueWalk - case .store: - let si = localAccess.operand!.instruction as! StoringInstruction - assert(si.sourceOperand == initialValue, "the only reachable store should be the current assignment") + case .store, .storeBorrow: + // A store does not use the previous in-memory value. + return .continueWalk case .apply: return visitAppliedUse(of: localAccess.operand!, by: localAccess.instruction as! FullApplySite) case .escape: @@ -945,13 +1063,15 @@ extension LifetimeDependenceDefUseWalker { return yieldedDependence(result: localAccess.operand!) case .incomingArgument: fatalError("Incoming arguments are never reachable") + case .deadEnd: + return .continueWalk } - return .continueWalk } private mutating func visitAppliedUse(of operand: Operand, by apply: FullApplySite) -> WalkResult { if let conv = apply.convention(of: operand), conv.isIndirectOut { - return leafUse(of: operand) + // This apply initializes an allocation. + return dependentUse(of: operand, dependentAddress: operand.value) } if apply.isCallee(operand: operand) { return leafUse(of: operand) @@ -1065,6 +1185,277 @@ private struct LifetimeDependenceUsePrinter : LifetimeDependenceDefUseWalker { } } +// ============================================================================= +// LifetimeDependenceUseDefWalker - upward walk +// ============================================================================= + +/// Walk up lifetime dependencies to the first value associated with a variable declaration. +/// +/// To start walking: +/// walkUp(newLifetime: Value) -> WalkResult +/// +/// This utility finds a value or address that is the best-effort "root" of the chain of temporary lifetime-dependent +/// values. +/// +/// This "looks through" projections: a property that is either visible as a stored property or access via +/// unsafe[Mutable]Address. +/// +/// dependsOn(lvalue.field) // finds 'lvalue' when 'field' is a stored property +/// +/// dependsOn(lvalue.computed) // finds the temporary value directly returned by a getter. +/// +/// SILGen emits temporary copies that violate lifetime dependence semantics. This utility looks through such temporary +/// copies, stopping at a value that introduces an immutable variable: move_value [var_decl] or begin_borrow [var_decl], +/// or at an access of a mutable variable: begin_access [read] or begin_access [modify]. +/// +/// In this example, the dependence "root" is copied, borrowed, and forwarded before being used as the base operand of +/// `mark_dependence`. The dependence "root" is the parent of the outer-most dependence scope. +/// +/// %root = apply // lifetime dependence root +/// %copy = copy_value %root +/// %parent = begin_borrow %copy // lifetime dependence parent value +/// %base = struct_extract %parent // lifetime dependence base value +/// %dependent = mark_dependence [nonescaping] %value on %base +/// +/// LifetimeDependenceUseDefWalker extends the ForwardingUseDefWalker to follow copies, moves, and +/// borrows. ForwardingUseDefWalker treats these as forward-extended lifetime introducers. But they inherit a lifetime +/// dependency from their operand because non-escapable values can be copied, moved, and borrowed. Nonetheless, all of +/// their uses must remain within original dependence scope. +/// +/// # owned lifetime dependence +/// %parent = apply // begin dependence scope -+ +/// ... | +/// %1 = mark_dependence [nonescaping] %value on %parent | +/// ... | +/// %2 = copy_value %1 -+ | +/// # forwarding instruction | | +/// %3 = struct $S (%2) | forward-extended lifetime | +/// | | OSSA Lifetime +/// %4 = move_value %3 -+ | +/// ... | forward-extended lifetime | +/// %5 = begin_borrow %4 | -+ | +/// # dependent use of %1 | | forward-extended lifetime| +/// end_borrow %5 | -+ | +/// destroy_value %4 -+ | +/// ... | +/// destroy_value %parent // end dependence scope -+ +/// +/// All of the dependent uses including `end_borrow %5` and `destroy_value %4` must be before the end of the dependence +/// scope: `destroy_value %parent`. In this case, the dependence parent is an owned value, so the scope is simply the +/// value's OSSA lifetime. +/// +/// The ForwardingUseDefWalker's PathContext is the most recent lifetime owner (Value?). If we ever want to track the +/// access path as well, then we can aggregate both the owner and access path in a single PathContext. +protocol LifetimeDependenceUseDefValueWalker : ForwardingUseDefWalker where PathContext == Value? { + var context: Context { get } + + // 'path' is the lifetime "owner": the "def" into which the current 'value' is eventually forwarded. + mutating func introducer(_ value: Value, _ path: PathContext) -> WalkResult + + // Minimally, check a ValueSet. This walker may traverse chains of + // aggregation and destructuring along with phis. + // + // 'path' is the "owner" of the forwarded value. + mutating func needWalk(for value: Value, _ path: PathContext) -> Bool + + // The 'newLifetime' value is not forwarded to its uses. + mutating func walkUp(newLifetime: Value) -> WalkResult + + // 'owner' is the "def" into which the current 'value' is eventually forwarded. + mutating func walkUp(value: Value, _ owner: Value?) -> WalkResult +} + +// Defaults +extension LifetimeDependenceUseDefValueWalker { + mutating func walkUp(value: Value, _ owner: Value?) -> WalkResult { + walkUpDefault(value: value, owner) + } +} + +// Helpers +extension LifetimeDependenceUseDefValueWalker { + mutating func walkUpDefault(value: Value, _ owner: Value?) -> WalkResult { + switch value.definingInstruction { + case let transition as OwnershipTransitionInstruction: + return walkUp(newLifetime: transition.operand.value) + case let load as LoadInstruction: + return walkUp(newLifetime: load.address) + default: + break + } + // If the dependence chain has a phi, consider it a root. Dependence roots dominate all dependent values. + if Phi(value) != nil { + return introducer(value, owner) + } + // ForwardingUseDefWalker will callback to introducer() when it finds no forwarding instruction. + return walkUpDefault(forwarded: value, owner) + } +} + +protocol LifetimeDependenceUseDefAddressWalker { + var context: Context { get } + + // If the scoped value is trivial, then only the variable's lexical scope is relevant, and access scopes can be + // ignored. + var isTrivialScope: Bool { get } + + mutating func needWalk(for address: Value) -> Bool + + mutating func addressIntroducer(_ address: Value, access: AccessBaseAndScopes) -> WalkResult + + // The 'newLifetime' value is not forwarded to its uses. It may be a non-address or an address. + mutating func walkUp(newLifetime: Value) -> WalkResult + + mutating func walkUp(address: Value, access: AccessBaseAndScopes) -> WalkResult +} + +// Defaults +extension LifetimeDependenceUseDefAddressWalker { + mutating func walkUp(address: Value, access: AccessBaseAndScopes) -> WalkResult { + walkUpDefault(address: address, access: access) + } +} + +// Helpers +extension LifetimeDependenceUseDefAddressWalker { + mutating func walkUp(address: Value) -> WalkResult { + walkUp(address: address, access: address.accessBaseWithScopes) + } + + mutating func walkUpDefault(address: Value, access: AccessBaseAndScopes) -> WalkResult { + if !needWalk(for: address) { + return .continueWalk + } + if let beginAccess = access.innermostAccess { + // Skip the access scope for unsafe[Mutable]Address. Treat it like a projection of 'self' rather than a separate + // variable access. + if let addressorSelf = beginAccess.unsafeAddressorSelf { + return walkUp(newLifetime: addressorSelf) + } + // Ignore the access scope for trivial values regardless of whether it is singly-initialized. Trivial values do not + // need to be kept alive in memory and can be safely be overwritten in the same scope. Lifetime dependence only + // cares that the loaded value is within the lexical scope of the trivial value's variable declaration. Rather + // than skipping all access scopes, call 'walkUp' on each nested access in case one of them needs to redirect the + // walk, as required for 'access.unsafeAddressorSelf' or in case the implementation overrides certain accesses. + if isTrivialScope { + return walkUp(newLifetime: beginAccess.address) + } + // Generally assume an access scope introduces a variable borrow scope. And generally ignore address forwarding + // mark_dependence. + return addressIntroducer(beginAccess, access: access) + } + // Continue walking for some kinds of access base. + switch access.base { + case .box, .global, .class, .tail, .pointer, .index, .unidentified: + break + case let .stack(allocStack): + // Ignore stack locations. Their access scopes do not affect lifetime dependence. + return walkUp(stackInitializer: allocStack, at: address, access: access) + case let .argument(arg): + if arg.convention.isExclusiveIndirect { + return addressIntroducer(arg, access: access) + } + case let .yield(yieldedAddress): + // Ignore access scopes for @in or @in_guaranteed yields when all scopes are reads. + let apply = yieldedAddress.definingInstruction as! FullApplySite + if apply.convention(of: yieldedAddress).isIndirectIn && access.isOnlyReadAccess { + return addressIntroducer(yieldedAddress, access: access) + } + case .storeBorrow(let sb): + // Walk up through a store into a temporary. + if access.scopes.isEmpty, + case .stack = sb.destinationOperand.value.accessBase { + return walkUp(newLifetime: sb.source) + } + } + return addressIntroducer(access.enclosingAccess.address ?? address, access: access) + } + + // Handle singly-initialized temporary stack locations. + mutating func walkUp(stackInitializer allocStack: AllocStackInst, at address: Value, + access: AccessBaseAndScopes) -> WalkResult { + guard let initializer = allocStack.accessBase.findSingleInitializer(context) else { + return addressIntroducer(address, access: access) + } + if case let .store(store, _) = initializer { + switch store { + case let store as StoringInstruction: + return walkUp(newLifetime: store.source) + case let srcDestInst as SourceDestAddrInstruction: + return walkUp(newLifetime: srcDestInst.source) + case let apply as FullApplySite: + if let f = apply.referencedFunction, f.isConvertPointerToPointerArgument { + return walkUp(newLifetime: apply.parameterOperands[0].value) + } + default: + break + } + } + return addressIntroducer(address, access: access) + } +} + +/// Walk up through all new lifetimes to find the roots of a lifetime dependence chain. +struct LifetimeDependenceRootWalker : LifetimeDependenceUseDefValueWalker, LifetimeDependenceUseDefAddressWalker { + // The ForwardingUseDefWalker's context is the most recent lifetime owner. + typealias PathContext = Value? + + let context: Context + + // If the scoped value is trivial, then only the variable's lexical scope is relevant, and access scopes can be + // ignored. + let isTrivialScope: Bool + + // This visited set is only really needed for instructions with + // multiple results, including phis. + private var visitedValues: ValueSet + + var roots: SingleInlineArray = SingleInlineArray() + + init(_ context: Context, scopedValue: Value) { + self.context = context + self.isTrivialScope = scopedValue.type.isAddress + ? scopedValue.type.objectType.isTrivial(in: scopedValue.parentFunction) + : scopedValue.isTrivial(context) + self.visitedValues = ValueSet(context) + } + + mutating func deinitialize() { + visitedValues.deinitialize() + } + + mutating func introducer(_ value: Value, _ owner: Value?) -> WalkResult { + roots.push(value) + return .continueWalk + } + + mutating func addressIntroducer(_ address: Value, access: AccessBaseAndScopes) -> WalkResult { + roots.push(address) + return .continueWalk + } + + mutating func needWalk(for value: Value, _ owner: Value?) -> Bool { + visitedValues.insert(value) + } + + mutating func needWalk(for address: Value) -> Bool { + visitedValues.insert(address) + } + + mutating func walkUp(newLifetime: Value) -> WalkResult { + if newLifetime.type.isAddress { + return walkUp(address: newLifetime) + } + let newOwner = newLifetime.ownership == .owned ? newLifetime : nil + return walkUp(value: newLifetime, newOwner) + } +} + +// ============================================================================= +// Unit tests +// ============================================================================= + + let lifetimeDependenceUseTest = FunctionTest("lifetime_dependence_use") { function, arguments, context in let value = arguments.takeValue() @@ -1087,6 +1478,18 @@ let lifetimeDependenceUseTest = FunctionTest("lifetime_dependence_use") { _ = printer.walkDown(dependence: dependence) } +let lifetimeDependenceRootTest = FunctionTest("lifetime_dependence_root") { + function, arguments, context in + let value = arguments.takeValue() + print("Lifetime dependence roots of: \(value)") + var walker = LifetimeDependenceRootWalker(context, scopedValue: value) + let result = walker.walkUp(newLifetime: value) + assert(result == .continueWalk) + for root in walker.roots { + print("root: \(root)") + } + walker.deinitialize() +} // SIL Unit tests diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift index 2736f60ca0531..e6fc197c40c43 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift @@ -12,7 +12,7 @@ /// /// SIL operates on three kinds of addressable memory: /// -/// 1. Temporary RValues. These are recognied by AddressInitializationWalker. These largely disappear with opaque SIL +/// 1. Temporary RValues. These are recognized by AddressInitializationWalker. These largely disappear with opaque SIL /// values. /// /// 2. Local variables. These are always introduced by either a VarDeclInstruction or a Function argument with non-nil @@ -27,9 +27,9 @@ import SIL private let verbose = false -private func log(_ message: @autoclosure () -> String) { +private func log(prefix: Bool = true, _ message: @autoclosure () -> String) { if verbose { - print("### \(message())") + debugLog(prefix: prefix, message()) } } @@ -69,8 +69,10 @@ struct LocalVariableAccess: CustomStringConvertible { case dependenceSource // A value/address depends on this local here (like a load) case dependenceDest // This local depends on another value/address here (like a store) case store // 'var' initialization and destruction + case storeBorrow // scoped initialization of temporaries case apply // indirect arguments case escape // alloc_box captures + case deadEnd // unreachable } let kind: Kind // All access have an operand except .incomingArgument and .outgoingArgument. @@ -101,9 +103,9 @@ struct LocalVariableAccess: CustomStringConvertible { case .`init`, .modify: return true } - case .load, .dependenceSource, .dependenceDest: + case .load, .dependenceSource, .dependenceDest, .deadEnd: return false - case .incomingArgument, .outgoingArgument, .store, .inoutYield: + case .incomingArgument, .outgoingArgument, .store, .storeBorrow, .inoutYield: return true case .apply: let apply = instruction as! FullApplySite @@ -146,10 +148,14 @@ struct LocalVariableAccess: CustomStringConvertible { str += "dependenceDest" case .store: str += "store" + case .storeBorrow: + str += "storeBorrow" case .apply: str += "apply" case .escape: str += "escape" + case .deadEnd: + str += "deadEnd" } if let inst = instruction { str += "\(inst)" @@ -178,9 +184,9 @@ class LocalVariableAccessInfo: CustomStringConvertible { case .`init`, .modify: break // lazily compute full assignment } - case .load, .dependenceSource, .dependenceDest: + case .load, .dependenceSource, .dependenceDest: self._isFullyAssigned = false - case .store: + case .store, .storeBorrow: if let store = localAccess.instruction as? StoringInstruction { self._isFullyAssigned = LocalVariableAccessInfo.isBase(address: store.destination) } else { @@ -198,7 +204,7 @@ class LocalVariableAccessInfo: CustomStringConvertible { self.hasEscaped = true case .inoutYield: self._isFullyAssigned = false - case .incomingArgument, .outgoingArgument: + case .incomingArgument, .outgoingArgument, .deadEnd: fatalError("Function arguments are never mapped to LocalVariableAccessInfo") } } @@ -226,7 +232,7 @@ class LocalVariableAccessInfo: CustomStringConvertible { } var description: String { - return "full-assign: \(_isFullyAssigned == nil ? "unknown" : String(describing: _isFullyAssigned!)), " + return "assign: \(_isFullyAssigned == nil ? "unknown" : String(describing: _isFullyAssigned!)), " + "\(access)" } @@ -329,7 +335,7 @@ struct LocalVariableAccessMap: Collection, CustomStringConvertible { subscript(instruction: Instruction) -> LocalVariableAccessInfo? { accessMap[instruction] } var description: String { - "Access map:\n" + map({String(describing: $0)}).joined(separator: "\n") + "Access map for: \(allocation)\n" + map({String(describing: $0)}).joined(separator: "\n") } } @@ -421,12 +427,17 @@ extension LocalVariableAccessWalker: AddressUseVisitor { // temporaries do not have access scopes, so we need to walk down any projection that may be used to initialize the // temporary. mutating func projectedAddressUse(of operand: Operand, into value: Value) -> WalkResult { - // Intercept mark_dependence destination to record an access point which can be used like a store when finding all - // uses that affect the base after the point that the dependence was marked. if let md = value as? MarkDependenceInst { - assert(operand == md.valueOperand) - visit(LocalVariableAccess(.dependenceDest, operand)) - // walk down the forwarded address as usual... + if operand == md.valueOperand { + // Intercept mark_dependence destination to record an access point which can be used like a store when finding + // all uses that affect the base after the point that the dependence was marked. + visit(LocalVariableAccess(.dependenceDest, operand)) + // walk down the forwarded address as usual... + } else { + // A dependence is similar to loading from its source. Downstream uses are not accesses of the original local. + visit(LocalVariableAccess(.dependenceSource, operand)) + return .continueWalk + } } return walkDownAddressUses(address: value) } @@ -442,6 +453,9 @@ extension LocalVariableAccessWalker: AddressUseVisitor { case is LoadBorrowInst: visit(LocalVariableAccess(.load, operand)) return .continueWalk + case is StoreBorrowInst: + visit(LocalVariableAccess(.storeBorrow, operand)) + return .continueWalk default: // A StoreBorrow should be guarded by an access scope. // @@ -457,13 +471,18 @@ extension LocalVariableAccessWalker: AddressUseVisitor { mutating func leafAddressUse(of operand: Operand) -> WalkResult { switch operand.instruction { case is StoringInstruction, is SourceDestAddrInstruction, is DestroyAddrInst, is DeinitExistentialAddrInst, - is InjectEnumAddrInst, is SwitchEnumAddrInst, is TupleAddrConstructorInst, is InitBlockStorageHeaderInst, - is PackElementSetInst: - // Handle instructions that initialize both temporaries and local variables. + is InjectEnumAddrInst, is TupleAddrConstructorInst, is InitBlockStorageHeaderInst, is PackElementSetInst: + // Handle instructions that initialize both temporaries and local variables. If operand's address is the same as + // the local variable's address, then this fully kills operand liveness. The original value in operand's address + // cannot be used in any way. visit(LocalVariableAccess(.store, operand)) case let md as MarkDependenceAddrInst: assert(operand == md.addressOperand) visit(LocalVariableAccess(.dependenceDest, operand)) + case is SwitchEnumAddrInst: + // switch_enum_addr is truly a leaf address use. It does not produce a new value. But in every other respect it is + // like a load. + visit(LocalVariableAccess(.load, operand)) case is DeallocStackInst: break default: @@ -630,13 +649,17 @@ struct LocalVariableReachableAccess { // Find reaching assignments... extension LocalVariableReachableAccess { - // Gather all fully assigned accesses that reach `instruction`. + // Gather all fully assigned accesses that reach 'instruction'. If 'instruction' is itself a modify access, it is + // ignored and the nearest assignments above 'instruction' are still gathered. func gatherReachingAssignments(for instruction: Instruction, in accessStack: inout Stack) -> Bool { var blockList = BasicBlockWorklist(context) defer { blockList.deinitialize() } - let initialEffect = backwardScanAccesses(before: instruction, accessStack: &accessStack) + var initialEffect: BlockEffect? = nil + if let prev = instruction.previous { + initialEffect = backwardScanAccesses(before: prev, accessStack: &accessStack) + } if !backwardPropagateEffect(in: instruction.parentBlock, effect: initialEffect, blockList: &blockList, accessStack: &accessStack) { return false @@ -647,7 +670,7 @@ extension LocalVariableReachableAccess { // lattice: none -> read -> modify -> escape -> assign // // `blockInfo.effect` is the same as `currentEffect` returned by backwardScanAccesses, except when an early escape - // happens after an assign. + // happens below an assign, in which case we report the escape here. switch currentEffect { case .none, .read, .modify, .escape: break @@ -695,10 +718,10 @@ extension LocalVariableReachableAccess { continue case .assign: accessStack.push(accessInfo.access) - break case .escape: break } + break } return currentEffect } @@ -708,27 +731,33 @@ extension LocalVariableReachableAccess { extension LocalVariableReachableAccess { /// This performs a forward CFG walk to find known reachable uses from `assignment`. This ignores aliasing and /// escapes. - func gatherKnownReachableUses(from assignment: LocalVariableAccess, + /// + /// The known live range is the range in which the assigned value is valid and may be used by dependent values. It + /// includes the destroy or reassignment of the local. + func gatherKnownLifetimeUses(from assignment: LocalVariableAccess, in accessStack: inout Stack) { if let modifyInst = assignment.instruction { - _ = gatherReachableUses(after: modifyInst, in: &accessStack, allowEscape: true) + _ = gatherReachableUses(after: modifyInst, in: &accessStack, lifetime: true) + return } - gatherKnownReachableUsesFromEntry(in: &accessStack) + gatherKnownLifetimeUsesFromEntry(in: &accessStack) } /// This performs a forward CFG walk to find known reachable uses from the function entry. This ignores aliasing and /// escapes. - private func gatherKnownReachableUsesFromEntry(in accessStack: inout Stack) { + private func gatherKnownLifetimeUsesFromEntry(in accessStack: inout Stack) { assert(accessMap.liveInAccess!.kind == .incomingArgument, "only an argument access is live in to the function") let firstInst = accessMap.function.entryBlock.instructions.first! - _ = gatherReachableUses(onOrAfter: firstInst, in: &accessStack, allowEscape: true) + _ = gatherReachableUses(onOrAfter: firstInst, in: &accessStack, lifetime: true) } /// This performs a forward CFG walk to find all reachable uses of `modifyInst`. `modifyInst` may be a `begin_access /// [modify]` or instruction that initializes the local variable. /// - /// Returns true if all possible reachable uses were visited. Returns false if any escapes may reach `modifyInst` are - /// reachable from `modifyInst`. + /// This does not include the destroy or reassignment of the value set by `modifyInst`. + /// + /// Returns true if all possible reachable uses were visited. Returns false if any escapes may reach `modifyInst` or + /// are reachable from `modifyInst`. /// /// This does not gather the escaping accesses themselves. When escapes are reachable, it also does not guarantee that /// previously reachable accesses are gathered. @@ -748,37 +777,40 @@ extension LocalVariableReachableAccess { if accessInfo.hasEscaped! { return false } - return gatherReachableUses(after: modifyInst, in: &accessStack, allowEscape: false) + return gatherReachableUses(after: modifyInst, in: &accessStack, lifetime: false) } /// This performs a forward CFG walk to find all uses of this local variable reachable after `begin`. /// - /// If `allowEscape` is true, then this returns false if the walk ended early because of a reachable escape. + /// If `lifetime` is true, then this gathers the full known lifetime, including destroys and reassignments ignoring + /// escapes. + /// + /// If `lifetime` is false, then this returns `false` if the walk ended early because of a reachable escape. private func gatherReachableUses(after begin: Instruction, in accessStack: inout Stack, - allowEscape: Bool) -> Bool { + lifetime: Bool) -> Bool { if let term = begin as? TermInst { for succ in term.successors { - if !gatherReachableUses(onOrAfter: succ.instructions.first!, in: &accessStack, allowEscape: allowEscape) { + if !gatherReachableUses(onOrAfter: succ.instructions.first!, in: &accessStack, lifetime: lifetime) { return false } } return true } else { - return gatherReachableUses(onOrAfter: begin.next!, in: &accessStack, allowEscape: allowEscape) + return gatherReachableUses(onOrAfter: begin.next!, in: &accessStack, lifetime: lifetime) } } /// This performs a forward CFG walk to find all uses of this local variable reachable after and including `begin`. /// - /// If `allowEscape` is true, then this returns false if the walk ended early because of a reachable escape. + /// If `lifetime` is true, then this returns false if the walk ended early because of a reachable escape. private func gatherReachableUses(onOrAfter begin: Instruction, in accessStack: inout Stack, - allowEscape: Bool) -> Bool { + lifetime: Bool) -> Bool { var blockList = BasicBlockWorklist(context) defer { blockList.deinitialize() } let initialBlock = begin.parentBlock - let initialEffect = forwardScanAccesses(after: begin, accessStack: &accessStack, allowEscape: allowEscape) - if !allowEscape, initialEffect == .escape { + let initialEffect = forwardScanAccesses(after: begin, accessStack: &accessStack, lifetime: lifetime) + if !lifetime, initialEffect == .escape { return false } forwardPropagateEffect(in: initialBlock, blockInfo: blockMap[initialBlock], effect: initialEffect, @@ -794,22 +826,22 @@ extension LocalVariableReachableAccess { case .none: break case .escape: - if !allowEscape { + if !lifetime { break } fallthrough case .read, .modify, .assign: let firstInst = block.instructions.first! - currentEffect = forwardScanAccesses(after: firstInst, accessStack: &accessStack, allowEscape: allowEscape) + currentEffect = forwardScanAccesses(after: firstInst, accessStack: &accessStack, lifetime: lifetime) } - if !allowEscape, currentEffect == .escape { + if !lifetime, currentEffect == .escape { return false } forwardPropagateEffect(in: block, blockInfo: blockInfo, effect: currentEffect, blockList: &blockList, accessStack: &accessStack) } - log("\(accessMap)") - log("Reachable access:\n\(accessStack.map({ String(describing: $0)}).joined(separator: "\n"))") + log("\n\(accessMap)") + log(prefix: false, "Reachable access:\n\(accessStack.map({ String(describing: $0)}).joined(separator: "\n"))") return true } @@ -826,6 +858,8 @@ extension LocalVariableReachableAccess { } if block.terminator.isFunctionExiting { accessStack.push(LocalVariableAccess(.outgoingArgument, block.terminator)) + } else if block.successors.isEmpty { + accessStack.push(LocalVariableAccess(.deadEnd, block.terminator)) } else { for successor in block.successors { blockList.pushIfNotVisited(successor) } } @@ -835,9 +869,9 @@ extension LocalVariableReachableAccess { } // Check all instructions in this block after and including `begin`. Return a BlockEffect indicating the combined - // effects seen before stopping the scan. An .assign stops the scan. A .escape stops the scan if allowEscape is false. + // effects seen before stopping the scan. An .assign stops the scan. A .escape stops the scan if lifetime is false. private func forwardScanAccesses(after first: Instruction, accessStack: inout Stack, - allowEscape: Bool) + lifetime: Bool) -> BlockEffect? { var currentEffect: BlockEffect? for inst in InstructionList(first: first) { @@ -847,9 +881,12 @@ extension LocalVariableReachableAccess { currentEffect = BlockEffect(for: accessInfo, accessMap.context).meet(currentEffect) switch currentEffect! { case .assign: + if lifetime { + accessStack.push(accessInfo.access) + } return currentEffect case .escape: - if !allowEscape { + if !lifetime { log("Local variable: \(accessMap.allocation)\n escapes at \(inst)") return currentEffect } @@ -873,7 +910,7 @@ extension LocalVariableReachableAccess { private func findAllEscapesPriorToAccess() { var visitedBlocks = BasicBlockSet(context) var escapedBlocks = BasicBlockSet(context) - var blockList = BasicBlockWorklist(context) + var blockList = Stack(context) defer { visitedBlocks.deinitialize() escapedBlocks.deinitialize() @@ -886,19 +923,19 @@ extension LocalVariableReachableAccess { for successor in from.successors { if hasEscaped { if escapedBlocks.insert(successor) { - blockList.pushIfNotVisited(successor) + blockList.push(successor) } } else if visitedBlocks.insert(successor) { - blockList.pushIfNotVisited(successor) + blockList.push(successor) } } } var hasEscaped = propagateEscapeInBlock(after: accessMap.allocation.nextInstruction, hasEscaped: false) forwardPropagate(accessMap.allocation.parentBlock, hasEscaped) while let block = blockList.pop() { - hasEscaped = escapedBlocks.insert(block) + hasEscaped = escapedBlocks.contains(block) hasEscaped = propagateEscapeInBlock(after: block.instructions.first!, hasEscaped: hasEscaped) - forwardPropagate(accessMap.allocation.parentBlock, hasEscaped) + forwardPropagate(block, hasEscaped) } } @@ -917,3 +954,49 @@ extension LocalVariableReachableAccess { return hasEscaped } } + +let localVariableReachingAssignmentsTest = FunctionTest("local_variable_reaching_assignments") { + function, arguments, context in + let allocation = arguments.takeValue() + let instruction = arguments.takeInstruction() + print("### Allocation: \(allocation)") + let localReachabilityCache = LocalVariableReachabilityCache() + guard let localReachability = localReachabilityCache.reachability(for: allocation, context) else { + print("No reachability") + return + } + print("### Access map:") + print(localReachability.accessMap) + print("### Instruction: \(instruction)") + var reachingAssignments = Stack(context) + defer { reachingAssignments.deinitialize() } + guard localReachability.gatherReachingAssignments(for: instruction, in: &reachingAssignments) else { + print("!!! Reaching escape") + return + } + print("### Reachable assignments:") + print(reachingAssignments.map({ String(describing: $0)}).joined(separator: "\n")) +} + +let localVariableReachableUsesTest = FunctionTest("local_variable_reachable_uses") { + function, arguments, context in + let allocation = arguments.takeValue() + let modify = arguments.takeInstruction() + print("### Allocation: \(allocation)") + let localReachabilityCache = LocalVariableReachabilityCache() + guard let localReachability = localReachabilityCache.reachability(for: allocation, context) else { + print("No reachability") + return + } + print("### Access map:") + print(localReachability.accessMap) + print("### Modify: \(modify)") + var reachableUses = Stack(context) + defer { reachableUses.deinitialize() } + guard localReachability.gatherAllReachableUses(of: modify, in: &reachableUses) else { + print("!!! Reachable escape") + return + } + print("### Reachable access:") + print(reachableUses.map({ String(describing: $0)}).joined(separator: "\n")) +} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 6173adaf24e75..801c0fbf2ce9a 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -45,6 +45,18 @@ extension Value { } } + var lookThroughIndexScalarCast: Value { + if let castBuiltin = self as? BuiltinInst { + switch castBuiltin.id { + case .TruncOrBitCast, .SExtOrBitCast: + return castBuiltin.arguments[0] + default: + return self + } + } + return self + } + func isInLexicalLiverange(_ context: some Context) -> Bool { var worklist = ValueWorklist(context) defer { worklist.deinitialize() } @@ -59,7 +71,7 @@ extension Value { } switch v { case let fw as ForwardingInstruction: - worklist.pushIfNotVisited(contentsOf: fw.definedOperands.lazy.map { $0.value }) + worklist.pushIfNotVisited(contentsOf: fw.definedOperands.values) case let bf as BorrowedFromInst: worklist.pushIfNotVisited(bf.borrowedValue) case let bb as BeginBorrowInst: @@ -70,7 +82,7 @@ extension Value { } else if let termResult = TerminatorResult(arg), let fw = termResult.terminator as? ForwardingInstruction { - worklist.pushIfNotVisited(contentsOf: fw.definedOperands.lazy.map { $0.value }) + worklist.pushIfNotVisited(contentsOf: fw.definedOperands.values) } default: continue @@ -113,20 +125,32 @@ extension Value { return true } + /// Project out a sub-field of this value according to `path`. + /// If this is an "owned" value the result is an "owned" value which forwards the original value. + /// This only works if _all_ non-trivial fields are projected. Otherwise some non-trivial results of + /// `destructure_struct` or `destructure_tuple` will be leaked. func createProjection(path: SmallProjectionPath, builder: Builder) -> Value { let (kind, index, subPath) = path.pop() + let result: Value switch kind { case .root: return self case .structField: - let structExtract = builder.createStructExtract(struct: self, fieldIndex: index) - return structExtract.createProjection(path: subPath, builder: builder) + if ownership == .owned { + result = builder.createDestructureStruct(struct: self).results[index] + } else { + result = builder.createStructExtract(struct: self, fieldIndex: index) + } case .tupleField: - let tupleExtract = builder.createTupleExtract(tuple: self, elementIndex: index) - return tupleExtract.createProjection(path: subPath, builder: builder) + if ownership == .owned { + result = builder.createDestructureTuple(tuple: self).results[index] + } else { + result = builder.createTupleExtract(tuple: self, elementIndex: index) + } default: fatalError("path is not materializable") } + return result.createProjection(path: subPath, builder: builder) } func createAddressProjection(path: SmallProjectionPath, builder: Builder) -> Value { @@ -238,6 +262,18 @@ extension Builder { } } + static func insertCleanupAtFunctionExits( + of function: Function, + _ context: some MutatingContext, + insert: (Builder) -> () + ) { + for exitBlock in function.blocks where exitBlock.terminator.isFunctionExiting { + let terminator = exitBlock.terminator + let builder = Builder(before: terminator, location: terminator.location.asCleanup, context) + insert(builder) + } + } + func destroyCapturedArgs(for paiOnStack: PartialApplyInst) { precondition(paiOnStack.isOnStack, "Function must only be called for `partial_apply`s on stack!") self.bridged.destroyCapturedArgs(paiOnStack.bridged) @@ -329,14 +365,6 @@ extension Value { } } -extension SingleValueInstruction { - /// Replaces all uses with `replacement` and then erases the instruction. - func replace(with replacement: Value, _ context: some MutatingContext) { - uses.replaceAll(with: replacement, context) - context.erase(instruction: self) - } -} - extension Instruction { var isTriviallyDead: Bool { if results.contains(where: { !$0.uses.isEmpty }) { @@ -387,8 +415,7 @@ extension Instruction { case let bi as BuiltinInst: switch bi.id { case .ZeroInitializer: - let type = bi.type.isBuiltinVector ? bi.type.builtinVectorElementType : bi.type - return type.isBuiltinInteger || type.isBuiltinFloat + return bi.arguments.count == 0 case .PtrToInt: return bi.operands[0].value is StringLiteralInst case .IntToPtr: @@ -456,7 +483,7 @@ extension Instruction { } } - /// Returns true if `otherInst` is in the same block and dominated by this instruction. + /// Returns true if `otherInst` is in the same block and is strictly dominated by this instruction. /// To be used as simple dominance check if both instructions are most likely located in the same block /// and no DominatorTree is available (like in instruction simplification). func dominatesInSameBlock(_ otherInst: Instruction) -> Bool { @@ -481,6 +508,22 @@ extension Instruction { } return false } + + /// Returns true if `otherInst` is in the same block and is strictly dominated by this instruction or + /// the parent block of the instruction dominates parent block of `otherInst`. + func dominates( + _ otherInst: Instruction, + _ domTree: DominatorTree + ) -> Bool { + if parentBlock == otherInst.parentBlock { + return dominatesInSameBlock(otherInst) + } else { + return parentBlock.dominates( + otherInst.parentBlock, + domTree + ) + } + } /// If this instruction uses a (single) existential archetype, i.e. it has a type-dependent operand, /// returns the concrete type if it is known. @@ -580,36 +623,22 @@ extension StoreInst { extension LoadInst { @discardableResult func trySplit(_ context: FunctionPassContext) -> Bool { - var elements = [Value]() - let builder = Builder(before: self, context) if type.isStruct { - if (type.nominal as! StructDecl).hasUnreferenceableStorage { - return false - } - guard let fields = type.getNominalFields(in: parentFunction) else { + guard !(type.nominal as! StructDecl).hasUnreferenceableStorage, + let fields = type.getNominalFields(in: parentFunction) else { return false } - for idx in 0.. LoadOwnership { @@ -620,6 +649,70 @@ extension LoadInst { return fieldValue.type.isTrivial(in: parentFunction) ? .trivial : self.loadOwnership } } + + func trySplit( + alongPath projectionPath: SmallProjectionPath, + _ context: FunctionPassContext + ) -> [LoadInst]? { + if projectionPath.isEmpty { + return nil + } + + let (fieldKind, index, pathRemainder) = projectionPath.pop() + + var elements: [LoadInst] + + switch fieldKind { + case .structField where type.isStruct: + guard !(type.nominal as! StructDecl).hasUnreferenceableStorage, + let fields = type.getNominalFields(in: parentFunction) else { + return nil + } + + elements = splitStruct(fields: fields, context) + case .tupleField where type.isTuple: + elements = splitTuple(context) + default: + return nil + } + + if let recursiveSplitLoad = elements[index].trySplit(alongPath: pathRemainder, context) { + elements.remove(at: index) + elements += recursiveSplitLoad + } + + return elements + } + + private func splitStruct(fields: NominalFieldsArray, _ context: FunctionPassContext) -> [LoadInst] { + var elements = [LoadInst]() + let builder = Builder(before: self, context) + + for idx in 0.. [LoadInst] { + var elements = [LoadInst]() + let builder = Builder(before: self, context) + + for idx in 0.. Bool -) -> (allocInst: AllocGlobalInst, storeToGlobal: StoreInst)? { - guard let block = function.blocks.singleElement else { - return nil - } - - var allocInst: AllocGlobalInst? = nil - var globalAddr: GlobalAddrInst? = nil - var store: StoreInst? = nil - - for inst in block.instructions { - switch inst { - case is ReturnInst, - is DebugValueInst, - is DebugStepInst, - is BeginAccessInst, - is EndAccessInst: - continue - case let agi as AllocGlobalInst: - if allocInst == nil { - allocInst = agi - continue - } - case let ga as GlobalAddrInst: - if let agi = allocInst, agi.global == ga.global { - globalAddr = ga - } - continue - case let si as StoreInst: - if store == nil, - let ga = globalAddr, - si.destination == ga - { - store = si - continue - } - // Note that the initializer must not contain a `global_value` because `global_value` needs to - // initialize the class metadata at runtime. - default: - if inst.isValidInStaticInitializerOfGlobal(context) { - continue - } - } - if handleUnknownInstruction(inst) { - continue - } - return nil - } - if let store = store { - return (allocInst: allocInst!, storeToGlobal: store) - } - return nil -} - func canDynamicallyCast(from sourceType: CanonicalType, to destType: CanonicalType, in function: Function, sourceTypeIsExact: Bool ) -> Bool? { @@ -941,27 +966,74 @@ extension CheckedCastAddrBranchInst { extension CopyAddrInst { @discardableResult - func replaceWithLoadAndStore(_ context: some MutatingContext) -> StoreInst { - let loadOwnership: LoadInst.LoadOwnership - let storeOwnership: StoreInst.StoreOwnership - if parentFunction.hasOwnership { - if source.type.isTrivial(in: parentFunction) { - loadOwnership = .trivial - storeOwnership = .trivial - } else { - loadOwnership = isTakeOfSrc ? .take : .copy - storeOwnership = isInitializationOfDest ? .initialize : .assign + func trySplit(_ context: FunctionPassContext) -> Bool { + let builder = Builder(before: self, context) + if source.type.isStruct { + if (source.type.nominal as! StructDecl).hasUnreferenceableStorage { + return false } - } else { - loadOwnership = .unqualified - storeOwnership = .unqualified + guard let fields = source.type.getNominalFields(in: parentFunction) else { + return false + } + for idx in 0.. Bool { + return isTakeOfSource && !fieldValue.type.objectType.isTrivial(in: parentFunction) + } + + @discardableResult + func replaceWithLoadAndStore(_ context: some MutatingContext) -> (load: LoadInst, store: StoreInst) { let builder = Builder(before: self, context) - let value = builder.createLoad(fromAddress: source, ownership: loadOwnership) - let store = builder.createStore(source: value, destination: destination, ownership: storeOwnership) + let load = builder.createLoad(fromAddress: source, ownership: loadOwnership) + let store = builder.createStore(source: load, destination: destination, ownership: storeOwnership) context.erase(instruction: self) - return store + return (load, store) + } + + var loadOwnership: LoadInst.LoadOwnership { + if !parentFunction.hasOwnership { + return .unqualified + } + if type.isTrivial(in: parentFunction) { + return .trivial + } + if isTakeOfSource { + return .take + } + return .copy + } + + var storeOwnership: StoreInst.StoreOwnership { + if !parentFunction.hasOwnership { + return .unqualified + } + if type.isTrivial(in: parentFunction) { + return .trivial + } + if isInitializationOfDestination { + return .initialize + } + return .assign } } @@ -983,6 +1055,81 @@ extension Type { if !context.options.useAggressiveReg2MemForCodeSize { return true } - return context._bridged.shouldExpand(self.bridged) + return context.bridgedPassContext.shouldExpand(self.bridged) + } +} + +/// Used by TempLValueElimination and TempRValueElimination to make the optimization work by both, +/// `copy_addr` and `load`-`store`-pairs. +protocol CopyLikeInstruction: Instruction { + var sourceAddress: Value { get } + var destinationAddress: Value { get } + var isTakeOfSource: Bool { get } + var isInitializationOfDestination: Bool { get } + var loadingInstruction: Instruction { get } +} + +extension CopyAddrInst: CopyLikeInstruction { + var sourceAddress: Value { source } + var destinationAddress: Value { destination } + var loadingInstruction: Instruction { self } +} + +// A `store` which has a `load` as source operand. This is basically the same as a `copy_addr`. +extension StoreInst: CopyLikeInstruction { + var sourceAddress: Value { load.address } + var destinationAddress: Value { destination } + var isTakeOfSource: Bool { load.loadOwnership == .take } + var isInitializationOfDestination: Bool { storeOwnership != .assign } + var loadingInstruction: Instruction { load } + private var load: LoadInst { source as! LoadInst } +} + +func eraseIfDead(functions: [Function], _ context: ModulePassContext) { + var toDelete = functions + while true { + var remaining = [Function]() + for fn in toDelete { + if !fn.isPossiblyUsedExternally && !fn.isReferencedInModule { + context.erase(function: fn) + } else { + remaining.append(fn) + } + } + if remaining.count == toDelete.count { + return + } + toDelete = remaining + } +} + +func isInLoop(block startBlock: BasicBlock, _ context: FunctionPassContext) -> Bool { + var worklist = BasicBlockWorklist(context) + defer { worklist.deinitialize() } + + worklist.pushIfNotVisited(contentsOf: startBlock.successors) + while let block = worklist.pop() { + if block == startBlock { + return true + } + worklist.pushIfNotVisited(contentsOf: block.successors) } + return false +} + +func cloneFunction(from originalFunction: Function, toEmpty targetFunction: Function, _ context: FunctionPassContext) { + var cloner = Cloner(cloneToEmptyFunction: targetFunction, context) + defer { cloner.deinitialize() } + cloner.cloneFunctionBody(from: originalFunction) +} + +func cloneAndSpecializeFunction(from originalFunction: Function, + toEmpty targetFunction: Function, + substitutions: SubstitutionMap, + _ context: FunctionPassContext +) { + var cloner = TypeSubstitutionCloner(fromFunction: originalFunction, toEmptyFunction: targetFunction, + substitutions: substitutions, context) + defer { cloner.deinitialize() } + cloner.cloneFunctionBody() } diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift index ec3c03b82ccaf..ba61cf0b440e8 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift @@ -84,7 +84,27 @@ func computeKnownLiveness(for definingValue: Value, visitInnerUses: Bool = false visitInnerUses: visitInnerUses, context).acquireRange } -/// If any interior pointer may escape, then record the first instance here. If 'ignoseEscape' is true, this +/// Compute the live range for the borrow scopes of a guaranteed value. This returns a separate instruction range for +/// each of the value's borrow introducers. +/// +/// TODO: This should return a single multiply-defined instruction range. +func computeBorrowLiveRange(for value: Value, _ context: FunctionPassContext) + -> SingleInlineArray<(BeginBorrowValue, InstructionRange)> { + assert(value.ownership == .guaranteed) + + var ranges = SingleInlineArray<(BeginBorrowValue, InstructionRange)>() + // If introducers is empty, then the dependence is on a trivial value, so + // there is no ownership range. + for beginBorrow in value.getBorrowIntroducers(context) { + /// FIXME: Remove calls to computeKnownLiveness() as soon as lifetime completion runs immediately after + /// SILGen. Instead, this should compute linear liveness for borrowed value by switching over BeginBorrowValue, just + /// like LifetimeDependence.Scope.computeRange(). + ranges.push((beginBorrow, computeKnownLiveness(for: beginBorrow.value, context))) + } + return ranges +} + +/// If any interior pointer may escape, then record the first instance here. If 'ignoreEscape' is true, this /// immediately aborts the walk, so further instances are unavailable. /// /// .escaping may either be a non-address operand with @@ -685,7 +705,7 @@ extension InteriorUseWalker: AddressUseVisitor { if handleInner(borrowed: ba) == .abortWalk { return .abortWalk } - return ba.endOperands.walk { useVisitor($0) } + return ba.scopeEndingOperands.walk { useVisitor($0) } case let ba as BeginApplyInst: if handleInner(borrowed: ba.token) == .abortWalk { return .abortWalk @@ -697,7 +717,7 @@ extension InteriorUseWalker: AddressUseVisitor { if handleInner(borrowed: sb) == .abortWalk { return .abortWalk } - return sb.uses.filterUsers(ofType: EndBorrowInst.self).walk { + return sb.uses.filterUses(ofType: EndBorrowInst.self).walk { useVisitor($0) } case let load as LoadBorrowInst: @@ -786,6 +806,10 @@ extension InteriorUseWalker { if let inst = operand.instruction as? ForwardingInstruction { return inst.forwardedResults.walk { walkDownUses(of: $0) } } + // TODO: Represent apply of borrow accessors as ForwardingOperation and use that over ForwardingInstruction + if let apply = operand.instruction as? FullApplySite, apply.hasGuaranteedResult { + return walkDownUses(of: apply.singleDirectResult!) + } // TODO: verify that ForwardInstruction handles all .forward operand ownership and assert that only phis can be // reached: assert(Phi(using: operand) != nil) return .continueWalk @@ -904,3 +928,29 @@ let interiorLivenessTest = FunctionTest("interior_liveness_swift") { defer { boundary.deinitialize() } print(boundary) } + +// +// TODO: Move this to InstructionRange.swift when computeLinearLiveness is in the SIL module. +// +let rangeOverlapsPathTest = FunctionTest("range_overlaps_path") { + function, arguments, context in + let rangeValue = arguments.takeValue() + print("Range of: \(rangeValue)") + var range = computeLinearLiveness(for: rangeValue, context) + defer { range.deinitialize() } + let pathInst = arguments.takeInstruction() + print("Path begin: \(pathInst)") + if let pathBegin = pathInst as? ScopedInstruction { + for end in pathBegin.endInstructions { + print("Overlap kind:", range.overlaps(pathBegin: pathInst, pathEnd: end, context)) + } + return + } + if let pathValue = pathInst as? SingleValueInstruction, pathValue.ownership == .owned { + for end in pathValue.uses.endingLifetime { + print("Overlap kind:", range.overlaps(pathBegin: pathInst, pathEnd: end.instruction, context)) + } + return + } + print("Test specification error: not a scoped or owned instruction: \(pathInst)") +} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/SSAUpdater.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/SSAUpdater.swift deleted file mode 100644 index 7b1d0f88c4d28..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/SSAUpdater.swift +++ /dev/null @@ -1,53 +0,0 @@ -//===--- SSAUpdater.swift -------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 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 SIL -import OptimizerBridging - -/// Utility for updating SSA for a set of SIL instructions defined in multiple blocks. -struct SSAUpdater { - let context: Context - - init(function: Function, type: Type, ownership: Ownership, - _ context: Context) { - self.context = context - context._bridged.SSAUpdater_initialize(function.bridged, type.bridged, - ownership._bridged) - } - - mutating func addAvailableValue(_ value: Value, in block: BasicBlock) { - context._bridged.SSAUpdater_addAvailableValue(block.bridged, value.bridged) - } - - /// Construct SSA for a value that is live at the *end* of a basic block. - mutating func getValue(atEndOf block: BasicBlock) -> Value { - context.notifyInstructionsChanged() - return context._bridged.SSAUpdater_getValueAtEndOfBlock(block.bridged).value - } - - /// Construct SSA for a value that is live in the *middle* of a block. - /// This handles the case where the use is before a definition of the value in the same block. - /// - /// bb1: - /// %1 = def - /// br bb2 - /// bb2: - /// = use(?) - /// %2 = def - /// cond_br bb2, bb3 - /// - /// In this case we need to insert a phi argument in bb2, merging %1 and %2. - mutating func getValue(inMiddleOf block: BasicBlock) -> Value { - context.notifyInstructionsChanged() - return context._bridged.SSAUpdater_getValueInMiddleOfBlock(block.bridged).value - } -} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/SpecializationCloner.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/SpecializationCloner.swift deleted file mode 100644 index 28a8240d46998..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/SpecializationCloner.swift +++ /dev/null @@ -1,49 +0,0 @@ -//===--- SpecializationCloner.swift --------------------------------------------==// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2024 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 OptimizerBridging -import SIL - -/// Utility cloner type that can be used by optimizations that generate new functions or specialized versions of -/// existing functions. -struct SpecializationCloner { - private let bridged: BridgedSpecializationCloner - let context: FunctionPassContext - - init(emptySpecializedFunction: Function, _ context: FunctionPassContext) { - self.bridged = BridgedSpecializationCloner(emptySpecializedFunction.bridged) - self.context = context - } - - var cloned: Function { - bridged.getCloned().function - } - - var entryBlock: BasicBlock { - if cloned.blocks.isEmpty { - return cloned.appendNewBlock(context) - } else { - return cloned.entryBlock - } - } - - func getClonedBlock(for originalBlock: BasicBlock) -> BasicBlock { - bridged.getClonedBasicBlock(originalBlock.bridged).block - } - - func cloneFunctionBody(from originalFunction: Function, entryBlockArguments: [Value]) { - entryBlockArguments.withBridgedValues { bridgedEntryBlockArgs in - bridged.cloneFunctionBody(originalFunction.bridged, self.entryBlock.bridged, bridgedEntryBlockArgs) - } - } - -} \ No newline at end of file diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/StaticInitCloner.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/StaticInitCloner.swift deleted file mode 100644 index dcc9853052c6a..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/StaticInitCloner.swift +++ /dev/null @@ -1,78 +0,0 @@ -//===--- StaticInitCloner.swift --------------------------------------------==// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 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 SIL -import OptimizerBridging - -/// Clones the initializer value of a GlobalVariable. -/// -/// Used to transitively clone "constant" instructions, including their operands, -/// from or to the static initializer value of a GlobalVariable. -/// -struct StaticInitCloner { - private var bridged: BridgedCloner - private let context: Context - private let cloningIntoFunction: Bool - - init(cloneTo global: GlobalVariable, _ context: Context) { - self.bridged = BridgedCloner(global.bridged, context._bridged) - self.context = context - self.cloningIntoFunction = false - } - - init(cloneBefore inst: Instruction, _ context: Context) { - self.bridged = BridgedCloner(inst.bridged, context._bridged) - self.context = context - self.cloningIntoFunction = true - } - - mutating func deinitialize() { - bridged.destroy(context._bridged) - } - - /// Transitively clones `value` including its defining instruction's operands. - mutating func clone(_ value: Value) -> Value { - - if isCloned(value: value) { - return getClonedValue(of: value) - } - - if let beginAccess = value as? BeginAccessInst { - // Skip access instructions, which might be generated for UnsafePointer globals which point to other globals. - let clonedOperand = clone(beginAccess.address) - bridged.recordFoldedValue(beginAccess.bridged, clonedOperand.bridged) - return clonedOperand - } - - let inst = value.definingInstruction! - assert(!(inst is ScopedInstruction), "global init value must not contain a scoped instruction") - - for op in inst.operands { - _ = clone(op.value) - } - - bridged.clone(inst.bridged) - let clonedValue = getClonedValue(of: value) - if cloningIntoFunction { - context.notifyInstructionChanged(clonedValue.definingInstruction!) - } - return clonedValue - } - - mutating func getClonedValue(of value: Value) -> Value { - bridged.getClonedValue(value.bridged).value - } - - func isCloned(value: Value) -> Bool { - bridged.isValueCloned(value.bridged) - } -} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift deleted file mode 100644 index 101beb34b03dc..0000000000000 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift +++ /dev/null @@ -1,276 +0,0 @@ -//===----------- Test.swift - In-IR tests from Swift source ---------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 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 -// -//===----------------------------------------------------------------------===// -// -// TO ADD A NEW TEST, just add a new FunctionTest instance. -// - In the source file containing the functionality you want to test: -// let myNewTest = -// FunctionTest("my_new_test") { function, arguments, context in -// } -// - In SwiftCompilerSources/Sources/SIL/Test.swift's registerOptimizerTests -// function, add a new argument to the variadic function: -// registerFunctionTests(..., myNewTest) -// -//===----------------------------------------------------------------------===// -// -// TO TROUBLESHOOT A NEW TEST, consider the following scenarios: -// - PROBLEM: The test isn't running on PLATFORM and the failure says -// -// Found no test named my_new_test. -// -// SOLUTION: Is this a platform one that doesn't use SwiftCompilerSources -// (e.g. Windows)? Then add -// -// // REQUIRES: swift_in_compiler -// -// to the test file. -// EXPLANATION: The tests written within SwiftCompilerSources only get built -// and registered on platforms where the SwiftCompilerSources are -// built and used. -// -//===----------------------------------------------------------------------===// -// -// Provides a mechanism for writing tests against compiler code in the context -// of a function. The goal is to get the same effect as calling a function and -// checking its output. -// -// This is done via the specify_test instruction. Using one or more instances -// of it in your test case's SIL function, you can specify which test (instance -// of FunctionTest) should be run and what arguments should be provided to it. -// For full details of the specify_test instruction's grammar, see SIL.rst. -// -// The test grabs the arguments it expects out of the TestArguments instance -// it is provided. It calls some function or functions. It then prints out -// interesting results. These results can then be FileCheck'd. -// -// CASE STUDY: -// Here's an example of how it works: -// 0) A source file, NeatUtils.swift contains -// -// fileprivate func myNeatoUtility(int: Int, value: Value, function: Function) { ... } -// -// and -// -// let myNeatoUtilityTest = -// FunctionTest("my_neato_utility") { function, arguments, test in -// // The code here is described in detail below. -// // See 4). -// let count = arguments.takeInt() -// let target = arguments.takeValue() -// let callee = arguments.takeFunction() -// // See 5) -// myNeatoUtility(int: count, value: target, function: callee) -// // See 6) -// print(function) -// } -// 1) A test test/SILOptimizer/interesting_functionality_unit.sil runs the -// TestRunner pass: -// // RUN: %target-sil-opt -test-runner %s -o /dev/null 2>&1 | %FileCheck %s -// // REQUIRES: swift_in_compiler -// 2) A function in interesting_functionality_unit.sil contains the -// specify_test instruction. -// sil @f : $() -> () { -// ... -// specify_test "my_neato_utility 43 %2 @function[other_fun]" -// ... -// } -// 3) TestRunner finds the FunctionTest instance myNeatoUtilityTest registered -// under the name "my_neato_utility", and calls run() on it, passing the -// passing first the function, last the FunctionTest instance, AND in the -// middle, most importantly a TestArguments instance that contains -// -// (43 : Int, someValue : Value, other_fun : Function) -// -// 4) myNeatoUtilityTest calls takeUInt(), takeValue(), and takeFunction() on -// the test::Arguments instance. -// let count = arguments.takeInt() -// let target = arguments.takeValue() -// let callee = arguments.takeFunction() -// 5) myNeatoUtilityTest calls myNeatoUtility, passing these values along. -// myNeatoUtility(int: count, value: target, function: callee) -// 6) myNeatoUtilityTest then dumps out the current function, which was modified -// in the process. -// print(function) -// 7) The test file test/SILOptimizer/interesting_functionality_unit.sil matches -// the -// expected contents of the modified function: -// // CHECK-LABEL: sil @f -// // CHECK-NOT: function_ref @other_fun -// -//===----------------------------------------------------------------------===// - -import Basic -import SIL -import SILBridging -import OptimizerBridging - -/// The primary interface to in-IR tests. -struct FunctionTest { - let name: String - let invocation: FunctionTestInvocation - - public init(_ name: String, invocation: @escaping FunctionTestInvocation) { - self.name = name - self.invocation = invocation - } -} - -/// The type of the closure passed to a FunctionTest. -typealias FunctionTestInvocation = @convention(thin) (Function, TestArguments, FunctionPassContext) -> () - -/// Wraps the arguments specified in the specify_test instruction. -public struct TestArguments { - public var bridged: BridgedTestArguments - fileprivate init(bridged: BridgedTestArguments) { - self.bridged = bridged - } - - public var hasUntaken: Bool { bridged.hasUntaken() } - public func takeString() -> StringRef { StringRef(bridged: bridged.takeString()) } - public func takeBool() -> Bool { bridged.takeBool() } - public func takeInt() -> Int { bridged.takeInt() } - public func takeOperand() -> Operand { Operand(bridged: bridged.takeOperand()) } - public func takeValue() -> Value { bridged.takeValue().value } - public func takeInstruction() -> Instruction { bridged.takeInstruction().instruction } - public func takeArgument() -> Argument { bridged.takeArgument().argument } - public func takeBlock() -> BasicBlock { bridged.takeBlock().block } - public func takeFunction() -> Function { bridged.takeFunction().function } -} - -extension BridgedTestArguments { - public var native: TestArguments { TestArguments(bridged: self) } -} - -/// Registration of each test in the SIL module. -public func registerOptimizerTests() { - // Register each test. - registerFunctionTests( - getAccessBaseTest, - addressOwnershipLiveRangeTest, - argumentConventionsTest, - borrowIntroducersTest, - enclosingValuesTest, - forwardingDefUseTest, - forwardingUseDefTest, - interiorLivenessTest, - lifetimeDependenceScopeTest, - lifetimeDependenceUseTest, - linearLivenessTest, - parseTestSpecificationTest, - variableIntroducerTest, - gatherCallSitesTest, - specializedFunctionSignatureAndBodyTest, - rewrittenCallerBodyTest - ) - - // Finally register the thunk they all call through. - registerFunctionTestThunk(functionTestThunk) -} - -private func registerFunctionTests(_ tests: FunctionTest...) { - tests.forEach { registerFunctionTest($0) } -} - -private func registerFunctionTest(_ test: FunctionTest) { - test.name._withBridgedStringRef { ref in - registerFunctionTest(ref, castToOpaquePointer(fromInvocation: test.invocation)) - } -} - -/// The function called by the swift::test::FunctionTest which invokes the -/// actual test function. -/// -/// This function is necessary because tests need to be written in terms of -/// native Swift types (Function, TestArguments, BridgedPassContext) -/// rather than their bridged variants, but such a function isn't representable -/// in C++. This thunk unwraps the bridged types and invokes the real -/// function. -private func functionTestThunk( - _ erasedInvocation: UnsafeMutableRawPointer, - _ function: BridgedFunction, - _ arguments: BridgedTestArguments, - _ passInvocation: BridgedSwiftPassInvocation) { - let invocation = castToInvocation(fromOpaquePointer: erasedInvocation) - let context = FunctionPassContext(_bridged: BridgedPassContext(invocation: passInvocation.invocation)) - invocation(function.function, arguments.native, context) -} - -/// Bitcast a thin test closure to void *. -/// -/// Needed so that the closure can be represented in C++ for storage in the test -/// registry. -private func castToOpaquePointer(fromInvocation invocation: FunctionTestInvocation) -> UnsafeMutableRawPointer { - return unsafeBitCast(invocation, to: UnsafeMutableRawPointer.self) -} - -/// Bitcast a void * to a thin test closure. -/// -/// Needed so that the closure stored in the C++ test registry can be invoked -/// via the functionTestThunk. -private func castToInvocation(fromOpaquePointer erasedInvocation: UnsafeMutableRawPointer) -> FunctionTestInvocation { - return unsafeBitCast(erasedInvocation, to: FunctionTestInvocation.self) -} - -// Arguments: -// - string: list of characters, each of which specifies subsequent arguments -// - A: (block) argument -// - F: function -// - B: block -// - I: instruction -// - V: value -// - O: operand -// - b: boolean -// - u: unsigned -// - s: string -// - ... -// - an argument of the type specified in the initial string -// - ... -// Dumps: -// - for each argument (after the initial string) -// - its type -// - something to identify the instance (mostly this means calling dump) -let parseTestSpecificationTest = -FunctionTest("test_specification_parsing") { function, arguments, context in - let expectedFields = arguments.takeString() - for expectedField in expectedFields.string { - switch expectedField { - case "A": - let argument = arguments.takeArgument() - print("argument:\n\(argument)") - case "F": - let function = arguments.takeFunction() - print("function: \(function.name)") - case "B": - let block = arguments.takeBlock() - print("block:\n\(block)") - case "I": - let instruction = arguments.takeInstruction() - print("instruction: \(instruction)") - case "V": - let value = arguments.takeValue() - print("value: \(value)") - case "O": - let operand = arguments.takeOperand() - print("operand: \(operand)") - case "u": - let u = arguments.takeInt() - print("uint: \(u)") - case "b": - let b = arguments.takeBool() - print("bool: \(b)") - case "s": - let s = arguments.takeString() - print("string: \(s)") - default: - fatalError("unknown field type was expected?!"); - } - } -} diff --git a/SwiftCompilerSources/Sources/SIL/ASTExtensions.swift b/SwiftCompilerSources/Sources/SIL/ASTExtensions.swift index f7aa212bfe8a2..f02ab08bef4a8 100644 --- a/SwiftCompilerSources/Sources/SIL/ASTExtensions.swift +++ b/SwiftCompilerSources/Sources/SIL/ASTExtensions.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2025 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,6 +31,11 @@ extension CanonicalType { public var silType: Type? { BridgedType.createSILType(bridged).typeOrNil } + + public func getBoxFields(in function: Function) -> BoxFieldsArray { + precondition(isBox) + return BoxFieldsArray(boxType: self, function: function) + } } extension Decl { @@ -50,7 +55,42 @@ extension ClassDecl { } extension SubstitutionMap { - public func getMethodSubstitutions(for method: Function) -> SubstitutionMap { - return SubstitutionMap(bridged: method.bridged.getMethodSubstitutions(bridged)) + /// Returns the substitutions to specialize a method. + /// + /// If this is a default witness methods (`selfType` != nil) it has generic self type. In this case + /// the generic self parameter is at depth 0 and the actual generic parameters of the substitution map + /// are at depth + 1, e.g: + /// ``` + /// @convention(witness_method: P) <τ_0_0><τ_1_0 where τ_0_0 : GenClass<τ_1_0>.T> + /// ^ ^ + /// self params of substitution map at depth + 1 + /// ``` + public func getMethodSubstitutions(for method: Function, selfType: CanonicalType? = nil) -> SubstitutionMap { + return SubstitutionMap(bridged: method.bridged.getMethodSubstitutions(bridged, + selfType?.bridged ?? BridgedCanType())) + } +} + +extension Conformance { + /// Returns true if the conformance is not isolated or if its isolation matches + /// the isolation in `function`. + public func matchesActorIsolation(in function: Function) -> Bool { + return function.bridged.conformanceMatchesActorIsolation(bridged) + } +} + +extension DiagnosticEngine { + public func diagnose(_ id: DiagID, _ args: DiagnosticArgument..., at location: Location) { + diagnose(id, args, at: location.getSourceLocation(diagnosticEngine: self)) + } + + public func diagnose(_ id: DiagID, _ args: [DiagnosticArgument], at location: Location) { + diagnose(id, args, at: location.getSourceLocation(diagnosticEngine: self)) + } +} + +extension Diagnostic where SourceLocation == Location { + public init(_ id: DiagID, _ arguments: DiagnosticArgument..., at location: Location) { + self.init(id, arguments, at: location) } } diff --git a/SwiftCompilerSources/Sources/SIL/ApplySite.swift b/SwiftCompilerSources/Sources/SIL/ApplySite.swift index a8bf3a07fb3bf..7f0187819e097 100644 --- a/SwiftCompilerSources/Sources/SIL/ApplySite.swift +++ b/SwiftCompilerSources/Sources/SIL/ApplySite.swift @@ -72,6 +72,7 @@ public struct ApplyOperandConventions : Collection { calleeArgumentIndex(ofOperandIndex: operandIndex)!] } + // If the specified parameter is a dependency target, return its dependency sources. public subscript(parameterDependencies operandIndex: Int) -> FunctionConvention.LifetimeDependencies? { return calleeArgumentConventions[parameterDependencies: @@ -229,6 +230,13 @@ extension ApplySite { functionConvention.resultDependencies != nil } + public func isAddressable(operand: Operand) -> Bool { + if let dep = resultDependence(on: operand) { + return dep.isAddressable(for: operand.value) + } + return false + } + public var hasLifetimeDependence: Bool { functionConvention.hasLifetimeDependencies() } @@ -270,18 +278,35 @@ extension ApplySite { return argumentOperands[callerArgIdx] } - /// Returns the argument index of an operand. + /// Returns the argument of `operand` in a callee function. /// - /// Returns nil if 'operand' is not an argument operand. This is the case if + /// Returns nil if `operand` is not an argument operand. This is the case if /// it's the callee function operand. + public func calleeArgument(of operand: Operand, in callee: Function) -> FunctionArgument? { + if let argIdx = calleeArgumentIndex(of: operand) { + return callee.arguments[argIdx] + } + return nil + } + + /// Returns the argument index of an operand. /// - /// Warning: the returned integer can be misused as an index into - /// the wrong collection. Replace uses of this API with safer APIs. + /// Returns nil if `operand` is not an argument operand. This is the case if + /// it's the callee function operand. /// - /// TODO: delete this API and rewrite the users. + /// Warning: the returned integer can be misused as an index into the wrong collection. + /// Use `calleeArgument(of:,in:)` if possible. public func calleeArgumentIndex(of operand: Operand) -> Int? { operandConventions.calleeArgumentIndex(of: operand) } + + public var hasGuaranteedResult: Bool { + functionConvention.hasGuaranteedResult + } + + public var hasGuaranteedAddressResult: Bool { + functionConvention.hasGuaranteedAddressResult + } } extension ApplySite { diff --git a/SwiftCompilerSources/Sources/SIL/Argument.swift b/SwiftCompilerSources/Sources/SIL/Argument.swift index 26c6bed560e4d..febd27f9af5dc 100644 --- a/SwiftCompilerSources/Sources/SIL/Argument.swift +++ b/SwiftCompilerSources/Sources/SIL/Argument.swift @@ -32,11 +32,23 @@ public class Argument : Value, Hashable { public var isReborrow: Bool { bridged.isReborrow() } + public func set(reborrow: Bool, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.setReborrow(reborrow) + } + public var isLexical: Bool { false } - public var varDecl: VarDecl? { bridged.getVarDecl().getAs(VarDecl.self) } + public var decl: ValueDecl? { bridged.getDecl().getAs(ValueDecl.self) } + + public func findVarDecl() -> VarDecl? { + if let varDecl = decl as? VarDecl { + return varDecl + } + return findVarDeclFromDebugUsers() + } - public var sourceLoc: SourceLoc? { varDecl?.nameLoc } + public var sourceLoc: SourceLoc? { findVarDecl()?.nameLoc } public static func ==(lhs: Argument, rhs: Argument) -> Bool { lhs === rhs @@ -56,6 +68,10 @@ final public class FunctionArgument : Argument { bridged.FunctionArgument_isLexical() } + public var isClosureCapture: Bool { + bridged.FunctionArgument_isClosureCapture() + } + public var isSelf: Bool { parentFunction.argumentConventions.selfIndex == index } @@ -78,7 +94,8 @@ final public class FunctionArgument : Argument { /// 2. lifetimeAnnotation /// 3. closureCapture /// 4. parameterPack - public func copyFlags(from arg: FunctionArgument) { + public func copyFlags(from arg: FunctionArgument, _ context: some MutatingContext) { + context.notifyInstructionsChanged() bridged.copyFlags(arg.bridged) } } @@ -153,7 +170,7 @@ public struct Phi { } public var incomingValues: LazyMapSequence, Value> { - incomingOperands.lazy.map { $0.value } + incomingOperands.values } public var isReborrow: Bool { value.isReborrow } @@ -192,6 +209,22 @@ public struct Phi { } } +extension Phi { + /// Return true of this phi is directly returned with no side effects between the phi and the return. + public var isReturnValue: Bool { + if let singleUse = value.uses.singleUse, let ret = singleUse.instruction as? ReturnInst, + ret.parentBlock == successor { + for inst in successor.instructions { + if inst.mayHaveSideEffects { + return false + } + } + return true + } + return false + } +} + extension Operand { public var forwardingBorrowedFromUser: BorrowedFromInst? { if let bfi = instruction as? BorrowedFromInst, index == 0 { @@ -260,7 +293,7 @@ public struct ArgumentConventions : Collection, CustomStringConvertible { if let paramIdx = parameterIndex(for: argumentIndex) { return convention.parameters[paramIdx].convention } - let resultInfo = convention.indirectSILResults[argumentIndex] + let resultInfo = convention.indirectSILResult(at: argumentIndex) return ArgumentConvention(result: resultInfo.convention) } @@ -268,7 +301,7 @@ public struct ArgumentConventions : Collection, CustomStringConvertible { if parameterIndex(for: argumentIndex) != nil { return nil } - return convention.indirectSILResults[argumentIndex] + return convention.indirectSILResult(at: argumentIndex) } public subscript(parameter argumentIndex: Int) -> ParameterInfo? { @@ -455,6 +488,8 @@ public enum ArgumentConvention : CustomStringConvertible { self = .directUnowned case .pack: self = .packOut + case .guaranteed, .guaranteedAddress: + fatalError("Result conventions @guaranteed and @guaranteed_addr are always returned directly") } } @@ -591,19 +626,6 @@ public enum ArgumentConvention : CustomStringConvertible { } } -extension BeginAccessInst.AccessKind { - public func isCompatible(with convention: ArgumentConvention) -> Bool { - switch self { - case .`init`, .deinit: - return false - case .read: - return convention.isIndirectIn - case .modify: - return convention.isInout - } - } -} - // Bridging utilities extension BridgedArgument { diff --git a/SwiftCompilerSources/Sources/SIL/BasicBlock.swift b/SwiftCompilerSources/Sources/SIL/BasicBlock.swift index 3d9613c085adc..c543559c24489 100644 --- a/SwiftCompilerSources/Sources/SIL/BasicBlock.swift +++ b/SwiftCompilerSources/Sources/SIL/BasicBlock.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -import Basic +import AST import SILBridging @_semantics("arc.immortal") @@ -25,8 +25,69 @@ final public class BasicBlock : CustomStringConvertible, HasShortDescription, Ha } public var shortDescription: String { name } + /// The index of the basic block in its function. + /// This has O(n) complexity. Only use it for debugging + public var index: Int { + for (idx, block) in parentFunction.blocks.enumerated() { + if block == self { return idx } + } + fatalError() + } + + public var name: String { "bb\(index)" } + + public static func == (lhs: BasicBlock, rhs: BasicBlock) -> Bool { lhs === rhs } + + public func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } + + public var bridged: BridgedBasicBlock { BridgedBasicBlock(SwiftObject(self)) } + + //===----------------------------------------------------------------------===// + // Arguments + //===----------------------------------------------------------------------===// + public var arguments: ArgumentArray { ArgumentArray(block: self) } + public func addArgument(type: Type, ownership: Ownership, _ context: some MutatingContext) -> Argument { + context.notifyInstructionsChanged() + return bridged.addBlockArgument(type.bridged, ownership._bridged).argument + } + + public func addFunctionArgument(type: Type, _ context: some MutatingContext) -> FunctionArgument { + context.notifyInstructionsChanged() + return bridged.addFunctionArgument(type.bridged).argument as! FunctionArgument + } + + public func insertFunctionArgument(atPosition: Int, type: Type, ownership: Ownership, decl: ValueDecl? = nil, + _ context: some MutatingContext) -> FunctionArgument + { + context.notifyInstructionsChanged() + return bridged.insertFunctionArgument(atPosition, type.bridged, ownership._bridged, + (decl as Decl?).bridged).argument as! FunctionArgument + } + + public func eraseArgument(at index: Int, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.eraseArgument(index) + } + + public func eraseAllArguments(_ context: some MutatingContext) { + // Arguments are stored in an array. We need to erase in reverse order to avoid quadratic complexity. + for argIdx in (0 ..< arguments.count).reversed() { + eraseArgument(at: argIdx, context) + } + } + + public func moveAllArguments(to otherBlock: BasicBlock, _ context: some MutatingContext) { + bridged.moveArgumentsTo(otherBlock.bridged) + } + + //===----------------------------------------------------------------------===// + // Instructions + //===----------------------------------------------------------------------===// + public var instructions: InstructionList { InstructionList(first: bridged.getFirstInst().instruction) } @@ -35,6 +96,22 @@ final public class BasicBlock : CustomStringConvertible, HasShortDescription, Ha bridged.getLastInst().instruction as! TermInst } + public func moveAllInstructions(toBeginOf otherBlock: BasicBlock, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + context.notifyBranchesChanged() + bridged.moveAllInstructionsToBegin(otherBlock.bridged) + } + + public func moveAllInstructions(toEndOf otherBlock: BasicBlock, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + context.notifyBranchesChanged() + bridged.moveAllInstructionsToEnd(otherBlock.bridged) + } + + //===----------------------------------------------------------------------===// + // predecessors and successors + //===----------------------------------------------------------------------===// + public var successors: SuccessorArray { terminator.successors } public var predecessors: PredecessorList { @@ -76,25 +153,14 @@ final public class BasicBlock : CustomStringConvertible, HasShortDescription, Ha return false } } - - /// The index of the basic block in its function. - /// This has O(n) complexity. Only use it for debugging - public var index: Int { - for (idx, block) in parentFunction.blocks.enumerated() { - if block == self { return idx } + + public func isCriticalEdge(edgeIndex: Int) -> Bool { + if terminator.successors.count <= 1 { + return false + } else { + return !terminator.successors[edgeIndex].hasSinglePredecessor } - fatalError() - } - - public var name: String { "bb\(index)" } - - public static func == (lhs: BasicBlock, rhs: BasicBlock) -> Bool { lhs === rhs } - - public func hash(into hasher: inout Hasher) { - hasher.combine(ObjectIdentifier(self)) } - - public var bridged: BridgedBasicBlock { BridgedBasicBlock(SwiftObject(self)) } } /// The list of instructions in a BasicBlock. diff --git a/SwiftCompilerSources/Sources/SIL/Builder.swift b/SwiftCompilerSources/Sources/SIL/Builder.swift index fa4b3e1725e0d..a452695eca9b8 100644 --- a/SwiftCompilerSources/Sources/SIL/Builder.swift +++ b/SwiftCompilerSources/Sources/SIL/Builder.swift @@ -26,9 +26,90 @@ public struct Builder { public let insertionPoint: InsertionPoint let location: Location - private let notificationHandler: BridgedChangeNotificationHandler + private let notificationHandler: BridgedContext private let notifyNewInstruction: (Instruction) -> () + /// Creates a builder which inserts _before_ `insPnt`, using a custom `location`. + public init(before insPnt: Instruction, location: Location, _ context: some MutatingContext) { + context.verifyIsTransforming(function: insPnt.parentFunction) + self.init(insertAt: .before(insPnt), location: location, context.notifyInstructionChanged, context._bridged) + } + + /// Creates a builder which inserts before `insPnt`, using `insPnt`'s next + /// non-meta instruction's location. + /// This function should be used when moving code to an unknown insert point, + /// when we want to inherit the location of the closest non-meta instruction. + /// For replacing an existing meta instruction with another, use + /// ``Builder.init(replacing:_:)``. + public init(before insPnt: Instruction, _ context: some MutatingContext) { + context.verifyIsTransforming(function: insPnt.parentFunction) + self.init(insertAt: .before(insPnt), location: insPnt.location, + context.notifyInstructionChanged, context._bridged) + } + + /// Creates a builder which inserts _before_ `insPnt`, using the exact location of `insPnt`, + /// for the purpose of replacing that meta instruction with an equivalent instruction. + /// This function does not delete `insPnt`. + public init(replacing insPnt: MetaInstruction, _ context: some MutatingContext) { + context.verifyIsTransforming(function: insPnt.parentFunction) + self.init(insertAt: .before(insPnt), location: insPnt.location, context.notifyInstructionChanged, context._bridged) + } + + /// Creates a builder which inserts _after_ `insPnt`, using a custom `location`. + /// + /// TODO: this is usually incorrect for terminator instructions. Instead use + /// `Builder.insert(after:location:_:insertFunc)` from OptUtils.swift. Rename this to afterNonTerminator. + public init(after insPnt: Instruction, location: Location, _ context: some MutatingContext) { + context.verifyIsTransforming(function: insPnt.parentFunction) + guard let nextInst = insPnt.next else { + fatalError("cannot insert an instruction after a block terminator.") + } + self.init(insertAt: .before(nextInst), location: location, context.notifyInstructionChanged, context._bridged) + } + + /// Creates a builder which inserts _after_ `insPnt`, using `insPnt`'s next + /// non-meta instruction's location. + /// + /// TODO: this is incorrect for terminator instructions. Instead use `Builder.insert(after:location:_:insertFunc)` + /// from OptUtils.swift. Rename this to afterNonTerminator. + public init(after insPnt: Instruction, _ context: some MutatingContext) { + self.init(after: insPnt, location: insPnt.location, context) + } + + /// Creates a builder which inserts at the end of `block`, using a custom `location`. + public init(atEndOf block: BasicBlock, location: Location, _ context: some MutatingContext) { + context.verifyIsTransforming(function: block.parentFunction) + self.init(insertAt: .atEndOf(block), location: location, context.notifyInstructionChanged, context._bridged) + } + + /// Creates a builder which inserts at the begin of `block`, using a custom `location`. + public init(atBeginOf block: BasicBlock, location: Location, _ context: some MutatingContext) { + context.verifyIsTransforming(function: block.parentFunction) + let firstInst = block.instructions.first! + self.init(insertAt: .before(firstInst), location: location, context.notifyInstructionChanged, context._bridged) + } + + /// Creates a builder which inserts at the begin of `block`, using the location of the first + /// non-meta instruction of `block`. + public init(atBeginOf block: BasicBlock, _ context: some MutatingContext) { + context.verifyIsTransforming(function: block.parentFunction) + let firstInst = block.instructions.first! + self.init(insertAt: .before(firstInst), + location: firstInst.location, context.notifyInstructionChanged, context._bridged) + } + + /// Creates a builder which inserts instructions into an empty function, using the location of the function itself. + public init(atStartOf function: Function, _ context: some MutatingContext) { + context.verifyIsTransforming(function: function) + self.init(insertAt: .atStartOf(function), location: function.location, + context.notifyInstructionChanged, context._bridged) + } + + public init(staticInitializerOf global: GlobalVariable, _ context: some MutatingContext) { + self.init(insertAt: .staticInitializer(global), + location: Location.artificialUnreachableLocation, { _ in }, context._bridged) + } + /// Return 'nil' when inserting at the start of a function or in a global initializer. public var insertionBlock: BasicBlock? { switch insertionPoint { @@ -59,20 +140,20 @@ public struct Builder { } private func notifyNew(_ instruction: I) -> I { - notificationHandler.notifyChanges(.instructionsChanged) + notificationHandler.notifyChanges(.Instructions) if instruction is FullApplySite { - notificationHandler.notifyChanges(.callsChanged) + notificationHandler.notifyChanges(.Calls) } if instruction is TermInst { - notificationHandler.notifyChanges(.branchesChanged) + notificationHandler.notifyChanges(.Branches) } notifyNewInstruction(instruction) return instruction } - public init(insertAt: InsertionPoint, location: Location, + private init(insertAt: InsertionPoint, location: Location, _ notifyNewInstruction: @escaping (Instruction) -> (), - _ notificationHandler: BridgedChangeNotificationHandler) { + _ notificationHandler: BridgedContext) { self.insertionPoint = insertAt self.location = location; self.notifyNewInstruction = notifyNewInstruction @@ -109,11 +190,40 @@ public struct Builder { } } - public func createIntegerLiteral(_ value: Int, type: Type) -> IntegerLiteralInst { - let literal = bridged.createIntegerLiteral(type.bridged, value) + /// Creates a integer literal instruction with the given integer value and + /// type. If an extension is necessary, the value is extended in accordance + /// with the signedness of `Value`. + public func createIntegerLiteral( + _ value: Value, + type: Type + ) -> IntegerLiteralInst { + precondition( + Value.bitWidth <= Int.bitWidth, + "Cannot fit \(Value.bitWidth)-bit integer into \(Int.bitWidth)-bit 'Swift.Int'" + ) + // Extend the value based on its signedness to the bit width of `Int` and + // reinterpret it as an `Int`. + let extendedValue: Int = + if Value.isSigned { + Int(value) + } else { + // NB: This initializer is effectively a generic equivalent + // of `Int(bitPattern:)` + Int(truncatingIfNeeded: value) + } + + let literal = bridged.createIntegerLiteral(type.bridged, extendedValue, Value.isSigned) return notifyNew(literal.getAs(IntegerLiteralInst.self)) } - + + /// Creates a `Builtin.Int1` integer literal instruction with a value + /// corresponding to the given Boolean. + public func createBoolLiteral(_ value: Bool) -> IntegerLiteralInst { + let boolType = notificationHandler.getBuiltinIntegerType(1).type + let integerValue: UInt = value ? 1 : 0 + return createIntegerLiteral(integerValue, type: boolType) + } + public func createAllocRef(_ type: Type, isObjC: Bool = false, canAllocOnStack: Bool = false, isBare: Bool = false, tailAllocatedTypes: TypeArray, tailAllocatedCounts: [Value]) -> AllocRefInst { return tailAllocatedCounts.withBridgedValues { countsRef in @@ -122,12 +232,20 @@ public struct Builder { } } - public func createAllocStack(_ type: Type, hasDynamicLifetime: Bool = false, + public func createAllocStack(_ type: Type, + debugVariable: DebugVariableInstruction.DebugVariable? = nil, + hasDynamicLifetime: Bool = false, isLexical: Bool = false, isFromVarDecl: Bool = false, usesMoveableValueDebugInfo: Bool = false) -> AllocStackInst { - let dr = bridged.createAllocStack(type.bridged, hasDynamicLifetime, isLexical, - isFromVarDecl, usesMoveableValueDebugInfo) - return notifyNew(dr.getAs(AllocStackInst.self)) + let allocStack: BridgedInstruction + if let debugVariable = debugVariable { + allocStack = bridged.createAllocStack(type.bridged, debugVariable, hasDynamicLifetime, isLexical, + isFromVarDecl, usesMoveableValueDebugInfo) + } else { + allocStack = bridged.createAllocStack(type.bridged, hasDynamicLifetime, isLexical, + isFromVarDecl, usesMoveableValueDebugInfo) + } + return notifyNew(allocStack.getAs(AllocStackInst.self)) } @discardableResult @@ -171,6 +289,11 @@ public struct Builder { return notifyNew(cast.getAs(UncheckedAddrCastInst.self)) } + public func createUncheckedValueCast(from value: Value, to type: Type) -> UncheckedValueCastInst { + let cast = bridged.createUncheckedValueCast(value.bridged, type.bridged) + return notifyNew(cast.getAs(UncheckedValueCastInst.self)) + } + public func createUpcast(from value: Value, to type: Type) -> UpcastInst { let cast = bridged.createUpcast(value.bridged, type.bridged) return notifyNew(cast.getAs(UpcastInst.self)) @@ -180,7 +303,7 @@ public struct Builder { public func createCheckedCastAddrBranch( source: Value, sourceFormalType: CanonicalType, destination: Value, targetFormalType: CanonicalType, - isolatedConformances: CastingIsolatedConformances, + options: CheckedCastInstOptions, consumptionKind: CheckedCastAddrBranchInst.CastConsumptionKind, successBlock: BasicBlock, failureBlock: BasicBlock @@ -195,7 +318,7 @@ public struct Builder { let cast = bridged.createCheckedCastAddrBranch(source.bridged, sourceFormalType.bridged, destination.bridged, targetFormalType.bridged, - isolatedConformances.bridged, + options.bridged, bridgedConsumption, successBlock.bridged, failureBlock.bridged) return notifyNew(cast.getAs(CheckedCastAddrBranchInst.self)) @@ -203,18 +326,24 @@ public struct Builder { @discardableResult public func createUnconditionalCheckedCastAddr( - isolatedConformances: CastingIsolatedConformances, + options: CheckedCastInstOptions, source: Value, sourceFormalType: CanonicalType, destination: Value, targetFormalType: CanonicalType ) -> UnconditionalCheckedCastAddrInst { let cast = bridged.createUnconditionalCheckedCastAddr( - isolatedConformances.bridged, source.bridged, - sourceFormalType.bridged, + options.bridged, source.bridged, sourceFormalType.bridged, destination.bridged, targetFormalType.bridged ) return notifyNew(cast.getAs(UnconditionalCheckedCastAddrInst.self)) } + public func createUncheckedOwnershipConversion( + operand: Value, resultOwnership: Ownership + ) -> UncheckedOwnershipConversionInst { + let uoc = bridged.createUncheckedOwnershipConversion(operand.bridged, resultOwnership._bridged) + return notifyNew(uoc.getAs(UncheckedOwnershipConversionInst.self)) + } + public func createLoad(fromAddress: Value, ownership: LoadInst.LoadOwnership) -> LoadInst { let load = bridged.createLoad(fromAddress.bridged, ownership.rawValue) return notifyNew(load.getAs(LoadInst.self)) @@ -368,6 +497,20 @@ public struct Builder { return notifyNew(apply.getAs(TryApplyInst.self)) } + public func createBeginApply(function: Value, + _ substitutionMap: SubstitutionMap, + arguments: [Value], + isNonThrowing: Bool = false, + isNonAsync: Bool = false, + specializationInfo: ApplyInst.SpecializationInfo = ApplyInst.SpecializationInfo() + ) -> BeginApplyInst { + let apply = arguments.withBridgedValues { valuesRef in + bridged.createBeginApply(function.bridged, substitutionMap.bridged, valuesRef, + isNonThrowing, isNonAsync, specializationInfo) + } + return notifyNew(apply.getAs(BeginApplyInst.self)) + } + public func createWitnessMethod(lookupType: CanonicalType, conformance: Conformance, member: DeclRef, @@ -458,6 +601,12 @@ public struct Builder { } } + @discardableResult + public func createCondBranch(condition: Value, trueBlock: BasicBlock, falseBlock: BasicBlock) -> CondBranchInst { + let condBr = bridged.createCondBranch(condition.bridged, trueBlock.bridged, falseBlock.bridged) + return notifyNew(condBr.getAs(CondBranchInst.self)) + } + @discardableResult public func createUnreachable() -> UnreachableInst { let ui = bridged.createUnreachable() @@ -480,6 +629,10 @@ public struct Builder { return notifyNew(vectorInst.getAs(VectorInst.self)) } + public func createVectorBaseAddr(vector: Value) -> VectorBaseAddrInst { + return notifyNew(bridged.createVectorBaseAddr(vector.bridged).getAs(VectorBaseAddrInst.self)) + } + public func createGlobalAddr(global: GlobalVariable, dependencyToken: Value?) -> GlobalAddrInst { return notifyNew(bridged.createGlobalAddr(global.bridged, dependencyToken.bridged).getAs(GlobalAddrInst.self)) } @@ -526,6 +679,10 @@ public struct Builder { return notifyNew(bridged.createDestructureTuple(tuple.bridged).getAs(DestructureTupleInst.self)) } + public func createProjectBox(box: Value, fieldIndex: Int) -> ProjectBoxInst { + return notifyNew(bridged.createProjectBox(box.bridged, fieldIndex).getAs(ProjectBoxInst.self)) + } + @discardableResult public func createStore(source: Value, destination: Value, ownership: StoreInst.StoreOwnership) -> StoreInst { let store = bridged.createStore(source.bridged, destination.bridged, ownership.rawValue) @@ -575,6 +732,12 @@ public struct Builder { return notifyNew(endMutation.getAs(EndCOWMutationInst.self)) } + @discardableResult + public func createEndCOWMutationAddr(address: Value) -> EndCOWMutationAddrInst { + let endMutation = bridged.createEndCOWMutationAddr(address.bridged) + return notifyNew(endMutation.getAs(EndCOWMutationAddrInst.self)) + } + public func createMarkDependence(value: Value, base: Value, kind: MarkDependenceKind) -> MarkDependenceInst { let markDependence = bridged.createMarkDependence(value.bridged, base.bridged, BridgedInstruction.MarkDependenceKind(rawValue: kind.rawValue)!) @@ -587,6 +750,18 @@ public struct Builder { return notifyNew(markDependence.getAs(MarkDependenceAddrInst.self)) } + public func createMarkUninitialized(value: Value, kind: MarkUninitializedInst.Kind) -> MarkUninitializedInst { + let mu = bridged.createMarkUninitialized(value.bridged, kind.rawValue) + return notifyNew(mu.getAs(MarkUninitializedInst.self)) + } + + public func createMarkUnresolvedNonCopyableValue(value: Value, + checkKind: MarkUnresolvedNonCopyableValueInst.CheckKind, + isStrict: Bool) -> MarkUnresolvedNonCopyableValueInst { + let mu = bridged.createMarkUnresolvedNonCopyableValue(value.bridged, checkKind.rawValue, isStrict) + return notifyNew(mu.getAs(MarkUnresolvedNonCopyableValueInst.self)) + } + @discardableResult public func createEndAccess(beginAccess: BeginAccessInst) -> EndAccessInst { let endAccess = bridged.createEndAccess(beginAccess.bridged) @@ -615,3 +790,26 @@ public struct Builder { return notifyNew(convertFunction.getAs(ConvertEscapeToNoEscapeInst.self)) } } + + +//===----------------------------------------------------------------------===// +// Utilities +//===----------------------------------------------------------------------===// + +extension Builder { + public func emitDestroy(of value: Value) { + if value.type.isTrivial(in: value.parentFunction) { + return + } + if value.type.isAddress { + return + } + if value.parentFunction.hasOwnership { + createDestroyValue(operand: value) + } else if value.type.isClass { + createStrongRelease(operand: value) + } else { + createReleaseValue(operand: value) + } + } +} diff --git a/SwiftCompilerSources/Sources/SIL/CMakeLists.txt b/SwiftCompilerSources/Sources/SIL/CMakeLists.txt index 12cf7737028c6..8d4c2ac9275ac 100644 --- a/SwiftCompilerSources/Sources/SIL/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/SIL/CMakeLists.txt @@ -17,6 +17,7 @@ add_swift_compiler_module(SIL BasicBlock.swift Builder.swift ConstExpressionEvaluator.swift + Context.swift DeclRef.swift Effects.swift ForwardingInstruction.swift @@ -34,5 +35,6 @@ add_swift_compiler_module(SIL VTable.swift WitnessTable.swift) +add_subdirectory(DataStructures) add_subdirectory(Utilities) diff --git a/SwiftCompilerSources/Sources/SIL/Context.swift b/SwiftCompilerSources/Sources/SIL/Context.swift new file mode 100644 index 0000000000000..e462904ade445 --- /dev/null +++ b/SwiftCompilerSources/Sources/SIL/Context.swift @@ -0,0 +1,210 @@ +//===--- Context.swift - defines the context protocols --------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 SILBridging +import AST + +public protocol Context { + var _bridged: BridgedContext { get } +} + +/// A context which allows mutation of a function's SIL. +public protocol MutatingContext : Context { + // Called by all instruction mutations, including inserted new instructions. + var notifyInstructionChanged: (Instruction) -> () { get } +} + +/// Common funcationality of all Contexts. +extension Context { + public var silStage: SILStage { + switch _bridged.getSILStage() { + case .Raw: return .raw + case .Canonical: return .canonical + case .Lowered: return .lowered + default: fatalError("unhandled SILStage case") + } + } + + public var currentModuleContext: ModuleDecl { + _bridged.getCurrentModuleContext().getAs(ModuleDecl.self) + } + + public var moduleIsSerialized: Bool { _bridged.moduleIsSerialized() } + + public var moduleHasLoweredAddresses: Bool { _bridged.moduleHasLoweredAddresses() } + + public func lookupDeinit(ofNominal: NominalTypeDecl) -> Function? { + _bridged.lookUpNominalDeinitFunction(ofNominal.bridged).function + } + + public func getBuiltinIntegerType(bitWidth: Int) -> Type { _bridged.getBuiltinIntegerType(bitWidth).type } + + public func getTupleType(elements: [Type]) -> AST.`Type` { + let bridgedElements = elements.map { $0.bridged } + return bridgedElements.withBridgedArrayRef { + AST.`Type`(bridged: _bridged.getTupleType($0)) + } + } + + public var swiftArrayDecl: NominalTypeDecl { + _bridged.getSwiftArrayDecl().getAs(NominalTypeDecl.self) + } + + public var swiftMutableSpan: NominalTypeDecl { + _bridged.getSwiftMutableSpanDecl().getAs(NominalTypeDecl.self) + } + + public func lookupFunction(name: String) -> Function? { + name._withBridgedStringRef { + _bridged.lookupFunction($0).function + } + } + + public func lookupWitnessTable(for conformance: Conformance) -> WitnessTable? { + return _bridged.lookupWitnessTable(conformance.bridged).witnessTable + } + + public func lookupVTable(for classDecl: NominalTypeDecl) -> VTable? { + return _bridged.lookupVTable(classDecl.bridged).vTable + } + + public func lookupSpecializedVTable(for classType: Type) -> VTable? { + return _bridged.lookupSpecializedVTable(classType.bridged).vTable + } + + /// Looks up a function in the `Swift` module. + /// The `name` is the source name of the function and not the mangled name. + /// Returns nil if no such function or multiple matching functions are found. + public func lookupStdlibFunction(name: StaticString) -> Function? { + return name.withUTF8Buffer { (nameBuffer: UnsafeBufferPointer) in + let nameStr = BridgedStringRef(data: nameBuffer.baseAddress, count: nameBuffer.count) + return _bridged.lookupStdlibFunction(nameStr).function + } + } + + public func getSpecializedConformance(of genericConformance: Conformance, + for type: AST.`Type`, + substitutions: SubstitutionMap) -> Conformance + { + let c = _bridged.getSpecializedConformance(genericConformance.bridged, type.bridged, substitutions.bridged) + return Conformance(bridged: c) + } +} + +extension MutatingContext { + public func verifyIsTransforming(function: Function) { + precondition(_bridged.isTransforming(function.bridged), "pass modifies wrong function") + } + + public func notifyInstructionsChanged() { + _bridged.notifyChanges(.Instructions) + } + + public func notifyCallsChanged() { + _bridged.notifyChanges(.Calls) + } + + public func notifyBranchesChanged() { + _bridged.notifyChanges(.Branches) + } + + public func notifyEffectsChanged() { + _bridged.notifyChanges(.Effects) + } + + public func createGlobalVariable(name: String, type: Type, linkage: Linkage, isLet: Bool, markedAsUsed: Bool) -> GlobalVariable { + let gv = name._withBridgedStringRef { + _bridged.createGlobalVariable($0, type.bridged, linkage.bridged, isLet, + markedAsUsed) + } + return gv.globalVar + } + + /// Splits the basic block, which contains `inst`, before `inst` and returns the + /// new block. + /// + /// `inst` and all subsequent instructions are moved to the new block, while all + /// instructions _before_ `inst` remain in the original block. + public func splitBlock(before inst: Instruction) -> BasicBlock { + notifyBranchesChanged() + return _bridged.splitBlockBefore(inst.bridged).block + } + + /// Splits the basic block, which contains `inst`, after `inst` and returns the + /// new block. + /// + /// All subsequent instructions after `inst` are moved to the new block, while `inst` and all + /// instructions _before_ `inst` remain in the original block. + public func splitBlock(after inst: Instruction) -> BasicBlock { + notifyBranchesChanged() + return _bridged.splitBlockAfter(inst.bridged).block + } + + public func createBlock(after block: BasicBlock) -> BasicBlock { + notifyBranchesChanged() + return _bridged.createBlockAfter(block.bridged).block + } + + /// Removes and deletes `instruction`. + /// If `salvageDebugInfo` is true, compensating `debug_value` instructions are inserted for certain + /// kind of instructions. + public func erase(instruction: Instruction, salvageDebugInfo: Bool = true) { + if !instruction.isInStaticInitializer { + verifyIsTransforming(function: instruction.parentFunction) + } + if instruction is FullApplySite { + notifyCallsChanged() + } + if instruction is TermInst { + notifyBranchesChanged() + } + notifyInstructionsChanged() + + for operand in instruction.operands { + if let opInst = operand.value.definingInstruction { + notifyInstructionChanged(opInst) + } + } + + _bridged.eraseInstruction(instruction.bridged, salvageDebugInfo) + } + + public func erase(instructionIncludingAllUsers inst: Instruction) { + if inst.isDeleted { + return + } + for result in inst.results { + for use in result.uses { + erase(instructionIncludingAllUsers: use.instruction) + } + } + // We rely that after deleting the instruction its operands have no users. + // Therefore `salvageDebugInfo` must be turned off because we cannot insert debug_value instructions. + erase(instruction: inst, salvageDebugInfo: false) + } + + public func erase(instructions: S) where S.Element: Instruction { + for inst in instructions { + erase(instruction: inst) + } + } + + public func erase(instructionIncludingDebugUses inst: Instruction) { + precondition(inst.results.allSatisfy { $0.uses.ignoreDebugUses.isEmpty }) + erase(instructionIncludingAllUsers: inst) + } + + public func erase(block: BasicBlock) { + _bridged.eraseBlock(block.bridged) + } + +} diff --git a/SwiftCompilerSources/Sources/Optimizer/DataStructures/BasicBlockRange.swift b/SwiftCompilerSources/Sources/SIL/DataStructures/BasicBlockRange.swift similarity index 79% rename from SwiftCompilerSources/Sources/Optimizer/DataStructures/BasicBlockRange.swift rename to SwiftCompilerSources/Sources/SIL/DataStructures/BasicBlockRange.swift index cc33b70c71ef5..140a746a41d6a 100644 --- a/SwiftCompilerSources/Sources/Optimizer/DataStructures/BasicBlockRange.swift +++ b/SwiftCompilerSources/Sources/SIL/DataStructures/BasicBlockRange.swift @@ -10,8 +10,6 @@ // //===----------------------------------------------------------------------===// -import SIL - /// A range of basic blocks. /// /// The `BasicBlockRange` defines a range from a dominating "begin" block to one or more "end" blocks. @@ -46,16 +44,16 @@ import SIL /// This type should be a move-only type, but unfortunately we don't have move-only /// types yet. Therefore it's needed to call `deinitialize()` explicitly to /// destruct this data structure, e.g. in a `defer {}` block. -struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren { +public struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren { /// The dominating begin block. - let begin: BasicBlock + public let begin: BasicBlock /// The inclusive range, i.e. the exclusive range plus the end blocks. - private(set) var inclusiveRange: Stack - + public private(set) var inclusiveRange: Stack + /// The exclusive range, i.e. not containing the end blocks. - var range: LazyFilterSequence> { + public var range: LazyFilterSequence> { inclusiveRange.lazy.filter { contains($0) } } @@ -66,7 +64,7 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren { private var inExclusiveRange: BasicBlockSet private var worklist: BasicBlockWorklist - init(begin: BasicBlock, _ context: some Context) { + public init(begin: BasicBlock, _ context: some Context) { self.begin = begin self.inclusiveRange = Stack(context) self.inserted = Stack(context) @@ -77,39 +75,48 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren { } /// Insert a potential end block. - mutating func insert(_ block: BasicBlock) { + /// + /// Returns true if the begin-block is reached during backward propagation. + /// Usually this is not relevant, but InstructionRange needs this information. + @discardableResult + public mutating func insert(_ block: BasicBlock) -> Bool { if wasInserted.insert(block) { inserted.append(block) } worklist.pushIfNotVisited(block) + var visitedBeginBlock = false while let b = worklist.pop() { inclusiveRange.append(b) if b != begin { for pred in b.predecessors { + if pred == begin { + visitedBeginBlock = true + } worklist.pushIfNotVisited(pred) inExclusiveRange.insert(pred) } } } + return visitedBeginBlock } /// Insert a sequence of potential end blocks. - mutating func insert(contentsOf other: S) where S.Element == BasicBlock { + public mutating func insert(contentsOf other: S) where S.Element == BasicBlock { for block in other { insert(block) } } /// Returns true if the exclusive range contains `block`. - func contains(_ block: BasicBlock) -> Bool { inExclusiveRange.contains(block) } - + public func contains(_ block: BasicBlock) -> Bool { inExclusiveRange.contains(block) } + /// Returns true if the inclusive range contains `block`. - func inclusiveRangeContains (_ block: BasicBlock) -> Bool { + public func inclusiveRangeContains (_ block: BasicBlock) -> Bool { worklist.hasBeenPushed(block) } /// Returns true if the range is valid and that's iff the begin block dominates all blocks of the range. - var isValid: Bool { + public var isValid: Bool { let entry = begin.parentFunction.entryBlock return begin == entry || // If any block in the range is not dominated by `begin`, the range propagates back to the entry block. @@ -117,12 +124,12 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren { } /// Returns the end blocks. - var ends: LazyFilterSequence> { + public var ends: LazyFilterSequence> { inserted.lazy.filter { !contains($0) } } /// Returns the exit blocks. - var exits: LazySequence>, LazyFilterSequence>>> { range.flatMap { @@ -133,11 +140,11 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren { } /// Returns the interior blocks. - var interiors: LazyFilterSequence> { + public var interiors: LazyFilterSequence> { inserted.lazy.filter { contains($0) && $0 != begin } } - var description: String { + public var description: String { return (isValid ? "" : "\n") + """ begin: \(begin.name) @@ -150,7 +157,7 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren { } /// TODO: once we have move-only types, make this a real deinit. - mutating func deinitialize() { + public mutating func deinitialize() { worklist.deinitialize() inExclusiveRange.deinitialize() wasInserted.deinitialize() diff --git a/SwiftCompilerSources/Sources/SIL/DataStructures/CMakeLists.txt b/SwiftCompilerSources/Sources/SIL/DataStructures/CMakeLists.txt new file mode 100644 index 0000000000000..7564b469aa421 --- /dev/null +++ b/SwiftCompilerSources/Sources/SIL/DataStructures/CMakeLists.txt @@ -0,0 +1,14 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +swift_compiler_sources(SIL + BasicBlockRange.swift + InstructionRange.swift + Set.swift + Stack.swift + Worklist.swift) diff --git a/SwiftCompilerSources/Sources/SIL/DataStructures/InstructionRange.swift b/SwiftCompilerSources/Sources/SIL/DataStructures/InstructionRange.swift new file mode 100644 index 0000000000000..c2acd2ca7a35b --- /dev/null +++ b/SwiftCompilerSources/Sources/SIL/DataStructures/InstructionRange.swift @@ -0,0 +1,277 @@ +//===--- InstructionRange.swift - a range of instructions -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 range of instructions. +/// +/// The `InstructionRange` defines a range from a dominating "begin" instruction to one or more "end" instructions. +/// The range is "exclusive", which means that the "end" instructions are not part of the range. +/// +/// One or more "potential" end instructions can be inserted. +/// Though, not all inserted instructions end up as "end" instructions. +/// +/// `InstructionRange` is useful for calculating the liverange of values. +/// +/// The `InstructionRange` is similar to a `BasicBlockRange`, but defines the range +/// in a "finer" granularity, i.e. on instructions instead of blocks. +/// `InstructionRange` uses an underlying `BasicBlockRange` to compute the +/// involved blocks of the instruction range. +/// +/// There are several kind of instructions: +/// * begin: it is a single instruction which dominates all instructions of the range +/// * ends: all inserted instruction which are at the end of the range +/// * exits: the first instructions of the exit blocks +/// * interiors: all inserted instructions which are not end instructions. +/// +/// See also `BasicBlockRange` for more information. +/// +/// This type should be a move-only type, but unfortunately we don't have move-only +/// types yet. Therefore it's needed to call `deinitialize()` explicitly to +/// destruct this data structure, e.g. in a `defer {}` block. +public struct InstructionRange : CustomStringConvertible, NoReflectionChildren { + + /// The underlying block range. + public private(set) var blockRange: BasicBlockRange + + private var insertedInsts: InstructionSet + + // For efficiency, this set does not include instructions in blocks which are not the begin or any end block. + private var inExclusiveRange: InstructionSet + + public init(begin beginInst: Instruction, _ context: some Context) { + self = InstructionRange(beginBlock: beginInst.parentBlock, context) + self.inExclusiveRange.insert(beginInst) + } + + // Note: 'ends' are simply the instructions to insert in the range. 'self.ends' might not return the same sequence + // as this 'ends' argument because 'self.ends' will not include block exits. + public init(begin beginInst: Instruction, ends: S, _ context: some Context) where S.Element: Instruction { + self = InstructionRange(begin: beginInst, context) + insert(contentsOf: ends) + } + + public init(for value: Value, _ context: some Context) { + if let inst = value.definingInstruction { + self = InstructionRange(begin: inst, context) + } else if let arg = value as? Argument { + self = InstructionRange(beginBlock: arg.parentBlock, context) + } else { + fatalError("cannot build an instruction range for \(value)") + } + } + + private init(beginBlock: BasicBlock, _ context: some Context) { + self.blockRange = BasicBlockRange(begin: beginBlock, context) + self.insertedInsts = InstructionSet(context) + self.inExclusiveRange = InstructionSet(context) + } + + /// Insert a potential end instruction. + public mutating func insert(_ inst: Instruction) { + insertedInsts.insert(inst) + insertIntoRange(instructions: ReverseInstructionList(first: inst.previous)) + if blockRange.insert(inst.parentBlock) { + // The first time an instruction is inserted in another block than the begin-block we need to insert + // instructions from the begin instruction to the end of the begin block. + // For subsequent insertions this is a no-op: `insertIntoRange` will return immediately because those + // instruction are already inserted. + insertIntoRange(instructions: blockRange.begin.instructions.reversed()) + } + } + + /// Insert a sequence of potential end instructions. + public mutating func insert(contentsOf other: S) where S.Element: Instruction { + for inst in other { + insert(inst) + } + } + + /// Returns true if the exclusive range contains `inst`. + public func contains(_ inst: Instruction) -> Bool { + if inExclusiveRange.contains(inst) { + return true + } + let block = inst.parentBlock + return block != blockRange.begin && blockRange.contains(block) + } + + /// Returns true if the inclusive range contains `inst`. + public func inclusiveRangeContains (_ inst: Instruction) -> Bool { + contains(inst) || insertedInsts.contains(inst) + } + + /// Returns the end instructions. + /// + /// Warning: this returns `begin` if no instructions were inserted. + public var ends: LazyMapSequence>, Instruction> { + blockRange.ends.map { + $0.instructions.reversed().first(where: { insertedInsts.contains($0)})! + } + } + + // Returns the exit blocks. + public var exitBlocks: LazySequence>, + LazyFilterSequence>>> { + blockRange.exits + } + + /// Returns the exit instructions. + public var exits: LazyMapSequence>, + LazyFilterSequence>>>, + Instruction> { + blockRange.exits.lazy.map { $0.instructions.first! } + } + + /// Returns the interior instructions. + public var interiors: LazySequence, + LazyFilterSequence>>> { + blockRange.inserted.lazy.flatMap { + var include = blockRange.contains($0) + return $0.instructions.reversed().lazy.filter { + if insertedInsts.contains($0) { + let isInterior = include + include = true + return isInterior + } + return false + } + } + } + + public var begin: Instruction? { + blockRange.begin.instructions.first(where: inExclusiveRange.contains) + } + + private mutating func insertIntoRange(instructions: ReverseInstructionList) { + for inst in instructions { + if !inExclusiveRange.insert(inst) { + return + } + } + } + + public var description: String { + return (blockRange.isValid ? "" : "\n") + + """ + begin: \(begin?.description ?? blockRange.begin.name) + ends: \(ends.map { $0.description }.joined(separator: "\n ")) + exits: \(exits.map { $0.description }.joined(separator: "\n ")) + interiors:\(interiors.map { $0.description }.joined(separator: "\n ")) + """ + } + + /// TODO: once we have move-only types, make this a real deinit. + public mutating func deinitialize() { + inExclusiveRange.deinitialize() + insertedInsts.deinitialize() + blockRange.deinitialize() + } +} + +extension InstructionRange { + public enum PathOverlap { + // range: --- + // | pathBegin + // | | + // | pathEnd + // --- + case containsPath + + // range: --- + // | pathBegin + // --- | + // pathEnd + case containsBegin + + // pathBegin + // range: --- | + // | pathEnd + // --- + case containsEnd + + // pathBegin + // range: --- | + // | | + // --- | + // pathEnd + case overlappedByPath + + // either: pathBegin + // | + // pathEnd + // range: --- + // | + // --- + // or: pathBegin + // | + // pathEnd + case disjoint + } + + /// Return true if any exclusive path from `begin` to `end` includes an instruction in this exclusive range. + /// + /// Returns .containsBegin, if this range has the same begin and end as the path. + /// + /// Precondition: `begin` dominates `end`. + public func overlaps(pathBegin: Instruction, pathEnd: Instruction, _ context: some Context) -> PathOverlap { + assert(pathBegin != pathEnd, "expect an exclusive path") + if contains(pathBegin) { + // Note: pathEnd != self.begin here since self.contains(pathBegin) + if contains(pathEnd) { return .containsPath } + return .containsBegin + } + if contains(pathEnd) { + if let rangeBegin = self.begin, rangeBegin == pathEnd { + return .disjoint + } + return .containsEnd + } + // Neither end-point is contained. If a backward path walk encounters this range, then it must overlap this + // range. Otherwise, it is disjoint. + var backwardBlocks = BasicBlockWorklist(context) + defer { backwardBlocks.deinitialize() } + backwardBlocks.pushIfNotVisited(pathEnd.parentBlock) + while let block = backwardBlocks.pop() { + if blockRange.inclusiveRangeContains(block) { + // This range overlaps with this block, but there are still three possibilities: + // (1) range, pathBegin, pathEnd = disjoint (range might not begin in this block) + // (2) pathBegin, pathEnd, range = disjoint (pathBegin might not be in this block) + // (3) pathBegin, range, pathEnd = overlappedByPath (range or pathBegin might not be in this block) + // + // Walk backward from pathEnd to find either pathBegin or an instruction in this range. + // Both this range and the path may or may not begin in this block. + let endInBlock = block == pathEnd.parentBlock ? pathEnd : block.terminator + for inst in ReverseInstructionList(first: endInBlock) { + // Check pathBegin first because the range is exclusive. + if inst == pathBegin { + break + } + // Check inclusiveRangeContains() in case the range end is the first instruction in this block. + if inclusiveRangeContains(inst) { + return .overlappedByPath + } + } + // No instructions in this range occur between pathBegin and pathEnd. + return .disjoint + } + // No range blocks have been reached. + if block == pathBegin.parentBlock { + return .disjoint + } + backwardBlocks.pushIfNotVisited(contentsOf: block.predecessors) + } + fatalError("begin: \(pathBegin)\n must dominate end: \(pathEnd)") + } +} + diff --git a/SwiftCompilerSources/Sources/SIL/DataStructures/Set.swift b/SwiftCompilerSources/Sources/SIL/DataStructures/Set.swift new file mode 100644 index 0000000000000..d631a35797811 --- /dev/null +++ b/SwiftCompilerSources/Sources/SIL/DataStructures/Set.swift @@ -0,0 +1,289 @@ +//===--- Set.swift - sets for basic blocks, values and instructions -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 SILBridging + +public protocol IntrusiveSet : CustomStringConvertible, NoReflectionChildren { + associatedtype Element + + init(_ context: some Context) + mutating func insert(_ element: Element) -> Bool + mutating func erase(_ element: Element) + func contains(_ element: Element) -> Bool + mutating func deinitialize() +} + +/// A set of basic blocks. +/// +/// This is an extremely efficient implementation which does not need memory +/// allocations or hash lookups. +/// +/// This type should be a move-only type, but unfortunately we don't have move-only +/// types yet. Therefore it's needed to call `deinitialize()` explicitly to +/// destruct this data structure, e.g. in a `defer {}` block. +public struct BasicBlockSet : IntrusiveSet { + + private let context: BridgedContext + private let bridged: BridgedBasicBlockSet + + public init(_ context: some Context) { + self.context = context._bridged + self.bridged = self.context.allocBasicBlockSet() + } + + public func contains(_ block: BasicBlock) -> Bool { + bridged.contains(block.bridged) + } + + /// Returns true if `block` was not contained in the set before inserting. + @discardableResult + public mutating func insert(_ block: BasicBlock) -> Bool { + bridged.insert(block.bridged) + } + + public mutating func erase(_ block: BasicBlock) { + bridged.erase(block.bridged) + } + + public var description: String { + let function = bridged.getFunction().function + let blockNames = function.blocks.enumerated().filter { contains($0.1) } + .map { "bb\($0.0)"} + return "{" + blockNames.joined(separator: ", ") + "}" + } + + /// TODO: once we have move-only types, make this a real deinit. + public mutating func deinitialize() { + context.freeBasicBlockSet(bridged) + } +} + +/// A set of values. +/// +/// This is an extremely efficient implementation which does not need memory +/// allocations or hash lookups. +/// +/// This type should be a move-only type, but unfortunately we don't have move-only +/// types yet. Therefore it's needed to call `deinitialize()` explicitly to +/// destruct this data structure, e.g. in a `defer {}` block. +public struct ValueSet : IntrusiveSet { + + private let context: BridgedContext + private let bridged: BridgedNodeSet + + public init(_ context: some Context) { + self.context = context._bridged + self.bridged = self.context.allocNodeSet() + } + + public func contains(_ value: Value) -> Bool { + bridged.containsValue(value.bridged) + } + + /// Returns true if `value` was not contained in the set before inserting. + @discardableResult + public mutating func insert(_ value: Value) -> Bool { + bridged.insertValue(value.bridged) + } + + public mutating func erase(_ value: Value) { + bridged.eraseValue(value.bridged) + } + + public var description: String { + let function = bridged.getFunction().function + var d = "{\n" + for block in function.blocks { + for arg in block.arguments { + if contains(arg) { + d += arg.description + } + } + for inst in block.instructions { + for result in inst.results { + if contains(result) { + d += result.description + } + } + } + } + d += "}\n" + return d + } + + /// TODO: once we have move-only types, make this a real deinit. + public mutating func deinitialize() { + context.freeNodeSet(bridged) + } +} + +/// A set of instructions. +/// +/// This is an extremely efficient implementation which does not need memory +/// allocations or hash lookups. +/// +/// This type should be a move-only type, but unfortunately we don't have move-only +/// types yet. Therefore it's needed to call `deinitialize()` explicitly to +/// destruct this data structure, e.g. in a `defer {}` block. +public struct SpecificInstructionSet : IntrusiveSet { + + private let context: BridgedContext + private let bridged: BridgedNodeSet + + public init(_ context: some Context) { + self.context = context._bridged + self.bridged = self.context.allocNodeSet() + } + + public func contains(_ inst: InstType) -> Bool { + bridged.containsInstruction(inst.bridged) + } + + /// Returns true if `inst` was not contained in the set before inserting. + @discardableResult + public mutating func insert(_ inst: InstType) -> Bool { + bridged.insertInstruction(inst.bridged) + } + + public mutating func erase(_ inst: InstType) { + bridged.eraseInstruction(inst.bridged) + } + + public var description: String { + let function = bridged.getFunction().function + var d = "{\n" + for i in function.instructions { + if let inst = i as? InstType, contains(inst) { + d += inst.description + "\n" + } + } + d += "}\n" + return d + } + + /// TODO: once we have move-only types, make this a real deinit. + public mutating func deinitialize() { + context.freeNodeSet(bridged) + } +} + +/// An `InstructionSet` which also provides a `count` property. +public struct SpecificInstructionSetWithCount : IntrusiveSet { + public private(set) var count = 0 + private var underlyingSet: SpecificInstructionSet + + public init(_ context: some Context) { + self.underlyingSet = SpecificInstructionSet(context) + } + + public func contains(_ inst: InstType) -> Bool { underlyingSet.contains(inst) } + + public var isEmpty: Bool { count == 0 } + + /// Returns true if `inst` was not contained in the set before inserting. + @discardableResult + public mutating func insert(_ inst: InstType) -> Bool { + if underlyingSet.insert(inst) { + count += 1 + return true + } + return false + } + + public mutating func erase(_ inst: InstType) { + if underlyingSet.contains(inst) { + count -= 1 + assert(count >= 0) + } + underlyingSet.erase(inst) + } + + public var description: String { underlyingSet.description } + + public mutating func deinitialize() { underlyingSet.deinitialize() } +} + +public typealias InstructionSet = SpecificInstructionSet +public typealias InstructionSetWithCount = SpecificInstructionSetWithCount + +/// A set of operands. +/// +/// This is an extremely efficient implementation which does not need memory +/// allocations or hash lookups. +/// +/// This type should be a move-only type, but unfortunately we don't have move-only +/// types yet. Therefore it's needed to call `deinitialize()` explicitly to +/// destruct this data structure, e.g. in a `defer {}` block. +public struct OperandSet : IntrusiveSet { + + private let context: BridgedContext + private let bridged: BridgedOperandSet + + public init(_ context: some Context) { + self.context = context._bridged + self.bridged = self.context.allocOperandSet() + } + + public func contains(_ operand: Operand) -> Bool { + bridged.contains(operand.bridged) + } + + /// Returns true if `inst` was not contained in the set before inserting. + @discardableResult + public mutating func insert(_ operand: Operand) -> Bool { + bridged.insert(operand.bridged) + } + + public mutating func erase(_ operand: Operand) { + bridged.erase(operand.bridged) + } + + public var description: String { + let function = bridged.getFunction().function + var d = "{\n" + for inst in function.instructions { + for op in inst.operands { + if contains(op) { + d += op.description + } + } + } + d += "}\n" + return d + } + + /// TODO: once we have move-only types, make this a real deinit. + public mutating func deinitialize() { + context.freeOperandSet(bridged) + } +} + +extension InstructionSet { + public mutating func insert(contentsOf source: some Sequence) { + for inst in source { + _ = insert(inst) + } + } +} + +extension IntrusiveSet { + public mutating func insert(contentsOf source: some Sequence) { + for element in source { + _ = insert(element) + } + } + + public init(insertContentsOf source: some Sequence, _ context: some Context) { + self.init(context) + insert(contentsOf: source) + } +} diff --git a/SwiftCompilerSources/Sources/SIL/DataStructures/Stack.swift b/SwiftCompilerSources/Sources/SIL/DataStructures/Stack.swift new file mode 100644 index 0000000000000..30444bdb7723b --- /dev/null +++ b/SwiftCompilerSources/Sources/SIL/DataStructures/Stack.swift @@ -0,0 +1,305 @@ +//===--- Stack.swift - defines the Stack data structure -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 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 SILBridging + +/// A very efficient implementation of a stack, which can also be iterated over. +/// +/// A Stack is the best choice for things like worklists, etc., if no random +/// access is needed. +/// Compared to Array, it does not require any memory allocations, because it +/// uses a recycling bump pointer allocator for allocating the slabs. +/// All operations have (almost) zero cost. +/// +/// This type should be a move-only type, but unfortunately we don't have move-only +/// types yet. Therefore it's needed to call `deinitialize()` explicitly to +/// destruct this data structure, e.g. in a `defer {}` block. +public struct Stack : CollectionLikeSequence { + + private let bridgedContext: BridgedContext + private var firstSlab = BridgedContext.Slab(nil) + private var lastSlab = BridgedContext.Slab(nil) + private var endIndex: Int = 0 + + private static var slabCapacity: Int { + BridgedContext.Slab.getCapacity() / MemoryLayout.stride + } + + private func allocate(after lastSlab: BridgedContext.Slab? = nil) -> BridgedContext.Slab { + let lastSlab = lastSlab ?? BridgedContext.Slab(nil) + let newSlab = bridgedContext.allocSlab(lastSlab) + UnsafeMutableRawPointer(newSlab.data!).bindMemory(to: Element.self, capacity: Stack.slabCapacity) + return newSlab + } + + private static func element(in slab: BridgedContext.Slab, at index: Int) -> Element { + return pointer(in: slab, at: index).pointee + } + + private static func pointer(in slab: BridgedContext.Slab, at index: Int) -> UnsafeMutablePointer { + return UnsafeMutableRawPointer(slab.data!).assumingMemoryBound(to: Element.self) + index + } + + public struct Iterator : IteratorProtocol { + var slab: BridgedContext.Slab + var index: Int + let lastSlab: BridgedContext.Slab + let endIndex: Int + + public mutating func next() -> Element? { + let end = (slab.data == lastSlab.data ? endIndex : slabCapacity) + + guard index < end else { return nil } + + let elem = Stack.element(in: slab, at: index) + index += 1 + + if index >= end && slab.data != lastSlab.data { + slab = slab.getNext() + index = 0 + } + return elem + } + } + + public init(_ context: some Context) { self.bridgedContext = context._bridged } + + public func makeIterator() -> Iterator { + return Iterator(slab: firstSlab, index: 0, lastSlab: lastSlab, endIndex: endIndex) + } + + public var first: Element? { + isEmpty ? nil : Stack.element(in: firstSlab, at: 0) + } + + public var last: Element? { + isEmpty ? nil : Stack.element(in: lastSlab, at: endIndex &- 1) + } + + public mutating func push(_ element: Element) { + if endIndex >= Stack.slabCapacity { + lastSlab = allocate(after: lastSlab) + endIndex = 0 + } else if firstSlab.data == nil { + assert(endIndex == 0) + firstSlab = allocate() + lastSlab = firstSlab + } + Stack.pointer(in: lastSlab, at: endIndex).initialize(to: element) + endIndex += 1 + } + + /// The same as `push` to provide an Array-like append API. + public mutating func append(_ element: Element) { push(element) } + + public mutating func append(contentsOf other: S) where S.Element == Element { + for elem in other { + append(elem) + } + } + + public var isEmpty: Bool { return endIndex == 0 } + + public mutating func pop() -> Element? { + if isEmpty { + return nil + } + assert(endIndex > 0) + endIndex -= 1 + let elem = Stack.pointer(in: lastSlab, at: endIndex).move() + + if endIndex == 0 { + if lastSlab.data == firstSlab.data { + _ = bridgedContext.freeSlab(lastSlab) + firstSlab.data = nil + lastSlab.data = nil + endIndex = 0 + } else { + lastSlab = bridgedContext.freeSlab(lastSlab) + endIndex = Stack.slabCapacity + } + } + + return elem + } + + public mutating func removeAll() { + while pop() != nil { } + } + + /// TODO: once we have move-only types, make this a real deinit. + public mutating func deinitialize() { removeAll() } +} + +public extension Stack { + /// Mark a stack location for future iteration. + /// + /// TODO: Marker should be ~Escapable. + struct Marker { + let slab: BridgedContext.Slab + let index: Int + } + + var top: Marker { Marker(slab: lastSlab, index: endIndex) } + + struct Segment : CollectionLikeSequence { + let low: Marker + let high: Marker + + public init(in stack: Stack, low: Marker, high: Marker) { + if low.slab.data == nil { + assert(low.index == 0, "invalid empty stack marker") + // `low == nil` and `high == nil` is a valid empty segment, + // even though `assertValid(marker:)` would return false. + if high.slab.data != nil { + stack.assertValid(marker: high) + } + self.low = Marker(slab: stack.firstSlab, index: 0) + self.high = high + return + } + stack.assertValid(marker: low) + stack.assertValid(marker: high) + self.low = low + self.high = high + } + + public func makeIterator() -> Stack.Iterator { + return Iterator(slab: low.slab, index: low.index, + lastSlab: high.slab, endIndex: high.index) + } + } + + /// Assert that `marker` is valid based on the current `top`. + /// + /// This is an assert rather than a query because slabs can reuse + /// memory leading to a stale marker that appears valid. + func assertValid(marker: Marker) { + var currentSlab = lastSlab + var currentIndex = endIndex + while currentSlab.data != marker.slab.data { + assert(currentSlab.data != firstSlab.data, "Invalid stack marker") + currentSlab = currentSlab.getPrevious() + currentIndex = Stack.slabCapacity + } + assert(marker.index <= currentIndex, "Invalid stack marker") + } + + /// Execute the `body` closure, passing it `self` for further + /// mutation of the stack and passing `marker` to mark the stack + /// position prior to executing `body`. `marker` must not escape the + /// `body` closure. + mutating func withMarker( + _ body: (inout Stack, Marker) throws -> R) rethrows -> R { + return try body(&self, top) + } + + /// Record a stack marker, execute a `body` closure, then execute a + /// `handleNewElements` closure with the Segment that contains all + /// elements that remain on the stack after being pushed on the + /// stack while executing `body`. `body` must push more elements + /// than it pops. + mutating func withMarker( + pushElements body: (inout Stack) throws -> R, + withNewElements handleNewElements: ((Segment) -> ()) + ) rethrows -> R { + return try withMarker { (stack: inout Stack, marker: Marker) in + let result = try body(&stack) + handleNewElements(Segment(in: stack, low: marker, high: stack.top)) + return result + } + } +} + +public struct StackWithCount : CollectionLikeSequence { + public private(set) var count = 0 + private var underlyingStack: Stack + + public typealias Iterator = Stack.Iterator + + public init(_ context: some Context) { + self.underlyingStack = Stack(context) + } + + public func makeIterator() -> Stack.Iterator { + underlyingStack.makeIterator() + } + + public var first: Element? { underlyingStack.first } + public var last: Element? { underlyingStack.last } + + public mutating func push(_ element: Element) { + count += 1 + underlyingStack.push(element) + } + + /// The same as `push` to provide an Array-like append API. + public mutating func append(_ element: Element) { push(element) } + + public mutating func append(contentsOf other: S) where S.Element == Element { + for elem in other { + append(elem) + } + } + + public var isEmpty: Bool { underlyingStack.isEmpty } + + public mutating func pop() -> Element? { + if underlyingStack.isEmpty { + return nil + } + + count -= 1 + return underlyingStack.pop() + } + + public mutating func removeAll() { + underlyingStack.removeAll() + } + + /// TODO: once we have move-only types, make this a real deinit. + public mutating func deinitialize() { removeAll() } +} + +public extension StackWithCount { + typealias Marker = Stack.Marker + + struct Segment : CollectionLikeSequence { + var underlyingSegment: Stack.Segment + + public init(in stack: StackWithCount, low: Marker, high: Marker) { + underlyingSegment = Stack.Segment(in: stack.underlyingStack, low: low, high: high) + } + + public func makeIterator() -> StackWithCount.Iterator { + return underlyingSegment.makeIterator() + } + } + + var top: Marker { underlyingStack.top } + + func assertValid(marker: Marker) { underlyingStack.assertValid(marker: marker) } + + mutating func withMarker( + _ body: (inout Stack, Marker) throws -> R) rethrows -> R { + return try underlyingStack.withMarker(body) + } + + mutating func withMarker( + pushElements body: (inout Stack) throws -> R, + withNewElements handleNewElements: ((Segment) -> ()) + ) rethrows -> R { + return try underlyingStack.withMarker(pushElements: body) { [self] segment in + handleNewElements(Segment(in: self, low: segment.low, high: segment.high)) + } + } +} diff --git a/SwiftCompilerSources/Sources/SIL/DataStructures/Worklist.swift b/SwiftCompilerSources/Sources/SIL/DataStructures/Worklist.swift new file mode 100644 index 0000000000000..acdbe882c7a4a --- /dev/null +++ b/SwiftCompilerSources/Sources/SIL/DataStructures/Worklist.swift @@ -0,0 +1,172 @@ +//===--- Worklist.swift ---------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 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 utility for processing entities in a worklist. +/// +/// A `Worklist` is basically a combination of a stack and a set. +/// It can be used for typical worklist-processing algorithms. +/// +/// This type should be a move-only type, but unfortunately we don't have move-only +/// types yet. Therefore it's needed to call `deinitialize()` explicitly to +/// destruct this data structure, e.g. in a `defer {}` block. +public struct Worklist : CustomStringConvertible, NoReflectionChildren { + public typealias Element = Set.Element + private var worklist: Stack + private var pushedElements: Set + + public init(_ context: some Context) { + self.worklist = Stack(context) + self.pushedElements = Set(context) + } + + public mutating func pop() -> Element? { return worklist.pop() } + + /// Pop and allow the popped element to be pushed again to the worklist. + public mutating func popAndForget() -> Element? { + if let element = worklist.pop() { + pushedElements.erase(element) + return element + } + return nil + } + + public mutating func pushIfNotVisited(_ element: Element) { + if pushedElements.insert(element) { + worklist.append(element) + } + } + + public mutating func pushIfNotVisited(contentsOf other: S) where S.Element == Element { + for element in other { + pushIfNotVisited(element) + } + } + + /// Returns true if \p element was pushed to the worklist, regardless if it's already popped or not. + public func hasBeenPushed(_ element: Element) -> Bool { pushedElements.contains(element) } + + public var isEmpty: Bool { worklist.isEmpty } + + public var description: String { + """ + worklist: \(worklist) + pushed: \(pushedElements) + """ + } + + /// TODO: once we have move-only types, make this a real deinit. + public mutating func deinitialize() { + pushedElements.deinitialize() + worklist.deinitialize() + } +} + +public typealias BasicBlockWorklist = Worklist +public typealias InstructionWorklist = Worklist +public typealias SpecificInstructionWorklist = Worklist> +public typealias ValueWorklist = Worklist +public typealias OperandWorklist = Worklist + +extension InstructionWorklist { + public mutating func pushPredecessors(of inst: Instruction, ignoring ignoreInst: Instruction? = nil) { + if let prev = inst.previous { + if prev != ignoreInst { + pushIfNotVisited(prev) + } + } else { + for predBlock in inst.parentBlock.predecessors { + let termInst = predBlock.terminator + if termInst != ignoreInst { + pushIfNotVisited(termInst) + } + } + } + } + + public mutating func pushSuccessors(of inst: Instruction, ignoring ignoreInst: Instruction? = nil) { + if let succ = inst.next { + if succ != ignoreInst { + pushIfNotVisited(succ) + } + } else { + for succBlock in inst.parentBlock.successors { + let firstInst = succBlock.instructions.first! + if firstInst != ignoreInst { + pushIfNotVisited(firstInst) + } + } + } + } +} + +/// A worklist for `Function`s. +public struct FunctionWorklist { + + // The current functions in the worklist. + public private(set) var functions = Array() + + // All functions which were ever pushed to the worklist. + private var pushedFunctions = Set() + + public init() {} + + public mutating func pushIfNotVisited(_ function: Function) { + if pushedFunctions.insert(function).inserted { + functions.append(function) + } + } + + public mutating func pushIfNotVisited(contentsOf functions: S) where S.Element == Function { + for f in functions { + pushIfNotVisited(f) + } + } + + public mutating func pop() -> Function? { + return functions.popLast() + } +} + +/// Like `ValueWorklist`, but allows pushing `Value`s from different functions - +/// at the cost of a less efficient implementation. +public struct CrossFunctionValueWorklist { + + // The current values in the worklist. + private(set) var values = Array() + + // All values which were ever pushed to the worklist. + private var pushedValues = Set(minimumCapacity: 8) + + public init() { + values.reserveCapacity(8) + } + + public mutating func pop() -> Value? { + return values.popLast() + } + + public mutating func pushIfNotVisited(_ value: Value) { + if pushedValues.insert(ObjectIdentifier(value)).inserted { + values.append(value) + } + } + + public mutating func pushIfNotVisited(contentsOf values: S) where S.Element == Value { + for value in values { + pushIfNotVisited(value) + } + } + + public func hasBeenPushed(_ value: Value) -> Bool { + return pushedValues.contains(ObjectIdentifier(value)) + } +} diff --git a/SwiftCompilerSources/Sources/SIL/DeclRef.swift b/SwiftCompilerSources/Sources/SIL/DeclRef.swift index 32ff660a6b151..a41660a57cc2a 100644 --- a/SwiftCompilerSources/Sources/SIL/DeclRef.swift +++ b/SwiftCompilerSources/Sources/SIL/DeclRef.swift @@ -30,4 +30,16 @@ public struct DeclRef: CustomStringConvertible, NoReflectionChildren { public static func ==(lhs: DeclRef, rhs: DeclRef) -> Bool { lhs.bridged.isEqualTo(rhs.bridged) } + + /// Do we have enough information to determine all callees that could + /// be reached by calling the function represented by Decl? + public func calleesAreStaticallyKnowable(_ context: some Context) -> Bool { + context._bridged.calleesAreStaticallyKnowable(bridged) + } +} + +extension DeclRef: DiagnosticArgument { + public func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { + fn(bridged.asDiagnosticArgument()) + } } diff --git a/SwiftCompilerSources/Sources/SIL/Effects.swift b/SwiftCompilerSources/Sources/SIL/Effects.swift index 9e106ccd3bf4f..311f4423c60aa 100644 --- a/SwiftCompilerSources/Sources/SIL/Effects.swift +++ b/SwiftCompilerSources/Sources/SIL/Effects.swift @@ -161,12 +161,7 @@ extension Function { break } if isProgramTerminationPoint { - // We can ignore any memory writes in a program termination point, because it's not relevant - // for the caller. But we need to consider memory reads, otherwise preceding memory writes - // would be eliminated by dead-store-elimination in the caller. E.g. String initialization - // for error strings which are printed by the program termination point. - // Regarding ownership: a program termination point must not touch any reference counted objects. - return SideEffects.GlobalEffects(memory: SideEffects.Memory(read: true)) + return SideEffects.GlobalEffects.worstEffects.forProgramTerminationPoints } var result = SideEffects.GlobalEffects.worstEffects switch effectAttribute { @@ -510,6 +505,14 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren { /// This is true when the function (or a callee, transitively) contains a /// deinit barrier instruction. public var isDeinitBarrier: Bool + + public static var noEffects: GlobalEffects { + return GlobalEffects(memory: .noEffects, ownership: .noEffects, allocates: false, isDeinitBarrier: false) + } + + public var isOnlyReading: Bool { + return !memory.write && ownership == .noEffects && !allocates && !isDeinitBarrier + } /// When called with default arguments, it creates an "effect-free" GlobalEffects. public init(memory: Memory = Memory(read: false, write: false), @@ -568,6 +571,18 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren { return result } + /// Effects with all effects removed which are not relevant for program termination points (like `fatalError`). + public var forProgramTerminationPoints: GlobalEffects { + // We can ignore any memory writes in a program termination point, because it's not relevant + // for the caller. But we need to consider memory reads, otherwise preceding memory writes + // would be eliminated by dead-store-elimination in the caller. E.g. String initialization + // for error strings which are printed by the program termination point. + // Regarding ownership: a program termination point must not touch any reference counted objects. + // Also, the deinit-barrier effect is not relevant because functions like `fatalError` and `exit` are + // not accessing objects (except strings). + return GlobalEffects(memory: Memory(read: memory.read)) + } + public static var worstEffects: GlobalEffects { GlobalEffects(memory: .worstEffects, ownership: .worstEffects, allocates: true, isDeinitBarrier: true) } @@ -636,6 +651,10 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren { copy = copy || other.copy destroy = destroy || other.destroy } + + public static var noEffects: Ownership { + return Ownership(copy: false, destroy: false) + } public static var worstEffects: Ownership { Ownership(copy: true, destroy: true) diff --git a/SwiftCompilerSources/Sources/SIL/ForwardingInstruction.swift b/SwiftCompilerSources/Sources/SIL/ForwardingInstruction.swift index 174c0e30be8e9..276bef4a9ff92 100644 --- a/SwiftCompilerSources/Sources/SIL/ForwardingInstruction.swift +++ b/SwiftCompilerSources/Sources/SIL/ForwardingInstruction.swift @@ -53,7 +53,12 @@ extension ForwardingInstruction { public var forwardingOwnership: Ownership { Ownership(bridged: bridged.ForwardingInst_forwardingOwnership()) } - + + public func setForwardingOwnership(to ownership: Ownership, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.ForwardingInst_setForwardingOwnership(ownership._bridged) + } + /// A forwarding instruction preserves reference counts if it has a dynamically non-trivial result in which all references are forwarded from the operand. /// /// A cast can only forward guaranteed values if it preserves reference counts. Such casts cannot release any references within their operand's value and cannot retain any references owned by their result. diff --git a/SwiftCompilerSources/Sources/SIL/Function.swift b/SwiftCompilerSources/Sources/SIL/Function.swift index 11db1fedf5f5b..45122979f77f9 100644 --- a/SwiftCompilerSources/Sources/SIL/Function.swift +++ b/SwiftCompilerSources/Sources/SIL/Function.swift @@ -36,6 +36,14 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash hasher.combine(ObjectIdentifier(self)) } + /// True if this function is referenced from anywhere within the module, + /// e.g. from a `function_ref` instruction. + public var isReferencedInModule: Bool { bridged.isReferencedInModule() } + + /// True if this function should be optimized, i.e. the module is compiled with optimizations + /// and the function has no `@_optimize(none)` attribute. + public var shouldOptimize: Bool { bridged.shouldOptimize() } + public var wasDeserializedCanonical: Bool { bridged.wasDeserializedCanonical() } public var isTrapNoReturn: Bool { bridged.isTrapNoReturn() } @@ -45,21 +53,26 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash public var isConvertPointerToPointerArgument: Bool { bridged.isConvertPointerToPointerArgument() } public var specializationLevel: Int { bridged.specializationLevel() } - + + public var isSpecialization: Bool { bridged.isSpecialization() } + public var hasOwnership: Bool { bridged.hasOwnership() } public var hasLoweredAddresses: Bool { bridged.hasLoweredAddresses() } - /// The lowered function type in the expansion context of self. + public var loweredFunctionType: CanonicalType { + CanonicalType(bridged: bridged.getLoweredFunctionType()) + } + + /// The lowered function type, with opaque archetypes erased. /// - /// Always expanding a function type means that the opaque result types - /// have the correct generic signature. For example: + /// For example: /// @substituted <τ_0_0> () -> @out τ_0_0 for /// is lowered to this inside its module: /// @substituted <τ_0_0> () -> @out τ_0_0 for /// and this outside its module /// @substituted <τ_0_0> () -> @out τ_0_0 for - public var loweredFunctionType: CanonicalType { + public var loweredFunctionTypeInContext: CanonicalType { CanonicalType(bridged: bridged.getLoweredFunctionTypeInContext()) } @@ -77,13 +90,18 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash /// Returns true if the function is a definition and not only an external declaration. /// - /// This is the case if the functioun contains a body, i.e. some basic blocks. + /// This is the case if the function contains a body, i.e. some basic blocks. public var isDefinition: Bool { blocks.first != nil } public var blocks : BasicBlockList { BasicBlockList(first: bridged.getFirstBlock().block) } public var entryBlock: BasicBlock { blocks.first! } + public func appendNewBlock(_ context: some MutatingContext) -> BasicBlock { + context.notifyBranchesChanged() + return context._bridged.appendBlock(bridged).block + } + public var arguments: LazyMapSequence { entryBlock.arguments.lazy.map { $0 as! FunctionArgument } } @@ -153,9 +171,23 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash bridged.hasSemanticsAttr(BridgedStringRef(data: buffer.baseAddress!, count: buffer.count)) } } - public var isSerialized: Bool { bridged.isSerialized() } + public var isSerialized: Bool { + switch serializedKind { + case .notSerialized, .serializedForPackage: + return false + case .serialized: + return true + } + } - public var isAnySerialized: Bool { bridged.isAnySerialized() } + public var isAnySerialized: Bool { + switch serializedKind { + case .notSerialized: + return false + case .serialized, .serializedForPackage: + return true + } + } public enum SerializedKind { case notSerialized, serialized, serializedForPackage @@ -187,7 +219,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash // If Package-CMO is enabled, we serialize package, public, and @usableFromInline decls as // [serialized_for_package]. - // Their bodies must not, however, leak into @inlinable functons (that are [serialized]) + // Their bodies must not, however, leak into @inlinable functions (that are [serialized]) // since they are inlined outside of their defining module. // // If this callee is [serialized_for_package], the caller must be either non-serialized @@ -218,6 +250,34 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash fatalError() } } + public func set(thunkKind: ThunkKind, _ context: some MutatingContext) { + context.notifyEffectsChanged() + switch thunkKind { + case .noThunk: bridged.setThunk(.IsNotThunk) + case .thunk: bridged.setThunk(.IsThunk) + case .reabstractionThunk: bridged.setThunk(.IsReabstractionThunk) + case .signatureOptimizedThunk: bridged.setThunk(.IsSignatureOptimizedThunk) + } + } + + public var accessorKindName: String? { + guard bridged.isAccessor() else { + return nil + } + return StringRef(bridged: bridged.getAccessorName()).string + } + + public var isInitializer: Bool { + return bridged.isInitializer() + } + + public var isDeinitializer: Bool { + return bridged.isDeinitializer() + } + + public var isImplicit: Bool { + return bridged.isImplicit() + } /// True, if the function runs with a swift 5.1 runtime. /// Note that this is function specific, because inlinable functions are de-serialized @@ -229,6 +289,10 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash public var needsStackProtection: Bool { bridged.needsStackProtection() } + public func set(needStackProtection: Bool, _ context: some MutatingContext) { + context.notifyEffectsChanged() + bridged.setNeedStackProtection(needStackProtection) + } public var isDeinitBarrier: Bool { effects.sideEffects?.global.isDeinitBarrier ?? true @@ -241,6 +305,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash case noRuntime case noExistentials case noObjCRuntime + case manualOwnership } public var performanceConstraints: PerformanceConstraints { @@ -251,13 +316,19 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash case .NoRuntime: return .noRuntime case .NoExistentials: return .noExistentials case .NoObjCBridging: return .noObjCRuntime + case .ManualOwnership: return .manualOwnership default: fatalError("unknown performance constraint") } } + public func set(isPerformanceConstraint: Bool, _ context: some MutatingContext) { + context.notifyEffectsChanged() + bridged.setIsPerformanceConstraint(isPerformanceConstraint) + } public enum InlineStrategy { case automatic case never + case heuristicAlways case always } @@ -265,12 +336,27 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash switch bridged.getInlineStrategy() { case .InlineDefault: return .automatic case .NoInline: return .never + case .HeuristicAlwaysInline: return .heuristicAlways case .AlwaysInline: return .always default: fatalError() } } + public enum ABILanguage { + case C + case Swift + } + + public var abi: ABILanguage { + switch bridged.getSILFunctionLanguage() { + case .C: return .C + case .Swift: return .Swift + default: + fatalError() + } + } + public enum SourceFileKind { case library /// A normal .swift file. case main /// A .swift file that can have top-level code. @@ -301,7 +387,7 @@ public func != (lhs: Function, rhs: Function) -> Bool { lhs !== rhs } // Function conventions. extension Function { public var convention: FunctionConvention { - FunctionConvention(for: loweredFunctionType, in: self) + FunctionConvention(for: loweredFunctionTypeInContext, in: self) } public var argumentConventions: ArgumentConventions { @@ -429,8 +515,8 @@ extension Function { } } - // Only to be called by PassContext - public func _modifyEffects(_ body: (inout FunctionEffects) -> ()) { + public func modifyEffects(_ context: some MutatingContext, _ body: (inout FunctionEffects) -> ()) { + context.notifyEffectsChanged() body(&effects) } } @@ -574,6 +660,10 @@ extension Function { atIndex: calleeArgIdx, withConvention: convention) return effects.memory.read + }, + // isDeinitBarrier + { (f: BridgedFunction) -> Bool in + return f.function.getSideEffects().isDeinitBarrier } ) } diff --git a/SwiftCompilerSources/Sources/SIL/FunctionConvention.swift b/SwiftCompilerSources/Sources/SIL/FunctionConvention.swift index a95ea62ee2f1e..eabb84031085b 100644 --- a/SwiftCompilerSources/Sources/SIL/FunctionConvention.swift +++ b/SwiftCompilerSources/Sources/SIL/FunctionConvention.swift @@ -26,10 +26,14 @@ public struct FunctionConvention : CustomStringConvertible { let functionType: CanonicalType let hasLoweredAddresses: Bool - init(for functionType: CanonicalType, in function: Function) { + public init(for functionType: CanonicalType, in function: Function) { + self.init(for: functionType, hasLoweredAddresses: function.hasLoweredAddresses) + } + + public init(for functionType: CanonicalType, hasLoweredAddresses: Bool) { assert(!functionType.hasTypeParameter, "requires contextual type") self.functionType = functionType - self.hasLoweredAddresses = function.hasLoweredAddresses + self.hasLoweredAddresses = hasLoweredAddresses } /// All results including the error. @@ -53,11 +57,15 @@ public struct FunctionConvention : CustomStringConvertible { : SILFunctionType_getNumPackResults(functionType.bridged) } - /// Indirect results including the error. - public var indirectSILResults: LazyFilterSequence { - hasLoweredAddresses - ? results.lazy.filter { $0.isSILIndirect } - : results.lazy.filter { $0.convention == .pack } + /// Returns the indirect result - including the error - at `index`. + public func indirectSILResult(at index: Int) -> ResultInfo { + let indirectResults = results.lazy.filter { + hasLoweredAddresses ? $0.isSILIndirect : $0.convention == .pack + } + // Note that subscripting a LazyFilterCollection (with the base index, e.g. `Int`) does not work + // as expected, because it returns the nth element of the base collection! + // Therefore we need to implement the subscript "manually". + return indirectResults.enumerated().first{ $0.offset == index }!.element } public var parameters: Parameters { @@ -90,6 +98,20 @@ public struct FunctionConvention : CustomStringConvertible { return SILFunctionType_getLifetimeDependencies(functionType.bridged).count() != 0 } + public var hasGuaranteedResult: Bool { + if results.count != 1 { + return false + } + return results[0].convention == .guaranteed + } + + public var hasGuaranteedAddressResult: Bool { + if results.count != 1 { + return false + } + return results[0].convention == .guaranteedAddress + } + public var description: String { var str = functionType.description for paramIdx in 0.. Value { fatalError() } @@ -83,6 +92,16 @@ public class Instruction : CustomStringConvertible, Hashable { return Location(bridged: bridged.getLocation()) } + public final func move(before otherInstruction: Instruction, _ context: some MutatingContext) { + BridgedContext.moveInstructionBefore(bridged, otherInstruction.bridged) + context.notifyInstructionsChanged() + } + + public final func copy(before otherInstruction: Instruction, _ context: some MutatingContext) { + BridgedContext.copyInstructionBefore(bridged, otherInstruction.bridged) + context.notifyInstructionsChanged() + } + public var mayTrap: Bool { false } final public var mayHaveSideEffects: Bool { @@ -149,7 +168,8 @@ public class Instruction : CustomStringConvertible, Hashable { /// their operand. public final var isIncidentalUse: Bool { switch self { - case is DebugValueInst, is FixLifetimeInst, is EndLifetimeInst: + case is DebugValueInst, is FixLifetimeInst, is EndLifetimeInst, + is IgnoredUseInst: return true default: return isEndOfScopeMarker @@ -162,6 +182,10 @@ public class Instruction : CustomStringConvertible, Hashable { public static func ==(lhs: Instruction, rhs: Instruction) -> Bool { lhs === rhs } + + public func isIdenticalTo(_ otherInst: Instruction) -> Bool { + return bridged.isIdenticalTo(otherInst.bridged) + } public func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) @@ -205,6 +229,12 @@ public class SingleValueInstruction : Instruction, Value { } public var isLexical: Bool { false } + + /// Replaces all uses with `replacement` and then erases the instruction. + public final func replace(with replacement: Value, _ context: some MutatingContext) { + uses.replaceAll(with: replacement, context) + context.erase(instruction: self) + } } public final class MultipleValueInstructionResult : Value, Hashable { @@ -246,6 +276,14 @@ public class MultipleValueInstruction : Instruction { fileprivate final override func getResult(index: Int) -> Value { bridged.MultipleValueInstruction_getResult(index).result } + + /// Replaces all uses with the result of `replacement` and then erases the instruction. + public final func replace(with replacement: MultipleValueInstruction, _ context: some MutatingContext) { + for (origResult, newResult) in zip(self.results, replacement.results) { + origResult.uses.replaceAll(with: newResult, context) + } + context.erase(instruction: self) + } } /// Instructions, which have a single operand (not including type-dependent operands). @@ -309,16 +347,14 @@ final public class AssignInst : Instruction, StoringInstruction { } } -final public class AssignByWrapperInst : Instruction, StoringInstruction {} - final public class AssignOrInitInst : Instruction, StoringInstruction {} /// Instruction that copy or move from a source to destination address. public protocol SourceDestAddrInstruction : Instruction { var sourceOperand: Operand { get } var destinationOperand: Operand { get } - var isTakeOfSrc: Bool { get } - var isInitializationOfDest: Bool { get } + var isTakeOfSource: Bool { get } + var isInitializationOfDestination: Bool { get } } extension SourceDestAddrInstruction { @@ -329,22 +365,33 @@ extension SourceDestAddrInstruction { } final public class CopyAddrInst : Instruction, SourceDestAddrInstruction { - public var isTakeOfSrc: Bool { + public var isTakeOfSource: Bool { bridged.CopyAddrInst_isTakeOfSrc() } - public var isInitializationOfDest: Bool { + public func set(isTakeOfSource: Bool, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.CopyAddrInst_setIsTakeOfSrc(isTakeOfSource) + context.notifyInstructionChanged(self) + } + + public var isInitializationOfDestination: Bool { bridged.CopyAddrInst_isInitializationOfDest() } + public func set(isInitializationOfDestination: Bool, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.CopyAddrInst_setIsInitializationOfDest(isInitializationOfDestination) + context.notifyInstructionChanged(self) + } } final public class ExplicitCopyAddrInst : Instruction, SourceDestAddrInstruction { public var source: Value { return sourceOperand.value } public var destination: Value { return destinationOperand.value } - public var isTakeOfSrc: Bool { + public var isTakeOfSource: Bool { bridged.ExplicitCopyAddrInst_isTakeOfSrc() } - public var isInitializationOfDest: Bool { + public var isInitializationOfDestination: Bool { bridged.ExplicitCopyAddrInst_isInitializationOfDest() } } @@ -449,17 +496,17 @@ public enum VariableScopeInstruction { scopeBegin.uses.lazy.filter { $0.endsLifetime || $0.instruction is ExtendLifetimeInst } } - // TODO: with SIL verification, we might be able to make varDecl non-Optional. - public var varDecl: VarDecl? { - if let debugVarDecl = instruction.debugVarDecl { - return debugVarDecl - } + // TODO: assert that VarDecl is valid whenever isFromVarDecl returns tyue. + public func findVarDecl() -> VarDecl? { // SILGen may produce double var_decl instructions for the same variable: // %box = alloc_box [var_decl] "x" // begin_borrow %box [var_decl] // - // Assume that, if the begin_borrow or move_value does not have its own debug_value, then it must actually be - // associated with its operand's var_decl. + // Therefore, first check if begin_borrow or move_value has any debug_value users. + if let debugVarDecl = instruction.findVarDeclFromDebugUsers() { + return debugVarDecl + } + // Otherwise, assume that the var_decl is associated with its operand's var_decl. return instruction.operands[0].value.definingInstruction?.findVarDecl() } } @@ -471,17 +518,33 @@ extension Instruction { return varDeclInst.varDecl } if let varScopeInst = VariableScopeInstruction(self) { - return varScopeInst.varDecl + return varScopeInst.findVarDecl() } - return debugVarDecl + return findVarDeclFromDebugUsers() } - var debugVarDecl: VarDecl? { + func findVarDeclFromDebugUsers() -> VarDecl? { for result in results { - for use in result.uses { - if let debugVal = use.instruction as? DebugValueInst { - return debugVal.varDecl - } + if let varDecl = result.findVarDeclFromDebugUsers() { + return varDecl + } + } + return nil + } +} + +extension Value { + public func findVarDecl() -> VarDecl? { + if let arg = self as? Argument { + return arg.findVarDecl() + } + return findVarDeclFromDebugUsers() + } + + func findVarDeclFromDebugUsers() -> VarDecl? { + for use in uses { + if let debugVal = use.instruction as? DebugValueInst { + return debugVal.varDecl } } return nil @@ -525,16 +588,12 @@ final public class UnconditionalCheckedCastAddrInst : Instruction, SourceDestAdd CanonicalType(bridged: bridged.UnconditionalCheckedCastAddr_getTargetFormalType()) } - public var isTakeOfSrc: Bool { true } - public var isInitializationOfDest: Bool { true } + public var isTakeOfSource: Bool { true } + public var isInitializationOfDestination: Bool { true } public override var mayTrap: Bool { true } - public var isolatedConformances: CastingIsolatedConformances { - switch bridged.UnconditionalCheckedCastAddr_getIsolatedConformances() { - case .Allow: .allow - case .Prohibit: .prohibit - @unknown default: fatalError("Unhandled CastingIsolatedConformances") - } + public var checkedCastOptions: CheckedCastInstOptions { + .init(storage: bridged.UnconditionalCheckedCastAddr_getCheckedCastOptions().storage) } } @@ -550,6 +609,12 @@ final public class RebindMemoryInst : SingleValueInstruction {} public class RefCountingInst : Instruction, UnaryInstruction { public var isAtomic: Bool { bridged.RefCountingInst_getIsAtomic() } + + public final func setAtomicity(isAtomic: Bool, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.RefCountingInst_setIsAtomic(isAtomic) + context.notifyInstructionChanged(self) + } } final public class StrongRetainInst : RefCountingInst { @@ -615,6 +680,7 @@ final public class ExtendLifetimeInst : Instruction, UnaryInstruction {} final public class InjectEnumAddrInst : Instruction, UnaryInstruction, EnumInstruction { public var `enum`: Value { operand.value } public var caseIndex: Int { bridged.InjectEnumAddrInst_caseIndex() } + public var element: EnumElementDecl { bridged.InjectEnumAddrInst_element().getAs(EnumElementDecl.self) } } //===----------------------------------------------------------------------===// @@ -630,11 +696,7 @@ extension Deallocation { } -final public class DeallocStackInst : Instruction, UnaryInstruction, Deallocation { - public var allocstack: AllocStackInst { - return operand.value as! AllocStackInst - } -} +final public class DeallocStackInst : Instruction, UnaryInstruction, Deallocation {} final public class DeallocStackRefInst : Instruction, UnaryInstruction, Deallocation { public var allocRef: AllocRefInstBase { operand.value as! AllocRefInstBase } @@ -644,7 +706,9 @@ final public class DeallocRefInst : Instruction, UnaryInstruction, Deallocation final public class DeallocPartialRefInst : Instruction, Deallocation {} -final public class DeallocBoxInst : Instruction, UnaryInstruction, Deallocation {} +final public class DeallocBoxInst : Instruction, UnaryInstruction, Deallocation { + public var isDeadEnd: Bool { bridged.DeallocBoxInst_isDeadEnd() } +} final public class DeallocExistentialBoxInst : Instruction, UnaryInstruction, Deallocation {} @@ -666,6 +730,11 @@ final public class LoadInst : SingleValueInstruction, LoadInstruction { public var loadOwnership: LoadOwnership { LoadOwnership(rawValue: bridged.LoadInst_getLoadOwnership())! } + public func set(ownership: LoadInst.LoadOwnership, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.LoadInst_setOwnership(ownership.rawValue) + context.notifyInstructionChanged(self) + } } final public class LoadWeakInst : SingleValueInstruction, LoadInstruction {} @@ -706,8 +775,8 @@ class UncheckedRefCastInst : SingleValueInstruction, UnaryInstruction { final public class UncheckedRefCastAddrInst : Instruction, SourceDestAddrInstruction { - public var isTakeOfSrc: Bool { true } - public var isInitializationOfDest: Bool { true } + public var isTakeOfSource: Bool { true } + public var isInitializationOfDestination: Bool { true } } final public class UncheckedAddrCastInst : SingleValueInstruction, UnaryInstruction { @@ -719,7 +788,9 @@ final public class UncheckedTrivialBitCastInst : SingleValueInstruction, UnaryIn } final public class UncheckedBitwiseCastInst : SingleValueInstruction, UnaryInstruction {} -final public class UncheckedValueCastInst : SingleValueInstruction, UnaryInstruction {} +final public class UncheckedValueCastInst : SingleValueInstruction, UnaryInstruction { + public var fromValue: Value { operand.value } +} final public class RefToRawPointerInst : SingleValueInstruction, UnaryInstruction {} final public class RefToUnmanagedInst : SingleValueInstruction, UnaryInstruction {} @@ -754,6 +825,11 @@ class PointerToAddressInst : SingleValueInstruction, UnaryInstruction { } return Int(exactly: maybeAlign) } + public func set(alignment: Int?, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.PointerToAddressInst_setAlignment(UInt64(alignment ?? 0)) + context.notifyInstructionChanged(self) + } } public protocol IndexingInstruction: SingleValueInstruction { @@ -812,7 +888,9 @@ final public class DeinitExistentialValueInst : Instruction {} final public -class OpenExistentialAddrInst : SingleValueInstruction, UnaryInstruction {} +class OpenExistentialAddrInst : SingleValueInstruction, UnaryInstruction { + public var isImmutable: Bool { bridged.OpenExistentialAddr_isImmutable() } +} final public class OpenExistentialBoxInst : SingleValueInstruction, UnaryInstruction {} @@ -843,8 +921,12 @@ final public class TypeValueInst: SingleValueInstruction, UnaryInstruction { CanonicalType(bridged: bridged.TypeValueInst_getParamType()) } - public var value: Int { - bridged.TypeValueInst_getValue() + /// Returns the value of the Integer type is known and fits into an `Int`. + public var value: Int? { + if paramType.isInteger { + return paramType.valueOfInteger + } + return nil } } @@ -881,10 +963,22 @@ final public class GlobalAddrInst : GlobalAccessInstruction, VarDeclInstruction public var dependencyToken: Value? { operands.count == 1 ? operands[0].value : nil } + + public func clearToken(_ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.GlobalAddrInst_clearToken() + context.notifyInstructionChanged(self) + } } final public class GlobalValueInst : GlobalAccessInstruction { public var isBare: Bool { bridged.GlobalValueInst_isBare() } + + public func setIsBare(_ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.GlobalValueInst_setIsBare() + context.notifyInstructionChanged(self) + } } final public class BaseAddrForOffsetInst : SingleValueInstruction {} @@ -1001,6 +1095,12 @@ final public class RefElementAddrInst : SingleValueInstruction, UnaryInstruction public var isImmutable: Bool { bridged.RefElementAddrInst_isImmutable() } + public func set(isImmutable: Bool, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.RefElementAddrInst_setImmutable(isImmutable) + context.notifyInstructionChanged(self) + } + public var varDecl: VarDecl? { bridged.RefElementAddr_getDecl().getAs(VarDecl.self) } @@ -1013,6 +1113,14 @@ final public class RefTailAddrInst : SingleValueInstruction, UnaryInstruction { } final public class KeyPathInst : SingleValueInstruction { + public var substitutionMap: SubstitutionMap { + SubstitutionMap(bridged: bridged.KeyPathInst_getSubstitutionMap()) + } + + public var hasPattern: Bool { + bridged.KeyPathInst_hasPattern() + } + public override func visitReferencedFunctions(_ cl: (Function) -> ()) { var results = BridgedInstruction.KeyPathFunctionResults() for componentIdx in 0...Elements, Value> { enclosingOperands.values } + + public var scopeEndingOperands: LazyFilterSequence { uses.endingLifetime } } final public class ProjectBoxInst : SingleValueInstruction, UnaryInstruction { @@ -1223,6 +1321,10 @@ final public class EndCOWMutationInst : SingleValueInstruction, UnaryInstruction public var doKeepUnique: Bool { bridged.EndCOWMutationInst_doKeepUnique() } } +final public class EndCOWMutationAddrInst : Instruction, UnaryInstruction { + public var address: Value { operand.value } +} + final public class ClassifyBridgeObjectInst : SingleValueInstruction, UnaryInstruction {} @@ -1290,13 +1392,47 @@ final public class IsUniqueInst : SingleValueInstruction, UnaryInstruction {} final public class DestroyNotEscapedClosureInst : SingleValueInstruction, UnaryInstruction {} -final public class MarkUnresolvedNonCopyableValueInst: SingleValueInstruction, UnaryInstruction {} +final public class MarkUnresolvedNonCopyableValueInst: SingleValueInstruction, UnaryInstruction { + // The raw values must match swift::MarkUnresolvedNonCopyableValueInst::CheckKind + public enum CheckKind: Int { + case invalid = 0 + + /// A signal to the move only checker to perform checking that allows for + /// this value to be consumed along its boundary (in the case of let/var + /// semantics) and also written over in the case of var semantics. NOTE: Of + /// course this still implies the value cannot be copied and can be consumed + /// only once along all program paths. + case consumableAndAssignable + + /// A signal to the move only checker to perform no consume or assign + /// checking. This forces the result of this instruction owned value to + /// never be consumed (for let/var semantics) or assigned over (for var + /// semantics). Of course, we still allow for non-consuming uses. + case noConsumeOrAssign + + /// A signal to the move checker that the given value cannot be consumed, + /// but is allowed to be assigned over. This is used for situations like + /// global_addr/ref_element_addr/closure escape where we do not want to + /// allow for the user to take the value (leaving the memory in an + /// uninitialized state), but we are ok with the user assigning a new value, + /// completely assigning over the value at once. + case assignableButNotConsumable + + /// A signal to the move checker that the given value cannot be consumed or + /// assigned, but is allowed to be initialized. This is used for situations + /// like class initializers. + case initableButNotConsumable + } + + var checkKind: CheckKind { CheckKind(rawValue: bridged.MarkUnresolvedNonCopyableValue_getCheckKind())! } + var isStrict: Bool { bridged.MarkUnresolvedNonCopyableValue_isStrict() } +} final public class MarkUnresolvedReferenceBindingInst : SingleValueInstruction {} final public class MarkUnresolvedMoveAddrInst : Instruction, SourceDestAddrInstruction { - public var isTakeOfSrc: Bool { true } - public var isInitializationOfDest: Bool { true } + public var isTakeOfSource: Bool { true } + public var isInitializationOfDestination: Bool { true } } final public class CopyableToMoveOnlyWrapperValueInst: SingleValueInstruction, UnaryInstruction {} @@ -1325,6 +1461,10 @@ final public class ObjectInst : SingleValueInstruction { final public class VectorInst : SingleValueInstruction { } +final public class VectorBaseAddrInst : SingleValueInstruction, UnaryInstruction { + public var vector: Value { operand.value } +} + final public class DifferentiableFunctionInst: SingleValueInstruction {} final public class LinearFunctionInst: SingleValueInstruction {} @@ -1346,6 +1486,7 @@ final public class AllocStackInst : SingleValueInstruction, Allocation, DebugVar public var hasDynamicLifetime: Bool { bridged.AllocStackInst_hasDynamicLifetime() } public var isFromVarDecl: Bool { bridged.AllocStackInst_isFromVarDecl() } public var usesMoveableValueDebugInfo: Bool { bridged.AllocStackInst_usesMoveableValueDebugInfo() } + public override var isLexical: Bool { bridged.AllocStackInst_isLexical() } public var varDecl: VarDecl? { bridged.AllocStack_getDecl().getAs(VarDecl.self) @@ -1355,7 +1496,7 @@ final public class AllocStackInst : SingleValueInstruction, Allocation, DebugVar return bridged.AllocStack_hasVarInfo() ? bridged.AllocStack_getVarInfo() : nil } - public var deallocations: LazyMapSequence, Instruction> { + public var deallocations: LazyMapSequence, DeallocStackInst> { uses.users(ofType: DeallocStackInst.self) } } @@ -1367,6 +1508,12 @@ public class AllocRefInstBase : SingleValueInstruction, Allocation { bridged.AllocRefInstBase_canAllocOnStack() } + public final func setIsStackAllocatable(_ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.AllocRefInstBase_setIsStackAllocatable() + context.notifyInstructionChanged(self) + } + final public var tailAllocatedCounts: OperandArray { let numTailTypes = bridged.AllocRefInstBase_getNumTailTypes() return operands[0.. { get } +public protocol ScopedInstruction: Instruction { + var scopeEndingOperands: LazyFilterSequence { get } var endInstructions: EndInstructions { get } } @@ -1425,43 +1578,32 @@ extension Instruction { /// Return the sequence of use points of any instruction. public var endInstructions: EndInstructions { if let scopedInst = self as? ScopedInstruction { - return .scoped(scopedInst.endOperands.users) + return .scoped(scopedInst.scopeEndingOperands.users) } return .single(self) } } -/// Instructions beginning a borrow-scope which must be ended by `end_borrow`. -public protocol BorrowIntroducingInstruction : SingleValueInstruction, ScopedInstruction { -} - -extension BorrowIntroducingInstruction { - public var instruction: Instruction { get { self } } +/// Single-value instructions beginning a borrow-scope which end with an `end_borrow` or a branch to a re-borrow phi. +/// See also `BeginBorrowValue` which represents all kind of `Value`s which begin a borrow scope. +public protocol BeginBorrowInstruction : SingleValueInstruction, ScopedInstruction { } final public class EndBorrowInst : Instruction, UnaryInstruction { public var borrow: Value { operand.value } } -extension BorrowIntroducingInstruction { - public var endOperands: LazyFilterSequence { - return uses.lazy.filter { $0.instruction is EndBorrowInst } - } -} - -final public class BeginBorrowInst : SingleValueInstruction, UnaryInstruction, BorrowIntroducingInstruction { +final public class BeginBorrowInst : SingleValueInstruction, UnaryInstruction, BeginBorrowInstruction { public var borrowedValue: Value { operand.value } public override var isLexical: Bool { bridged.BeginBorrow_isLexical() } public var hasPointerEscape: Bool { bridged.BeginBorrow_hasPointerEscape() } public var isFromVarDecl: Bool { bridged.BeginBorrow_isFromVarDecl() } - public var endOperands: LazyFilterSequence { - return uses.endingLifetime - } + public var scopeEndingOperands: LazyFilterSequence { uses.endingLifetime } } -final public class LoadBorrowInst : SingleValueInstruction, LoadInstruction, BorrowIntroducingInstruction { +final public class LoadBorrowInst : SingleValueInstruction, LoadInstruction, BeginBorrowInstruction { // True if the invariants on `load_borrow` have not been checked and should not be strictly enforced. // @@ -1469,10 +1611,12 @@ final public class LoadBorrowInst : SingleValueInstruction, LoadInstruction, Bor // code using noncopyable types that consumes or mutates a memory location while that location is borrowed, // but the move-only checker must diagnose those problems before canonical SIL is formed. public var isUnchecked: Bool { bridged.LoadBorrowInst_isUnchecked() } + + public var scopeEndingOperands: LazyFilterSequence { uses.endingLifetime } } -final public class StoreBorrowInst : SingleValueInstruction, StoringInstruction, BorrowIntroducingInstruction { - var allocStack: AllocStackInst { +final public class StoreBorrowInst : SingleValueInstruction, StoringInstruction, BeginBorrowInstruction { + public var allocStack: AllocStackInst { var dest = destination if let mark = dest as? MarkUnresolvedNonCopyableValueInst { dest = mark.operand.value @@ -1480,7 +1624,13 @@ final public class StoreBorrowInst : SingleValueInstruction, StoringInstruction, return dest as! AllocStackInst } - public var endBorrows: LazyMapSequence, Instruction> { + public var scopeEndingOperands: LazyFilterSequence { + return self.uses.lazy.filter { $0.instruction is EndBorrowInst } + } + + public var endBorrows: LazyMapSequence, EndBorrowInst> { + // A `store_borrow` is an address value. + // Only `end_borrow`s (with this address operand) can end such a borrow scope. uses.users(ofType: EndBorrowInst.self) } } @@ -1496,6 +1646,38 @@ final public class BeginAccessInst : SingleValueInstruction, UnaryInstruction { public var accessKind: AccessKind { AccessKind(rawValue: bridged.BeginAccessInst_getAccessKind())! } + public func set(accessKind: AccessKind, context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.BeginAccess_setAccessKind(accessKind.rawValue) + context.notifyInstructionChanged(self) + } + + // The raw values must match SILAccessEnforcement. + public enum Enforcement: Int { + /// The access's enforcement has not yet been determined. + case unknown = 0 + + /// The access is statically known to not conflict with other accesses. + case `static` = 1 + + /// The access is not statically known to not conflict with anything and must be dynamically checked. + case dynamic = 2 + + /// The access is not statically known to not conflict with anything but dynamic checking should + /// be suppressed, leaving it undefined behavior. + case unsafe = 3 + + /// Access to pointers that are signed via pointer authentication. + case signed = 4 + } + public var enforcement: Enforcement { + Enforcement(rawValue: bridged.BeginAccessInst_getEnforcement())! + } + public func set(enforcement: Enforcement, context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.BeginAccess_setEnforcement(enforcement.rawValue) + context.notifyInstructionChanged(self) + } public var isStatic: Bool { bridged.BeginAccessInst_isStatic() } public var isUnsafe: Bool { bridged.BeginAccessInst_isUnsafe() } @@ -1505,7 +1687,7 @@ final public class BeginAccessInst : SingleValueInstruction, UnaryInstruction { public typealias EndAccessInstructions = LazyMapSequence, EndAccessInst> public var endAccessInstructions: EndAccessInstructions { - endOperands.map { $0.instruction as! EndAccessInst } + scopeEndingOperands.map { $0.instruction as! EndAccessInst } } } @@ -1516,9 +1698,7 @@ final public class EndAccessInst : Instruction, UnaryInstruction { } extension BeginAccessInst : ScopedInstruction { - public var instruction: Instruction { get { self } } - - public var endOperands: LazyFilterSequence { + public var scopeEndingOperands: LazyFilterSequence { return uses.lazy.filter { $0.instruction is EndAccessInst } } } @@ -1531,15 +1711,19 @@ final public class EndUnpairedAccessInst : Instruction {} final public class BeginApplyInst : MultipleValueInstruction, FullApplySite { public var numArguments: Int { bridged.BeginApplyInst_numArguments() } + public var isCalleeAllocated: Bool { bridged.BeginApplyInst_isCalleeAllocated() } public var singleDirectResult: Value? { nil } public var singleDirectErrorResult: Value? { nil } - public var token: Value { getResult(index: resultCount - 1) } + public var token: Value { getResult(index: resultCount - (isCalleeAllocated ? 2 : 1)) } public var yieldedValues: Results { - Results(inst: self, numResults: resultCount - 1) + Results(inst: self, numResults: resultCount - (isCalleeAllocated ? 2 : 1)) } + + public var isNonThrowing: Bool { bridged.BeginApplyInst_getNonThrowing() } + public var isNonAsync: Bool { bridged.BeginApplyInst_getNonAsync() } } final public class EndApplyInst : SingleValueInstruction, UnaryInstruction { @@ -1553,9 +1737,7 @@ final public class AbortApplyInst : Instruction, UnaryInstruction { } extension BeginApplyInst : ScopedInstruction { - public var instruction: Instruction { get { self } } - - public var endOperands: LazyFilterSequence { + public var scopeEndingOperands: LazyFilterSequence { return token.uses.lazy.filter { $0.isScopeEndingUse } } } @@ -1660,6 +1842,11 @@ public class TermInst : Instruction { } public var isFunctionExiting: Bool { false } + + public final func replaceBranchTarget(from fromBlock: BasicBlock, to toBlock: BasicBlock, _ context: some MutatingContext) { + context.notifyBranchesChanged() + bridged.TermInst_replaceBranchTarget(fromBlock.bridged, toBlock.bridged) + } } final public class UnreachableInst : TermInst { @@ -1680,6 +1867,9 @@ final public class ThrowAddrInst : TermInst { } final public class YieldInst : TermInst { + public func convention(of operand: Operand) -> ArgumentConvention { + return bridged.YieldInst_getConvention(operand.bridged).convention + } } final public class UnwindInst : TermInst { @@ -1787,16 +1977,21 @@ final public class DynamicMethodBranchInst : TermInst { final public class AwaitAsyncContinuationInst : TermInst, UnaryInstruction { } +public struct CheckedCastInstOptions { + var storage: UInt8 = 0 + + var bridged: BridgedInstruction.CheckedCastInstOptions { + .init(storage: storage) + } + + var isolatedConformances: CastingIsolatedConformances { + return (storage & 0x01) != 0 ? .prohibit : .allow + } +} + public enum CastingIsolatedConformances { case allow case prohibit - - var bridged: BridgedInstruction.CastingIsolatedConformances { - switch self { - case .allow: return .Allow - case .prohibit: return .Prohibit - } - } } final public class CheckedCastBranchInst : TermInst, UnaryInstruction { @@ -1808,12 +2003,8 @@ final public class CheckedCastBranchInst : TermInst, UnaryInstruction { bridged.CheckedCastBranch_updateSourceFormalTypeFromOperandLoweredType() } - public var isolatedConformances: CastingIsolatedConformances { - switch bridged.CheckedCastBranch_getIsolatedConformances() { - case .Allow: return .allow - case .Prohibit: return .prohibit - default: fatalError("Bad CastingIsolatedConformances value") - } + public var checkedCastOptions: CheckedCastInstOptions { + .init(storage: bridged.CheckedCastBranch_getCheckedCastOptions().storage) } } @@ -1856,12 +2047,8 @@ final public class CheckedCastAddrBranchInst : TermInst { } } - public var isolatedConformances: CastingIsolatedConformances { - switch bridged.CheckedCastAddrBranch_getIsolatedConformances() { - case .Allow: .allow - case .Prohibit: .prohibit - @unknown default: fatalError("Unhandled CastingIsolatedConformances") - } + public var checkedCastOptions: CheckedCastInstOptions { + .init(storage: bridged.CheckedCastAddrBranch_getCheckedCastOptions().storage) } } diff --git a/SwiftCompilerSources/Sources/SIL/Location.swift b/SwiftCompilerSources/Sources/SIL/Location.swift index 62e2245739792..51844f88ff49f 100644 --- a/SwiftCompilerSources/Sources/SIL/Location.swift +++ b/SwiftCompilerSources/Sources/SIL/Location.swift @@ -11,8 +11,21 @@ //===----------------------------------------------------------------------===// import SILBridging +import AST -public struct Location: Equatable, CustomStringConvertible { +/// Represents a location in source code. +/// `Location` is used in SIL and by the Optimizer. +/// +/// When compiling a Swift file, `Location` is basically a `SourceLoc` + information about the +/// containing debug scope. In this case the `SourceLoc` is directly retrieved from the AST nodes. +/// +/// However, for debug info which is de-serialized from a swiftmodule file, the location consists of +/// a filename + line and column indices. From such a location, a `SourceLoc` can only be created by +/// loading the file with `DiagnosticEngine.getLocationFromExternalSource`. +/// +/// In case of parsing textual SIL (e.g. with `sil-opt`), which does _not_ contain debug line +/// information, the location is also a `SourceLoc` which points to the textual SIL. +public struct Location: ProvidingSourceLocation, Equatable, CustomStringConvertible { let bridged: BridgedLocation public var description: String { @@ -20,20 +33,52 @@ public struct Location: Equatable, CustomStringConvertible { } public var sourceLoc: SourceLoc? { - return SourceLoc(bridged: bridged.getSourceLocation()) + if hasValidLineNumber { + return SourceLoc(bridged: bridged.getSourceLocation()) + } + return nil + } + + public var fileNameAndPosition: (path: StringRef, line: Int, column: Int)? { + if bridged.isFilenameAndLocation() { + let loc = bridged.getFilenameAndLocation() + return (StringRef(bridged: loc.path), loc.line, loc.column) + } + return nil + } + + public func getSourceLocation(diagnosticEngine: DiagnosticEngine) -> SourceLoc? { + if let sourceLoc = sourceLoc { + return sourceLoc + } + if let (path, line, column) = fileNameAndPosition { + return diagnosticEngine.getLocationFromExternalSource(path: path, line: line, column: column) + } + return nil } /// Keeps the debug scope but marks it as auto-generated. - public var autoGenerated: Location { + public var asAutoGenerated: Location { Location(bridged: bridged.getAutogeneratedLocation()) } + public var asCleanup: Location { + Location(bridged: bridged.getCleanupLocation()) + } + + public func withScope(of other: Location) -> Location { + Location(bridged: bridged.withScopeOf(other.bridged)) + } + public var hasValidLineNumber: Bool { bridged.hasValidLineNumber() } public var isAutoGenerated: Bool { bridged.isAutoGenerated() } public var isInlined: Bool { bridged.isInlined() } public var isDebugSteppable: Bool { hasValidLineNumber && !isAutoGenerated } + /// The `Decl` if the location refers to a declaration. + public var decl: Decl? { bridged.getDecl().decl } + public static func ==(lhs: Location, rhs: Location) -> Bool { lhs.bridged.isEqualTo(rhs.bridged) } diff --git a/SwiftCompilerSources/Sources/SIL/Operand.swift b/SwiftCompilerSources/Sources/SIL/Operand.swift index 541b6f8f6a5d4..cda27da5f934c 100644 --- a/SwiftCompilerSources/Sources/SIL/Operand.swift +++ b/SwiftCompilerSources/Sources/SIL/Operand.swift @@ -27,6 +27,10 @@ public struct Operand : CustomStringConvertible, NoReflectionChildren, Equatable public var value: Value { bridged.getValue().value } + public func set(to value: Value, _ context: some MutatingContext) { + instruction.setOperand(at: index, to: value, context) + } + public static func ==(lhs: Operand, rhs: Operand) -> Bool { return lhs.bridged.op == rhs.bridged.op } @@ -45,6 +49,12 @@ public struct Operand : CustomStringConvertible, NoReflectionChildren, Equatable public func canAccept(ownership: Ownership) -> Bool { bridged.canAcceptOwnership(ownership._bridged) } + public func changeOwnership(from: Ownership, to: Ownership, _ context: some MutatingContext) { + context.notifyInstructionsChanged() + bridged.changeOwnership(from._bridged, to._bridged) + context.notifyInstructionChanged(instruction) + } + public var description: String { "operand #\(index) of \(instruction)" } } @@ -94,10 +104,6 @@ public struct OperandArray : RandomAccessCollection, CustomReflectable { base: OptionalBridgedOperand(op: base.advancedBy(bounds.lowerBound).op), count: bounds.upperBound - bounds.lowerBound) } - - public var values: LazyMapSequence.Elements, Value> { - self.lazy.map { $0.value } - } } public struct UseList : CollectionLikeSequence { @@ -133,6 +139,10 @@ public struct UseList : CollectionLikeSequence { } extension Sequence where Element == Operand { + public var values: LazyMapSequence { + self.lazy.map { $0.value } + } + public var singleUse: Operand? { var result: Operand? = nil for op in self { @@ -154,11 +164,15 @@ extension Sequence where Element == Operand { self.lazy.filter { !($0.instruction is DebugValueInst) } } - public func filterUsers(ofType: I.Type) -> LazyFilterSequence { + public func filterUses(ofType: I.Type) -> LazyFilterSequence { self.lazy.filter { $0.instruction is I } } - public func ignoreUsers(ofType: I.Type) -> LazyFilterSequence { + public func filterUsers(ofType: I.Type) -> LazyMapSequence, I> { + self.lazy.filter { $0.instruction is I }.lazy.map { $0.instruction as! I } + } + + public func ignoreUses(ofType: I.Type) -> LazyFilterSequence { self.lazy.filter { !($0.instruction is I) } } @@ -167,11 +181,11 @@ extension Sequence where Element == Operand { } public func getSingleUser(ofType: I.Type) -> I? { - filterUsers(ofType: I.self).singleUse?.instruction as? I + filterUses(ofType: I.self).singleUse?.instruction as? I } public func getSingleUser(notOfType: I.Type) -> Instruction? { - ignoreUsers(ofType: I.self).singleUse?.instruction + ignoreUses(ofType: I.self).singleUse?.instruction } public var endingLifetime: LazyFilterSequence { @@ -186,10 +200,10 @@ extension Sequence where Element == Operand { self.lazy.filter{ $0.instruction is I }.lazy.map { $0.instruction as! I } } - // This overload which returns a Sequence of `Instruction` and not a Sequence of `I` is used for APIs, like - // `InstructionSet.insert(contentsOf:)`, which require a sequence of `Instruction`. - public func users(ofType: I.Type) -> LazyMapSequence, Instruction> { - self.lazy.filter{ $0.instruction is I }.users + public func replaceAll(with replacement: Value, _ context: some MutatingContext) { + for use in self { + use.set(to: replacement, context) + } } } @@ -197,6 +211,12 @@ extension Value { public var users: LazyMapSequence { uses.users } } +extension Instruction { + public func isUsing(_ value: Value) -> Bool { + return operands.contains { $0.value == value } + } +} + extension Operand { /// Return true if this operation will store a full value into this /// operand's address. @@ -262,7 +282,7 @@ public enum OperandOwnership { /// Escape a pointer into a value which cannot be tracked or verified. /// - /// PointerEscape operands indicate a SIL deficiency to suffuciently model dependencies. They never arise from user-level escapes. + /// PointerEscape operands indicate a SIL deficiency to sufficiently model dependencies. They never arise from user-level escapes. case pointerEscape /// Bitwise escape. Escapes the nontrivial contents of the value. OSSA does not enforce the lifetime of the escaping bits. The programmer must explicitly force lifetime extension. (ref_to_unowned, unchecked_trivial_bitcast) diff --git a/SwiftCompilerSources/Sources/SIL/Registration.swift b/SwiftCompilerSources/Sources/SIL/Registration.swift index 3a6b1c7c73960..25cdfcab10060 100644 --- a/SwiftCompilerSources/Sources/SIL/Registration.swift +++ b/SwiftCompilerSources/Sources/SIL/Registration.swift @@ -13,6 +13,11 @@ import Basic import SILBridging +public func registerSIL() { + registerSILClasses() + registerUtilities() +} + private func register(_ cl: T.Type) { "\(cl)"._withBridgedStringRef { nameStr in let metatype = unsafeBitCast(cl, to: SwiftMetatype.self) @@ -20,7 +25,7 @@ private func register(_ cl: T.Type) { } } -public func registerSILClasses() { +private func registerSILClasses() { Function.register() register(BasicBlock.self) register(GlobalVariable.self) @@ -38,7 +43,6 @@ public func registerSILClasses() { register(StoreUnownedInst.self) register(StoreBorrowInst.self) register(AssignInst.self) - register(AssignByWrapperInst.self) register(AssignOrInitInst.self) register(CopyAddrInst.self) register(ExplicitCopyAddrInst.self) @@ -118,6 +122,7 @@ public func registerSILClasses() { register(MoveOnlyWrapperToCopyableAddrInst.self) register(ObjectInst.self) register(VectorInst.self) + register(VectorBaseAddrInst.self) register(TuplePackExtractInst.self) register(TuplePackElementAddrInst.self) register(PackElementGetInst.self) @@ -215,6 +220,7 @@ public func registerSILClasses() { register(MoveValueInst.self) register(DropDeinitInst.self) register(EndCOWMutationInst.self) + register(EndCOWMutationAddrInst.self) register(ClassifyBridgeObjectInst.self) register(PartialApplyInst.self) register(ApplyInst.self) @@ -260,3 +266,8 @@ public func registerSILClasses() { register(MergeIsolationRegionInst.self) register(IgnoredUseInst.self) } + +private func registerUtilities() { + registerVerifier() + registerPhiUpdater() +} diff --git a/SwiftCompilerSources/Sources/SIL/Type.swift b/SwiftCompilerSources/Sources/SIL/Type.swift index 9ff1526276dcc..ea1767fccb7b7 100644 --- a/SwiftCompilerSources/Sources/SIL/Type.swift +++ b/SwiftCompilerSources/Sources/SIL/Type.swift @@ -78,7 +78,13 @@ public struct Type : TypeProperties, CustomStringConvertible, NoReflectionChildr !isNoEscapeFunction && isEscapable(in: function) } - public var builtinVectorElementType: Type { canonicalType.builtinVectorElementType.silType! } + public func builtinVectorElementType(in function: Function) -> Type { + canonicalType.builtinVectorElementType.loweredType(in: function) + } + + public func builtinFixedArrayElementType(in function: Function, maximallyAbstracted: Bool = false) -> Type { + canonicalType.builtinFixedArrayElementType.loweredType(in: function, maximallyAbstracted: maximallyAbstracted) + } public var superClassType: Type? { canonicalType.superClassType?.silType } @@ -97,6 +103,16 @@ public struct Type : TypeProperties, CustomStringConvertible, NoReflectionChildr bridged.isAddressableForDeps(function.bridged) } + /// If this is a raw layout type, returns the substituted like-type. + public var rawLayoutSubstitutedLikeType: AST.`Type`? { + .init(bridgedOrNil: bridged.getRawLayoutSubstitutedLikeType()) + } + + /// If this is a raw layout type, returns the substituted count-type. + public var rawLayoutSubstitutedCountType: AST.`Type`? { + .init(bridgedOrNil: bridged.getRawLayoutSubstitutedCountType()) + } + //===--------------------------------------------------------------------===// // Properties of lowered `SILFunctionType`s //===--------------------------------------------------------------------===// @@ -108,6 +124,21 @@ public struct Type : TypeProperties, CustomStringConvertible, NoReflectionChildr bridged.getFunctionTypeWithNoEscape(withNoEscape).type } + /// True if a function with this type can be code-generated in Embedded Swift. + /// These are basically all non-generic functions. But also certain generic functions are supported: + /// Generic function arguments which have a class-bound type are valid in Embedded Swift, because for + /// such arguments, no metadata is needed, except the isa-pointer of the class. + public var hasValidSignatureForEmbedded: Bool { + let genericSignature = invocationGenericSignatureOfFunction + for genParam in genericSignature.genericParameters { + let mappedParam = genericSignature.mapTypeIntoContext(genParam) + if mappedParam.isArchetype && !mappedParam.archetypeRequiresClass { + return false + } + } + return true + } + //===--------------------------------------------------------------------===// // Aggregates //===--------------------------------------------------------------------===// @@ -125,12 +156,20 @@ public struct Type : TypeProperties, CustomStringConvertible, NoReflectionChildr return TupleElementArray(type: self) } + public func getBoxFields(in function: Function) -> BoxFieldsArray { + precondition(isBox) + return BoxFieldsArray(boxType: canonicalType, function: function) + } + /// Returns nil if the nominal is a resilient type because in this case the complete list /// of fields is not known. public func getNominalFields(in function: Function) -> NominalFieldsArray? { guard let nominal = nominal, !nominal.isResilient(in: function) else { return nil } + if let structDecl = nominal as? StructDecl, structDecl.hasUnreferenceableStorage { + return nil + } return NominalFieldsArray(type: self, function: function) } @@ -267,6 +306,28 @@ public struct TupleElementArray : RandomAccessCollection, FormattedLikeArray { } } +public struct BoxFieldsArray : RandomAccessCollection, FormattedLikeArray { + public let boxType: CanonicalType + public let function: Function + + public var startIndex: Int { return 0 } + public var endIndex: Int { BridgedType.getNumBoxFields(boxType.bridged) } + + public subscript(_ index: Int) -> Type { + BridgedType.getBoxFieldType(boxType.bridged, index, function.bridged).type + } + + public func isMutable(fieldIndex: Int) -> Bool { + BridgedType.getBoxFieldIsMutable(boxType.bridged, fieldIndex) + } +} + +extension Type: DiagnosticArgument { + public func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) { + rawType._withBridgedDiagnosticArgument(fn) + } +} + extension BridgedType { public var type: Type { Type(bridged: self) } var typeOrNil: Type? { isNull() ? nil : type } diff --git a/SwiftCompilerSources/Sources/SIL/Utilities/AccessUtils.swift b/SwiftCompilerSources/Sources/SIL/Utilities/AccessUtils.swift index d7680d9e832f5..e2c489693e50f 100644 --- a/SwiftCompilerSources/Sources/SIL/Utilities/AccessUtils.swift +++ b/SwiftCompilerSources/Sources/SIL/Utilities/AccessUtils.swift @@ -89,6 +89,8 @@ public enum AccessBase : CustomStringConvertible, Hashable { /// The access base is some SIL pattern which does not fit into any other case. /// This should be a very rare situation. + /// + /// TODO: unidentified should preserve its base address value, but AccessBase must be Hashable. case unidentified public init(baseAddress: Value) { @@ -118,7 +120,7 @@ public enum AccessBase : CustomStringConvertible, Hashable { } } - /// Return 'nil' for global varabiables and unidentified addresses. + /// Return 'nil' for global variables and unidentified addresses. public var address: Value? { switch self { case .global, .unidentified: return nil @@ -221,6 +223,21 @@ public enum AccessBase : CustomStringConvertible, Hashable { } } + public var storageIsLexical: Bool { + switch self { + case .argument(let arg): + return arg.isLexical + case .stack(let allocStack): + return allocStack.isLexical + case .global: + return true + case .box, .class, .tail: + return reference!.referenceRoot.isLexical + case .yield, .pointer, .index, .storeBorrow, .unidentified: + return false + } + } + /// Returns true if it's guaranteed that this access has the same base address as the `other` access. /// /// `isEqual` abstracts away the projection instructions that are included as part of the AccessBase: @@ -306,8 +323,19 @@ public enum AccessBase : CustomStringConvertible, Hashable { isDifferentAllocation(rea.instance, otherRea.instance) || hasDifferentType(rea.instance, otherRea.instance) case (.tail(let rta), .tail(let otherRta)): - return isDifferentAllocation(rta.instance, otherRta.instance) || - hasDifferentType(rta.instance, otherRta.instance) + if isDifferentAllocation(rta.instance, otherRta.instance) { + return true + } + if hasDifferentType(rta.instance, otherRta.instance), + // In contrast to `ref_element_addr`, tail addresses can also be obtained via a superclass + // (in case the derived class doesn't add any stored properties). + // Therefore if the instance types differ by sub-superclass relationship, the base is _not_ different. + !rta.instance.type.isExactSuperclass(of: otherRta.instance.type), + !otherRta.instance.type.isExactSuperclass(of: rta.instance.type) + { + return true + } + return false case (.argument(let arg), .argument(let otherArg)): return (arg.convention.isExclusiveIndirect || otherArg.convention.isExclusiveIndirect) && arg != otherArg @@ -375,6 +403,11 @@ public struct AccessPath : CustomStringConvertible, Hashable { public func isEqualOrContains(_ other: AccessPath) -> Bool { return getProjection(to: other) != nil } + + /// Returns true if this access contains `other` access and is not equal. + public func contains(_ other: AccessPath) -> Bool { + return !(getProjection(to: other)?.isEmpty ?? true) + } public var materializableProjectionPath: SmallProjectionPath? { if projectionPath.isMaterializable { @@ -414,7 +447,7 @@ public struct AccessPath : CustomStringConvertible, Hashable { private func canBeOperandOfIndexAddr(_ value: Value) -> Bool { switch value { - case is IndexAddrInst, is RefTailAddrInst, is PointerToAddressInst: + case is IndexAddrInst, is RefTailAddrInst, is PointerToAddressInst, is VectorBaseAddrInst: return true default: return false @@ -556,10 +589,6 @@ public struct AccessBaseAndScopes { self.scopes = scopes } - public var outerAddress: Value? { - base.address ?? scopes.last?.address - } - public var enclosingAccess: EnclosingAccessScope { return scopes.first ?? .base(base) } @@ -733,7 +762,7 @@ extension Value { // Although an AccessPathWalker is created for each call of these properties, // it's very unlikely that this will end up in memory allocations. // Only in the rare case of `pointer_to_address` -> `address_to_pointer` pairs, which - // go through phi-arguments, the AccessPathWalker will allocate memnory in its cache. + // go through phi-arguments, the AccessPathWalker will allocate memory in its cache. /// Computes the access base of this address value. public var accessBase: AccessBase { accessPath.base } @@ -857,3 +886,11 @@ extension Function { return nil } } + +let getAccessBaseTest = Test("swift_get_access_base") { + function, arguments, context in + let address = arguments.takeValue() + print("Address: \(address)") + let base = address.accessBase + print("Base: \(base)") +} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowUtils.swift b/SwiftCompilerSources/Sources/SIL/Utilities/BorrowUtils.swift similarity index 89% rename from SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowUtils.swift rename to SwiftCompilerSources/Sources/SIL/Utilities/BorrowUtils.swift index 2aa2343f36aff..187dc228c153b 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowUtils.swift +++ b/SwiftCompilerSources/Sources/SIL/Utilities/BorrowUtils.swift @@ -130,8 +130,6 @@ // patterns). // ===----------------------------------------------------------------------===// -import SIL - /// A scoped instruction that borrows one or more operands. /// /// If this instruction produces a borrowed value, then BeginBorrowValue(resultOf: self) != nil. @@ -151,7 +149,7 @@ import SIL /// operand. /// /// TODO: replace BorrowIntroducingInstruction with this. -enum BorrowingInstruction : CustomStringConvertible, Hashable { +public enum BorrowingInstruction : CustomStringConvertible, Hashable { case beginBorrow(BeginBorrowInst) case borrowedFrom(BorrowedFromInst) case storeBorrow(StoreBorrowInst) @@ -160,7 +158,7 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable { case markDependence(MarkDependenceInst) case startAsyncLet(BuiltinInst) - init?(_ inst: Instruction) { + public init?(_ inst: Instruction) { switch inst { case let bbi as BeginBorrowInst: self = .beginBorrow(bbi) @@ -185,7 +183,7 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable { } } - var instruction: Instruction { + public var instruction: Instruction { switch self { case .beginBorrow(let bbi): return bbi @@ -204,14 +202,17 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable { } } - var innerValue: Value? { + public var innerValue: Value? { if let dependent = dependentValue { return dependent } return scopedValue } - var dependentValue: Value? { + /// Returns non-nil if this borrowing instruction produces an guaranteed dependent value and does not have immediate + /// scope-ending uses. Finding the borrow scope in such cases requires recursively following uses of the guaranteed + /// value. + public var dependentValue: Value? { switch self { case .borrowedFrom(let bfi): let phi = bfi.borrowedPhi @@ -230,7 +231,7 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable { } /// If this is valid, then visitScopeEndingOperands succeeds. - var scopedValue: Value? { + public var scopedValue: Value? { switch self { case .beginBorrow, .storeBorrow: return instruction as! SingleValueInstruction @@ -268,7 +269,7 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable { /// /// TODO: For instructions that are not a BeginBorrowValue, verify that scope ending instructions exist on all /// paths. These instructions should be complete after SILGen and never cloned to produce phis. - func visitScopeEndingOperands(_ context: Context, visitor: @escaping (Operand) -> WalkResult) -> WalkResult { + public func visitScopeEndingOperands(_ context: Context, visitor: @escaping (Operand) -> WalkResult) -> WalkResult { guard let val = scopedValue else { return .abortWalk } @@ -299,7 +300,7 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable { extension BorrowingInstruction { private func visitEndBorrows(value: Value, _ context: Context, _ visitor: @escaping (Operand) -> WalkResult) -> WalkResult { - return value.lookThroughBorrowedFromUser.uses.filterUsers(ofType: EndBorrowInst.self).walk { + return value.lookThroughBorrowedFromUser.uses.filterUses(ofType: EndBorrowInst.self).walk { visitor($0) } } @@ -323,7 +324,7 @@ extension BorrowingInstruction { } } - var description: String { instruction.description } + public var description: String { instruction.description } } /// A value that introduces a borrow scope: @@ -337,7 +338,7 @@ extension BorrowingInstruction { /// one of the yielded values. In any case, the scope ending operands /// are on the end_apply or abort_apply instructions that use the /// token. -enum BeginBorrowValue { +public enum BeginBorrowValue { case beginBorrow(BeginBorrowInst) case loadBorrow(LoadBorrowInst) case beginApply(Value) @@ -345,7 +346,7 @@ enum BeginBorrowValue { case functionArgument(FunctionArgument) case reborrow(Phi) - init?(_ value: Value) { + public init?(_ value: Value) { switch value { case let bbi as BeginBorrowInst: self = .beginBorrow(bbi) @@ -366,7 +367,7 @@ enum BeginBorrowValue { } } - var value: Value { + public var value: Value { switch self { case .beginBorrow(let bbi): return bbi case .loadBorrow(let lbi): return lbi @@ -377,7 +378,7 @@ enum BeginBorrowValue { } } - init?(using operand: Operand) { + public init?(using operand: Operand) { switch operand.instruction { case is BeginBorrowInst, is LoadBorrowInst: let inst = operand.instruction as! SingleValueInstruction @@ -395,7 +396,7 @@ enum BeginBorrowValue { } } - init?(resultOf borrowInstruction: BorrowingInstruction) { + public init?(resultOf borrowInstruction: BorrowingInstruction) { switch borrowInstruction { case let .beginBorrow(beginBorrow): self.init(beginBorrow) @@ -409,7 +410,7 @@ enum BeginBorrowValue { } } - var hasLocalScope: Bool { + public var hasLocalScope: Bool { switch self { case .beginBorrow, .loadBorrow, .beginApply, .reborrow, .uncheckOwnershipConversion: return true @@ -422,10 +423,10 @@ enum BeginBorrowValue { // load_borrow. // // Return nil for begin_apply and reborrow, which need special handling. - var baseOperand: Operand? { + public var baseOperand: Operand? { switch self { case let .beginBorrow(beginBorrow): - return beginBorrow.operand + return beginBorrow.operand case let .loadBorrow(loadBorrow): return loadBorrow.operand case .beginApply, .functionArgument, .reborrow, .uncheckOwnershipConversion: @@ -435,7 +436,7 @@ enum BeginBorrowValue { /// The EndBorrows, reborrows (phis), and consumes (of closures) /// that end the local borrow scope. Empty if hasLocalScope is false. - var scopeEndingOperands: LazyFilterSequence { + public var scopeEndingOperands: LazyFilterSequence { switch self { case let .beginApply(value): return (value.definingInstruction @@ -448,28 +449,8 @@ enum BeginBorrowValue { } } -/// Compute the live range for the borrow scopes of a guaranteed value. This returns a separate instruction range for -/// each of the value's borrow introducers. -/// -/// TODO: This should return a single multiply-defined instruction range. -func computeBorrowLiveRange(for value: Value, _ context: FunctionPassContext) - -> SingleInlineArray<(BeginBorrowValue, InstructionRange)> { - assert(value.ownership == .guaranteed) - - var ranges = SingleInlineArray<(BeginBorrowValue, InstructionRange)>() - // If introducers is empty, then the dependence is on a trivial value, so - // there is no ownership range. - for beginBorrow in value.getBorrowIntroducers(context) { - /// FIXME: Remove calls to computeKnownLiveness() as soon as lifetime completion runs immediately after - /// SILGen. Instead, this should compute linear liveness for borrowed value by switching over BeginBorrowValue, just - /// like LifetimeDependenc.Scope.computeRange(). - ranges.push((beginBorrow, computeKnownLiveness(for: beginBorrow.value, context))) - } - return ranges -} - extension Value { - var lookThroughBorrowedFrom: Value { + public var lookThroughBorrowedFrom: Value { if let bfi = self as? BorrowedFromInst { return bfi.borrowedValue.lookThroughBorrowedFrom } @@ -477,20 +458,20 @@ extension Value { } } -struct BorrowIntroducers : CollectionLikeSequence { +public struct BorrowIntroducers : CollectionLikeSequence { let initialValue: Value let context: Ctxt - func makeIterator() -> EnclosingValueIterator { + public func makeIterator() -> EnclosingValueIterator { EnclosingValueIterator(forBorrowIntroducers: initialValue, context) } } -struct EnclosingValues : CollectionLikeSequence { +public struct EnclosingValues : CollectionLikeSequence { let initialValue: Value let context: Ctxt - func makeIterator() -> EnclosingValueIterator { + public func makeIterator() -> EnclosingValueIterator { EnclosingValueIterator(forEnclosingValues: initialValue, context) } } @@ -498,7 +479,7 @@ struct EnclosingValues : CollectionLikeSequence { // This iterator must be a class because we need a deinit. // It shouldn't be a performance problem because the optimizer should always be able to stack promote the iterator. // TODO: Make it a struct once this is possible with non-copyable types. -final class EnclosingValueIterator : IteratorProtocol { +public final class EnclosingValueIterator : IteratorProtocol { var worklist: ValueWorklist init(forBorrowIntroducers value: Value, _ context: some Context) { @@ -532,7 +513,7 @@ final class EnclosingValueIterator : IteratorProtocol { worklist.deinitialize() } - func next() -> Value? { + public func next() -> Value? { while let value = worklist.pop() { switch value.ownership { case .none, .unowned: @@ -553,7 +534,10 @@ final class EnclosingValueIterator : IteratorProtocol { } else if let forwardingInst = value.forwardingInstruction { // Recurse through guaranteed forwarding non-phi instructions. let ops = forwardingInst.forwardedOperands - worklist.pushIfNotVisited(contentsOf: ops.lazy.map { $0.value }) + worklist.pushIfNotVisited(contentsOf: ops.values) + } else if value.isGuaranteedApplyResult { + let selfArgument = (value as! ApplyInst).arguments.last! + worklist.pushIfNotVisited(selfArgument) } else { fatalError("cannot get borrow introducers for unknown guaranteed value") } @@ -581,7 +565,9 @@ extension Value { /// %field = ref_element_addr %first // (none) /// %load = load_borrow %field : $*C // %load /// - func getBorrowIntroducers(_ context: Ctxt) -> LazyMapSequence, BeginBorrowValue> { + public func getBorrowIntroducers( + _ context: Ctxt + ) -> LazyMapSequence, BeginBorrowValue> { BorrowIntroducers(initialValue: self, context: context).lazy.map { BeginBorrowValue($0)! } } @@ -622,15 +608,37 @@ extension Value { /// bb1(%outerReborrow : @reborrow, // %0 /// %innerReborrow : @reborrow) // %outerReborrow /// - func getEnclosingValues(_ context: Ctxt) -> EnclosingValues { + public func getEnclosingValues(_ context: Ctxt) -> EnclosingValues { EnclosingValues(initialValue: self, context: context) } + + public var lookThroughBorrowedFromUser: Value { + for use in uses { + if let bfi = use.forwardingBorrowedFromUser { + return bfi + } + } + return self + } + + public var isGuaranteedApplyResult: Bool { + guard let definingInstruction = self.definingInstruction else { + return false + } + guard let apply = definingInstruction as? ApplyInst else { + return false + } + guard apply.singleDirectResult != nil else { + return false + } + return apply.functionConvention.results[0].convention == .guaranteed + } } extension Phi { /// The inner adjacent phis of this outer "enclosing" phi. /// These keep the enclosing (outer adjacent) phi alive. - var innerAdjacentPhis: LazyMapSequence>, Phi> { + public var innerAdjacentPhis: LazyMapSequence>, Phi> { value.uses.lazy.compactMap { use in if let bfi = use.instruction as? BorrowedFromInst, use.index != 0 @@ -644,7 +652,7 @@ extension Phi { /// Gathers enclosing values by visiting predecessor blocks. /// Only used for updating borrowed-from instructions and for verification. -func gatherEnclosingValuesFromPredecessors( +public func gatherEnclosingValuesFromPredecessors( for phi: Phi, in enclosingValues: inout Stack, _ context: some Context @@ -666,7 +674,7 @@ func gatherEnclosingValuesFromPredecessors( extension BasicBlock { // Returns either the `incomingEnclosingValue` or an adjacent phi in the successor block. - func getEnclosingValueInSuccessor(ofIncoming incomingEnclosingValue: Value) -> Value { + public func getEnclosingValueInSuccessor(ofIncoming incomingEnclosingValue: Value) -> Value { let branch = terminator as! BranchInst if let incomingEV = branch.operands.first(where: { branchOp in // Only if the lifetime of `branchOp` ends at the branch (either because it's a reborrow or an owned value), @@ -694,7 +702,7 @@ extension BasicBlock { } } -let borrowIntroducersTest = FunctionTest("borrow_introducers") { +let borrowIntroducersTest = Test("borrow_introducers") { function, arguments, context in let value = arguments.takeValue() print(function) @@ -704,7 +712,7 @@ let borrowIntroducersTest = FunctionTest("borrow_introducers") { } } -let enclosingValuesTest = FunctionTest("enclosing_values") { +let enclosingValuesTest = Test("enclosing_values") { function, arguments, context in let value = arguments.takeValue() print(function) @@ -718,14 +726,3 @@ let enclosingValuesTest = FunctionTest("enclosing_values") { } } -extension Value { - var lookThroughBorrowedFromUser: Value { - for use in uses { - if let bfi = use.forwardingBorrowedFromUser { - return bfi - } - } - return self - } -} - diff --git a/SwiftCompilerSources/Sources/SIL/Utilities/CMakeLists.txt b/SwiftCompilerSources/Sources/SIL/Utilities/CMakeLists.txt index ef985c9c68ab4..59ba3af5f8912 100644 --- a/SwiftCompilerSources/Sources/SIL/Utilities/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/SIL/Utilities/CMakeLists.txt @@ -8,8 +8,15 @@ swift_compiler_sources(SIL AccessUtils.swift + BorrowUtils.swift + Cloner.swift + ForwardingUtils.swift + PhiUpdater.swift SequenceUtilities.swift SmallProjectionPath.swift + SSAUpdater.swift + Test.swift WalkUtils.swift + Verifier.swift ) diff --git a/SwiftCompilerSources/Sources/SIL/Utilities/Cloner.swift b/SwiftCompilerSources/Sources/SIL/Utilities/Cloner.swift new file mode 100644 index 0000000000000..e010a16642048 --- /dev/null +++ b/SwiftCompilerSources/Sources/SIL/Utilities/Cloner.swift @@ -0,0 +1,197 @@ +//===--- Cloner.swift ------------------------------------------------------==// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 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 SILBridging +import AST + +/// Clones the initializer value of a GlobalVariable. +/// +/// Used to transitively clone "constant" instructions, including their operands, +/// from or to the static initializer value of a GlobalVariable. +/// +public struct Cloner { + private var bridged: BridgedCloner + public let context: Context + + public enum GetClonedResult { + case defaultValue + case customValue(Value) + case stopCloning + } + + public enum Target { + case function(Function) + case global(GlobalVariable) + } + public let target: Target + + public init(cloneToGlobal: GlobalVariable, _ context: Context) { + self.bridged = BridgedCloner(cloneToGlobal.bridged, context._bridged) + self.context = context + self.target = .global(cloneToGlobal) + } + + public init(cloneBefore inst: Instruction, _ context: Context) { + self.bridged = BridgedCloner(inst.bridged, context._bridged) + self.context = context + self.target = .function(inst.parentFunction) + } + + public init(cloneToEmptyFunction: Function, _ context: Context) { + self.bridged = BridgedCloner(cloneToEmptyFunction.bridged, context._bridged) + self.context = context + self.target = .function(cloneToEmptyFunction) + } + + public mutating func deinitialize() { + bridged.destroy(context._bridged) + } + + public var targetFunction: Function { + guard case .function(let function) = target else { + fatalError("expected cloning into a function") + } + return function + } + + public func getOrCreateEntryBlock() -> BasicBlock { + if let entryBlock = targetFunction.blocks.first { + return entryBlock + } + let entryBlock = targetFunction.appendNewBlock(context) + bridged.setInsertionBlockIfNotSet(entryBlock.bridged) + return entryBlock + } + + public func cloneFunctionBody(from originalFunction: Function, entryBlockArguments: [Value]) { + entryBlockArguments.withBridgedValues { bridgedEntryBlockArgs in + let entryBlock = getOrCreateEntryBlock() + bridged.cloneFunctionBody(originalFunction.bridged, entryBlock.bridged, bridgedEntryBlockArgs) + } + } + + public func cloneFunctionBody(from originalFunction: Function) { + bridged.cloneFunctionBody(originalFunction.bridged) + } + + public mutating func clone(instruction: Instruction) -> Instruction { + let cloned = bridged.clone(instruction.bridged).instruction + if case .function = target { + context.notifyInstructionChanged(cloned) + context.notifyInstructionsChanged() + } + return cloned + } + + public mutating func cloneRecursively(globalInitValue: Value) -> Value { + guard let cloned = cloneRecursively(value: globalInitValue, customGetCloned: { value, cloner in + guard let beginAccess = value as? BeginAccessInst else { + return .defaultValue + } + + // Skip access instructions, which might be generated for UnsafePointer globals which point to other globals. + let clonedOperand = cloner.cloneRecursively(globalInitValue: beginAccess.address) + cloner.recordFoldedValue(beginAccess, mappedTo: clonedOperand) + return .customValue(clonedOperand) + }) else { + fatalError("Clone recursively to global shouldn't bail.") + } + + return cloned + } + + /// Transitively clones `value` including its defining instruction's operands. + public mutating func cloneRecursively( value: Value) -> Value { + return cloneRecursively(value: value, customGetCloned: { _, _ in .defaultValue })! + } + + /// Transitively clones `value` including its defining instruction's operands. + public mutating func cloneRecursively(value: Value, customGetCloned: (Value, inout Cloner) -> GetClonedResult) -> Value? { + if isCloned(value: value) { + return getClonedValue(of: value) + } + + switch customGetCloned(value, &self) { + case .customValue(let base): + return base + case .stopCloning: + return nil + case .defaultValue: + break + } + + guard let inst = value.definingInstruction else { + fatalError("expected instruction to clone or already cloned value") + } + + for op in inst.operands { + if cloneRecursively(value: op.value, customGetCloned: customGetCloned) == nil { + return nil + } + } + + let cloned = clone(instruction: inst) + if let svi = cloned as? SingleValueInstruction { + return svi + } else if let originalMvi = value as? MultipleValueInstructionResult { + return cloned.results[originalMvi.index] + } + fatalError("unexpected instruction kind") + } + + public mutating func getClonedValue(of originalValue: Value) -> Value { + bridged.getClonedValue(originalValue.bridged).value + } + + public func isCloned(value: Value) -> Bool { + bridged.isValueCloned(value.bridged) + } + + public func getClonedBlock(for originalBlock: BasicBlock) -> BasicBlock { + bridged.getClonedBasicBlock(originalBlock.bridged).block + } + + public func recordFoldedValue(_ origValue: Value, mappedTo mappedValue: Value) { + bridged.recordFoldedValue(origValue.bridged, mappedValue.bridged) + } +} + +public struct TypeSubstitutionCloner { + public private(set) var bridged: BridgedTypeSubstCloner + public let context: Context + + public init(fromFunction: Function, + toEmptyFunction: Function, + substitutions: SubstitutionMap, _ context: Context + ) { + context.verifyIsTransforming(function: toEmptyFunction) + self.bridged = BridgedTypeSubstCloner(fromFunction.bridged, toEmptyFunction.bridged, + substitutions.bridged, context._bridged) + self.context = context + } + + public mutating func deinitialize() { + bridged.destroy(context._bridged) + } + + public mutating func getClonedValue(of originalValue: Value) -> Value { + bridged.getClonedValue(originalValue.bridged).value + } + + public func getClonedBlock(for originalBlock: BasicBlock) -> BasicBlock { + bridged.getClonedBasicBlock(originalBlock.bridged).block + } + + public func cloneFunctionBody() { + bridged.cloneFunctionBody() + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/ForwardingUtils.swift b/SwiftCompilerSources/Sources/SIL/Utilities/ForwardingUtils.swift similarity index 86% rename from SwiftCompilerSources/Sources/Optimizer/Utilities/ForwardingUtils.swift rename to SwiftCompilerSources/Sources/SIL/Utilities/ForwardingUtils.swift index 56122160a64c8..03b0e3b422830 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/ForwardingUtils.swift +++ b/SwiftCompilerSources/Sources/SIL/Utilities/ForwardingUtils.swift @@ -16,8 +16,6 @@ /// is part of a forward-extended lifetime that has a pointer escape. //===----------------------------------------------------------------------===// -import SIL - private let verbose = false private func log(_ message: @autoclosure () -> String) { @@ -36,7 +34,7 @@ private func log(_ message: @autoclosure () -> String) { /// Allocation. Then add a `Value.hasPointerEscapingUse` property that /// performs the use-def walk to pickup the flags. Then only call into /// this def-use walk to initially set the flags. -func findPointerEscapingUse(of value: Value) -> Bool { +public func findPointerEscapingUse(of value: Value) -> Bool { value.bridged.findPointerEscape() } @@ -100,7 +98,7 @@ func findPointerEscapingUse(of value: Value) -> Bool { /// This walker is used to query basic lifetime attributes on values, /// such as "escaping" or "lexical". It must be precise for /// correctness and is performance critical. -protocol ForwardingUseDefWalker { +public protocol ForwardingUseDefWalker { associatedtype PathContext mutating func introducer(_ value: Value, _ path: PathContext) -> WalkResult @@ -113,12 +111,12 @@ protocol ForwardingUseDefWalker { } extension ForwardingUseDefWalker { - mutating func walkUp(value: Value, _ path: PathContext) -> WalkResult { + public mutating func walkUp(value: Value, _ path: PathContext) -> WalkResult { walkUpDefault(forwarded: value, path) } - mutating func walkUpDefault(forwarded value: Value, _ path: PathContext) + public mutating func walkUpDefault(forwarded value: Value, _ path: PathContext) -> WalkResult { - if let inst = value.forwardingInstruction { + if let inst = value.forwardingInstruction, !inst.forwardedOperands.isEmpty { return walkUp(instruction: inst, path) } if let phi = Phi(value) { @@ -126,7 +124,7 @@ extension ForwardingUseDefWalker { } return introducer(value, path) } - mutating func walkUp(instruction: ForwardingInstruction, _ path: PathContext) + public mutating func walkUp(instruction: ForwardingInstruction, _ path: PathContext) -> WalkResult { for operand in instruction.forwardedOperands { if needWalk(for: operand.value, path) { @@ -137,7 +135,7 @@ extension ForwardingUseDefWalker { } return .continueWalk } - mutating func walkUp(phi: Phi, _ path: PathContext) -> WalkResult { + public mutating func walkUp(phi: Phi, _ path: PathContext) -> WalkResult { for operand in phi.incomingOperands { if needWalk(for: operand.value, path) { if walkUp(value: operand.value, path) == .abortWalk { @@ -151,7 +149,7 @@ extension ForwardingUseDefWalker { // This conveniently gathers all forward introducers and deinitializes // visitedValues before the caller has a chance to recurse. -func gatherLifetimeIntroducers(for value: Value, _ context: Context) -> [Value] { +public func gatherLifetimeIntroducers(for value: Value, _ context: Context) -> [Value] { var introducers: [Value] = [] var walker = VisitLifetimeIntroducers(context) { introducers.append($0) @@ -163,8 +161,8 @@ func gatherLifetimeIntroducers(for value: Value, _ context: Context) -> [Value] } // TODO: visitor can be nonescaping when we have borrowed properties. -func visitLifetimeIntroducers(for value: Value, _ context: Context, - visitor: @escaping (Value) -> WalkResult) +public func visitLifetimeIntroducers(for value: Value, _ context: Context, + visitor: @escaping (Value) -> WalkResult) -> WalkResult { var walker = VisitLifetimeIntroducers(context, visitor: visitor) defer { walker.visitedValues.deinitialize() } @@ -191,11 +189,11 @@ private struct VisitLifetimeIntroducers : ForwardingUseDefWalker { } } -enum ForwardingUseResult: CustomStringConvertible { +public enum ForwardingUseResult: CustomStringConvertible { case operand(Operand) case deadValue(Value, Operand?) - var description: String { + public var description: String { switch self { case .operand(let operand): return operand.description @@ -220,7 +218,7 @@ enum ForwardingUseResult: CustomStringConvertible { /// Start walking: /// walkDown(root: Value) /// -protocol ForwardingDefUseWalker { +public protocol ForwardingDefUseWalker { /// Minimally, check a ValueSet. This walker may traverse chains of /// aggregation and destructuring by default. Implementations may /// handle phis. @@ -249,16 +247,16 @@ protocol ForwardingDefUseWalker { extension ForwardingDefUseWalker { /// Start walking - mutating func walkDown(root: Value) -> WalkResult { + public mutating func walkDown(root: Value) -> WalkResult { walkDownUses(of: root, using: nil) } - mutating func walkDownUses(of value: Value, using operand: Operand?) + public mutating func walkDownUses(of value: Value, using operand: Operand?) -> WalkResult { return walkDownUsesDefault(forwarding: value, using: operand) } - mutating func walkDownUsesDefault(forwarding value: Value, + public mutating func walkDownUsesDefault(forwarding value: Value, using operand: Operand?) -> WalkResult { if !needWalk(for: value) { @@ -277,11 +275,11 @@ extension ForwardingDefUseWalker { return .continueWalk } - mutating func walkDown(operand: Operand) -> WalkResult { + public mutating func walkDown(operand: Operand) -> WalkResult { walkDownDefault(forwarding: operand) } - mutating func walkDownDefault(forwarding operand: Operand) -> WalkResult { + public mutating func walkDownDefault(forwarding operand: Operand) -> WalkResult { if let inst = operand.instruction as? ForwardingInstruction { let singleOper = inst.singleForwardedOperand if singleOper == nil || singleOper! == operand { @@ -303,8 +301,8 @@ extension ForwardingDefUseWalker { /// gatherLifetimeIntroducers(). /// /// TODO: make the visitor non-escaping once Swift supports stored -/// non-escaping closues. -func visitForwardedUses(introducer: Value, _ context: Context, +/// non-escaping closures. +public func visitForwardedUses(introducer: Value, _ context: Context, visitor: @escaping (ForwardingUseResult) -> WalkResult) -> WalkResult { var useVisitor = VisitForwardedUses(visitor: visitor, context) @@ -345,29 +343,29 @@ private struct VisitForwardedUses : ForwardingDefUseWalker { /// This is a subset of the functionality in LifetimeDependenceDefUseWalker, but significantly simpler. This avoids /// traversing lifetime dependencies that do not propagate context. For example, a mark_dependence on a closure extends /// its lifetime but cannot introduce any new uses of the closure context. -struct NonEscapingClosureDefUseWalker { +public struct NonEscapingClosureDefUseWalker { let context: Context var visitedValues: ValueSet - var applyOperandStack: Stack + public var applyOperandStack: Stack /// `visitor` takes an operand whose instruction is always a FullApplySite. - init(_ context: Context) { + public init(_ context: Context) { self.context = context self.visitedValues = ValueSet(context) self.applyOperandStack = Stack(context) } - mutating func deinitialize() { + public mutating func deinitialize() { visitedValues.deinitialize() applyOperandStack.deinitialize() } - mutating func walkDown(closure: PartialApplyInst) -> WalkResult { + public mutating func walkDown(closure: PartialApplyInst) -> WalkResult { assert(!closure.mayEscape) return walkDownUses(of: closure, using: nil) } - mutating func closureContextLeafUse(of operand: Operand) -> WalkResult { + public mutating func closureContextLeafUse(of operand: Operand) -> WalkResult { switch operand.instruction { case is FullApplySite: applyOperandStack.push(operand) @@ -388,11 +386,11 @@ struct NonEscapingClosureDefUseWalker { } extension NonEscapingClosureDefUseWalker: ForwardingDefUseWalker { - mutating func needWalk(for value: Value) -> Bool { + public mutating func needWalk(for value: Value) -> Bool { visitedValues.insert(value) } - mutating func nonForwardingUse(of operand: Operand) -> WalkResult { + public mutating func nonForwardingUse(of operand: Operand) -> WalkResult { // Nonescaping closures may be moved, copied, or borrowed. switch operand.instruction { case let transition as OwnershipTransitionInstruction: @@ -405,12 +403,12 @@ extension NonEscapingClosureDefUseWalker: ForwardingDefUseWalker { } } - mutating func deadValue(_ value: Value, using operand: Operand?) -> WalkResult { + public mutating func deadValue(_ value: Value, using operand: Operand?) -> WalkResult { return .continueWalk } } -let forwardingUseDefTest = FunctionTest("forwarding_use_def_test") { +let forwardingUseDefTest = Test("forwarding_use_def_test") { function, arguments, context in let value = arguments.takeValue() for introducer in gatherLifetimeIntroducers(for: value, context) { @@ -418,7 +416,7 @@ let forwardingUseDefTest = FunctionTest("forwarding_use_def_test") { } } -let forwardingDefUseTest = FunctionTest("forwarding_def_use_test") { +let forwardingDefUseTest = Test("forwarding_def_use_test") { function, arguments, context in let value = arguments.takeValue() _ = visitForwardedUses(introducer: value, context) { useResult in diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/PhiUpdater.swift b/SwiftCompilerSources/Sources/SIL/Utilities/PhiUpdater.swift similarity index 83% rename from SwiftCompilerSources/Sources/Optimizer/Utilities/PhiUpdater.swift rename to SwiftCompilerSources/Sources/SIL/Utilities/PhiUpdater.swift index 9b0a8ccc803d4..5029a91136a5e 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/PhiUpdater.swift +++ b/SwiftCompilerSources/Sources/SIL/Utilities/PhiUpdater.swift @@ -10,23 +10,22 @@ // //===----------------------------------------------------------------------===// -import SIL -import OptimizerBridging +import SILBridging /// Updates the reborrow flags and the borrowed-from instructions for all guaranteed phis in `function`. -func updateGuaranteedPhis(in function: Function, _ context: some MutatingContext) { +public func updateGuaranteedPhis(in function: Function, _ context: some MutatingContext) { updateReborrowFlags(in: function, context) updateBorrowedFrom(in: function, context) } /// Updates the reborrow flags and the borrowed-from instructions for all `phis`. -func updateGuaranteedPhis(phis: some Sequence, _ context: some MutatingContext) { +public func updateGuaranteedPhis(phis: some Sequence, _ context: some MutatingContext) { updateReborrowFlags(for: phis, context) updateBorrowedFrom(for: phis, context) } /// Update all borrowed-from instructions in the `function` -func updateBorrowedFrom(in function: Function, _ context: some MutatingContext) { +public func updateBorrowedFrom(in function: Function, _ context: some MutatingContext) { if !function.hasOwnership { return } @@ -44,7 +43,7 @@ func updateBorrowedFrom(in function: Function, _ context: some MutatingContext) } /// Update borrowed-from instructions for a set of phi arguments. -func updateBorrowedFrom(for phis: some Sequence, _ context: some MutatingContext) { +public func updateBorrowedFrom(for phis: some Sequence, _ context: some MutatingContext) { for phi in phis { if !phi.value.parentFunction.hasOwnership { return @@ -67,7 +66,7 @@ func updateBorrowedFrom(for phis: some Sequence, _ context: some MutatingCo } /// Updates the reborrow flags for all guaranteed phis in `function`. -func updateReborrowFlags(in function: Function, _ context: some MutatingContext) { +public func updateReborrowFlags(in function: Function, _ context: some MutatingContext) { if !function.hasOwnership { return } @@ -90,7 +89,7 @@ func updateReborrowFlags(in function: Function, _ context: some MutatingContext) /// by cutting off the control flow before an `end_borrow`, the re-borrow flags still have to remain /// without the possibility to re-calculate them from the (now missing) `end_borrow`. /// -func updateReborrowFlags(for phis: some Sequence, _ context: some MutatingContext) { +public func updateReborrowFlags(for phis: some Sequence, _ context: some MutatingContext) { if let phi = phis.first(where: { phi in true }), !phi.value.parentFunction.hasOwnership { return } @@ -138,7 +137,7 @@ private func createEmptyBorrowedFrom(for phi: Phi, _ context: some MutatingConte } let builder = Builder(atBeginOf: phi.value.parentBlock, context) let bfi = builder.createBorrowedFrom(borrowedValue: phi.value, enclosingValues: []) - phi.value.uses.ignoreUsers(ofType: BorrowedFromInst.self).replaceAll(with: bfi, context) + phi.value.uses.ignoreUses(ofType: BorrowedFromInst.self).replaceAll(with: bfi, context) } /// Replaces a phi with the unique incoming value if all incoming values are the same: @@ -160,7 +159,7 @@ private func createEmptyBorrowedFrom(for phi: Phi, _ context: some MutatingConte /// use(%1) /// ``` /// -func replacePhiWithIncomingValue(phi: Phi, _ context: some MutatingContext) -> Bool { +public func replacePhiWithIncomingValue(phi: Phi, _ context: some MutatingContext) -> Bool { if phi.predecessors.isEmpty { return false } @@ -207,7 +206,7 @@ func replacePhiWithIncomingValue(phi: Phi, _ context: some MutatingContext) -> B /// /// It's not needed to run this utility if SSAUpdater is used to create a _new_ OSSA liverange. /// -func replacePhisWithIncomingValues(phis: [Phi], _ context: some MutatingContext) { +public func replacePhisWithIncomingValues(phis: [Phi], _ context: some MutatingContext) { var currentPhis = phis // Do this in a loop because replacing one phi might open up the opportunity for another phi // and the order of phis in the array can be arbitrary. @@ -228,14 +227,14 @@ func replacePhisWithIncomingValues(phis: [Phi], _ context: some MutatingContext) func registerPhiUpdater() { BridgedUtilities.registerPhiUpdater( // updateAllGuaranteedPhis - { (bridgedCtxt: BridgedPassContext, bridgedFunction: BridgedFunction) in - let context = FunctionPassContext(_bridged: bridgedCtxt) + { (bridgedCtxt: BridgedContext, bridgedFunction: BridgedFunction) in + let context = PhiUpdaterContext(_bridged: bridgedCtxt) let function = bridgedFunction.function; updateGuaranteedPhis(in: function, context) }, // updateGuaranteedPhis - { (bridgedCtxt: BridgedPassContext, bridgedPhiArray: BridgedArrayRef) in - let context = FunctionPassContext(_bridged: bridgedCtxt) + { (bridgedCtxt: BridgedContext, bridgedPhiArray: BridgedArrayRef) in + let context = PhiUpdaterContext(_bridged: bridgedCtxt) var guaranteedPhis = Stack(context) defer { guaranteedPhis.deinitialize() } bridgedPhiArray.withElements(ofType: BridgedValue.self) { @@ -249,8 +248,8 @@ func registerPhiUpdater() { updateGuaranteedPhis(phis: guaranteedPhis, context) }, // replacePhisWithIncomingValues - { (bridgedCtxt: BridgedPassContext, bridgedPhiArray: BridgedArrayRef) in - let context = FunctionPassContext(_bridged: bridgedCtxt) + { (bridgedCtxt: BridgedContext, bridgedPhiArray: BridgedArrayRef) in + let context = PhiUpdaterContext(_bridged: bridgedCtxt) var phis = [Phi]() bridgedPhiArray.withElements(ofType: BridgedValue.self) { phis = $0.map { Phi($0.value)! } @@ -258,13 +257,9 @@ func registerPhiUpdater() { replacePhisWithIncomingValues(phis: phis, context) } ) -} - -/// This pass is only used for testing. -/// In the regular pipeline it's not needed because optimization passes must make sure that borrowed-from -/// instructions are updated once the pass finishes. -let updateBorrowedFromPass = FunctionPass(name: "update-borrowed-from") { - (function: Function, context: FunctionPassContext) in - updateBorrowedFrom(in: function, context) + struct PhiUpdaterContext: MutatingContext { + let _bridged: BridgedContext + public let notifyInstructionChanged: (Instruction) -> () = { inst in } + } } diff --git a/SwiftCompilerSources/Sources/SIL/Utilities/SSAUpdater.swift b/SwiftCompilerSources/Sources/SIL/Utilities/SSAUpdater.swift new file mode 100644 index 0000000000000..6feaf4544b3c6 --- /dev/null +++ b/SwiftCompilerSources/Sources/SIL/Utilities/SSAUpdater.swift @@ -0,0 +1,60 @@ +//===--- SSAUpdater.swift -------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 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 SILBridging + +/// Utility for updating SSA for a set of SIL instructions defined in multiple blocks. +public struct SSAUpdater { + let context: Context + + public init(function: Function, type: Type, ownership: Ownership, _ context: Context) { + self.context = context + context._bridged.SSAUpdater_initialize(function.bridged, type.bridged, ownership._bridged) + } + + public mutating func addAvailableValue(_ value: Value, in block: BasicBlock) { + context._bridged.SSAUpdater_addAvailableValue(block.bridged, value.bridged) + } + + /// Construct SSA for a value that is live at the *end* of a basic block. + public mutating func getValue(atEndOf block: BasicBlock) -> Value { + context.notifyInstructionsChanged() + return context._bridged.SSAUpdater_getValueAtEndOfBlock(block.bridged).value + } + + /// Construct SSA for a value that is live in the *middle* of a block. + /// This handles the case where the use is before a definition of the value in the same block. + /// + /// bb1: + /// %1 = def + /// br bb2 + /// bb2: + /// = use(?) + /// %2 = def + /// cond_br bb2, bb3 + /// + /// In this case we need to insert a phi argument in bb2, merging %1 and %2. + public mutating func getValue(inMiddleOf block: BasicBlock) -> Value { + context.notifyInstructionsChanged() + return context._bridged.SSAUpdater_getValueInMiddleOfBlock(block.bridged).value + } + + public var insertedPhis: [Phi] { + var phis = [Phi]() + let numPhis = context._bridged.SSAUpdater_getNumInsertedPhis() + phis.reserveCapacity(numPhis) + for idx in 0.. SmallProjectionPath { - return Self(bytes: bytes &>> numBits) + return Self(bytes: bytes >> numBits) } /// Pops and returns the first path component included the resulting path @@ -210,7 +214,7 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect // Ignore zero indices return self } - if k == .indexedElement { + if k == .indexedElement && index &+ i >= 0 { // "Merge" two constant successive indexed elements return pop(numBits: numBits).push(.indexedElement, index: index + i) } @@ -398,7 +402,7 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect return subPath.matches(pattern: subPattern) case .anyIndexedElement: return popIndexedElements().matches(pattern: subPattern) - case .structField, .tupleField, .enumCase, .classField, .tailElements, .indexedElement, .existential: + case .structField, .tupleField, .enumCase, .classField, .tailElements, .indexedElement, .existential, .vectorBase: let (kind, index, subPath) = pop() if kind != patternKind || index != patternIdx { return false } return subPath.matches(pattern: subPattern) @@ -478,8 +482,18 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect } if (lhsKind == rhsKind && lhsIdx == rhsIdx) || (lhsKind == .anyClassField && rhsKind.isClassField) || - (lhsKind.isClassField && rhsKind == .anyClassField) { - return pop(numBits: lhsBits).mayOverlap(with: rhs.pop(numBits: rhsBits)) + (lhsKind.isClassField && rhsKind == .anyClassField) + { + let poppedPath = pop(numBits: lhsBits) + let rhsPoppedPath = rhs.pop(numBits: rhsBits) + // Check for the case of overlapping the first element of a vector with another element. + // Note that the index of `.indexedElement` cannot be 0. + if (poppedPath.isEmpty && rhsPoppedPath.pop().kind == .indexedElement) || + (rhsPoppedPath.isEmpty && poppedPath.pop().kind == .indexedElement) + { + return false + } + return poppedPath.mayOverlap(with: rhsPoppedPath) } return false } @@ -496,7 +510,7 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect switch lhsKind { case .root: return rhs - case .classField, .tailElements, .structField, .tupleField, .enumCase, .existential, .indexedElement: + case .classField, .tailElements, .structField, .tupleField, .enumCase, .existential, .indexedElement, .vectorBase: let (rhsKind, rhsIdx, rhsBits) = rhs.top if lhsKind == rhsKind && lhsIdx == rhsIdx { return pop(numBits: lhsBits).subtract(from: rhs.pop(numBits: rhsBits)) @@ -520,6 +534,18 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect return false } } + + public var isConstant: Bool { + let (kind, _, subPath) = pop() + switch kind { + case .root: + return true + case .structField, .tupleField, .enumCase, .classField, .existential, .indexedElement: + return subPath.isConstant + default: + return false + } + } } //===----------------------------------------------------------------------===// @@ -601,6 +627,8 @@ extension StringParser { entries.append((.tailElements, 0)) } else if consume("x") { entries.append((.existential, 0)) + } else if consume("b") { + entries.append((.vectorBase, 0)) } else if consume("c") { guard let idx = consumeInt(withWhiteSpace: false) else { try throwError("expected class field index") @@ -652,278 +680,307 @@ extension StringParser { // Unit Tests //===----------------------------------------------------------------------===// -extension SmallProjectionPath { - public static func runUnitTests() { - - basicPushPop() - parsing() - merging() - subtracting() - matching() - overlapping() - predicates() - path2path() - - func basicPushPop() { - let p1 = SmallProjectionPath(.structField, index: 3) - .push(.classField, index: 12345678) - let (k2, i2, p2) = p1.pop() - assert(k2 == .classField && i2 == 12345678) - let (k3, i3, p3) = p2.pop() - assert(k3 == .structField && i3 == 3) - assert(p3.isEmpty) - let (k4, i4, _) = p2.push(.enumCase, index: 876).pop() - assert(k4 == .enumCase && i4 == 876) - let p5 = SmallProjectionPath(.anything) - assert(p5.pop().path.isEmpty) - let p6 = SmallProjectionPath(.indexedElement, index: 1).push(.indexedElement, index: 2) - let (k6, i6, p7) = p6.pop() - assert(k6 == .indexedElement && i6 == 3 && p7.isEmpty) - let p8 = SmallProjectionPath(.indexedElement, index: 0) - assert(p8.isEmpty) - let p9 = SmallProjectionPath(.indexedElement, index: 1).push(.anyIndexedElement) - let (k9, i9, p10) = p9.pop() - assert(k9 == .anyIndexedElement && i9 == 0 && p10.isEmpty) - let p11 = SmallProjectionPath(.anyIndexedElement).push(.indexedElement, index: 1) - let (k11, i11, p12) = p11.pop() - assert(k11 == .anyIndexedElement && i11 == 0 && p12.isEmpty) - } - - func parsing() { - testParse("v**.c*", expect: SmallProjectionPath(.anyClassField) - .push(.anyValueFields)) - testParse("s3.c*.v**.s1", expect: SmallProjectionPath(.structField, index: 1) - .push(.anyValueFields) - .push(.anyClassField) - .push(.structField, index: 3)) - testParse("2.c*.e6.ct.**", expect: SmallProjectionPath(.anything) - .push(.tailElements) - .push(.enumCase, index: 6) - .push(.anyClassField) - .push(.tupleField, index: 2)) - testParse("i3.x.i*", expect: SmallProjectionPath(.anyIndexedElement) - .push(.existential) - .push(.indexedElement, index: 3)) - - do { - var parser = StringParser("c*.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.**") - _ = try parser.parseProjectionPathFromSIL() - fatalError("too long path not detected") - } catch { - } - do { - var parser = StringParser("**.s0") - _ = try parser.parseProjectionPathFromSIL() - fatalError("wrong '**' not detected") - } catch { - } +let smallProjectionPathTest = Test("small_projection_path") { + function, arguments, context in + + basicPushPop() + parsing() + merging() + subtracting() + matching() + overlapping() + predicates() + path2path() + indexedElements() + + func basicPushPop() { + let p1 = SmallProjectionPath(.structField, index: 3) + .push(.classField, index: 12345678) + let (k2, i2, p2) = p1.pop() + assert(k2 == .classField && i2 == 12345678) + let (k3, i3, p3) = p2.pop() + assert(k3 == .structField && i3 == 3) + assert(p3.isEmpty) + let (k4, i4, _) = p2.push(.enumCase, index: 876).pop() + assert(k4 == .enumCase && i4 == 876) + let p5 = SmallProjectionPath(.anything) + assert(p5.pop().path.isEmpty) + let p6 = SmallProjectionPath(.indexedElement, index: 1).push(.indexedElement, index: 2) + let (k6, i6, p7) = p6.pop() + assert(k6 == .indexedElement && i6 == 3 && p7.isEmpty) + let p8 = SmallProjectionPath(.indexedElement, index: 0) + assert(p8.isEmpty) + let p9 = SmallProjectionPath(.indexedElement, index: 1).push(.anyIndexedElement) + let (k9, i9, p10) = p9.pop() + assert(k9 == .anyIndexedElement && i9 == 0 && p10.isEmpty) + let p11 = SmallProjectionPath(.anyIndexedElement).push(.indexedElement, index: 1) + let (k11, i11, p12) = p11.pop() + assert(k11 == .anyIndexedElement && i11 == 0 && p12.isEmpty) + } + + func parsing() { + testParse("v**.c*", expect: SmallProjectionPath(.anyClassField) + .push(.anyValueFields)) + testParse("s3.c*.v**.s1", expect: SmallProjectionPath(.structField, index: 1) + .push(.anyValueFields) + .push(.anyClassField) + .push(.structField, index: 3)) + testParse("2.c*.e6.ct.**", expect: SmallProjectionPath(.anything) + .push(.tailElements) + .push(.enumCase, index: 6) + .push(.anyClassField) + .push(.tupleField, index: 2)) + testParse("i3.x.b.i*", expect: SmallProjectionPath(.anyIndexedElement) + .push(.vectorBase) + .push(.existential) + .push(.indexedElement, index: 3)) + + do { + var parser = StringParser("c*.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.**") + _ = try parser.parseProjectionPathFromSIL() + fatalError("too long path not detected") + } catch { + } + do { + var parser = StringParser("**.s0") + _ = try parser.parseProjectionPathFromSIL() + fatalError("wrong '**' not detected") + } catch { } + } + + func testParse(_ pathStr: String, expect: SmallProjectionPath) { + var parser = StringParser(pathStr) + let path = try! parser.parseProjectionPathFromSIL() + assert(path == expect) + let str = path.description + assert(str == pathStr) + } + + func merging() { + testMerge("c1.c0", "c0", expect: "c*.**") + testMerge("c2.c1", "c2", expect: "c2.**") + testMerge("s3.c0", "v**.c0", expect: "v**.c0") + testMerge("c0", "s2.c1", expect: "v**.c*") + testMerge("s1.s1.c2", "s1.c2", expect: "s1.v**.c2") + testMerge("s1.s0", "s2.s0", expect: "v**") + testMerge("ct", "c2", expect: "c*") + testMerge("i1", "i2", expect: "i*") + testMerge("i*", "i2", expect: "i*") + testMerge("s0.i*.e3", "s0.e3", expect: "s0.i*.e3") + testMerge("i*", "v**", expect: "v**") + testMerge("s0.b.i1", "s0.b.i0", expect: "s0.b.i*") + testMerge("s0.b", "s0.1", expect: "s0.v**") + + testMerge("ct.s0.e0.v**.c0", "ct.s0.e0.v**.c0", expect: "ct.s0.e0.v**.c0") + testMerge("ct.s0.s0.c0", "ct.s0.e0.s0.c0", expect: "ct.s0.v**.c0") + } + + func testMerge(_ lhsStr: String, _ rhsStr: String, + expect expectStr: String) { + var lhsParser = StringParser(lhsStr) + let lhs = try! lhsParser.parseProjectionPathFromSIL() + var rhsParser = StringParser(rhsStr) + let rhs = try! rhsParser.parseProjectionPathFromSIL() + var expectParser = StringParser(expectStr) + let expect = try! expectParser.parseProjectionPathFromSIL() + + let result = lhs.merge(with: rhs) + assert(result == expect) + let result2 = rhs.merge(with: lhs) + assert(result2 == expect) + } + + func subtracting() { + testSubtract("s0", "s0.s1", expect: "s1") + testSubtract("s0", "s1", expect: nil) + testSubtract("s0.s1", "s0.s1", expect: "") + testSubtract("i*.s1", "i*.s1", expect: nil) + testSubtract("ct.s1.0.i3.x", "ct.s1.0.i3.x", expect: "") + testSubtract("c0.s1.0.i3", "c0.s1.0.i3.x", expect: "x") + testSubtract("s1.0.i3.x", "s1.0.i3", expect: nil) + testSubtract("v**.s1", "v**.s1", expect: nil) + testSubtract("i*", "i*", expect: nil) + } + + func testSubtract(_ lhsStr: String, _ rhsStr: String, expect expectStr: String?) { + var lhsParser = StringParser(lhsStr) + let lhs = try! lhsParser.parseProjectionPathFromSIL() + var rhsParser = StringParser(rhsStr) + let rhs = try! rhsParser.parseProjectionPathFromSIL() + + let result = lhs.subtract(from: rhs) - func testParse(_ pathStr: String, expect: SmallProjectionPath) { - var parser = StringParser(pathStr) - let path = try! parser.parseProjectionPathFromSIL() - assert(path == expect) - let str = path.description - assert(str == pathStr) - } - - func merging() { - testMerge("c1.c0", "c0", expect: "c*.**") - testMerge("c2.c1", "c2", expect: "c2.**") - testMerge("s3.c0", "v**.c0", expect: "v**.c0") - testMerge("c0", "s2.c1", expect: "v**.c*") - testMerge("s1.s1.c2", "s1.c2", expect: "s1.v**.c2") - testMerge("s1.s0", "s2.s0", expect: "v**") - testMerge("ct", "c2", expect: "c*") - testMerge("i1", "i2", expect: "i*") - testMerge("i*", "i2", expect: "i*") - testMerge("s0.i*.e3", "s0.e3", expect: "s0.i*.e3") - testMerge("i*", "v**", expect: "v**") - - testMerge("ct.s0.e0.v**.c0", "ct.s0.e0.v**.c0", expect: "ct.s0.e0.v**.c0") - testMerge("ct.s0.s0.c0", "ct.s0.e0.s0.c0", expect: "ct.s0.v**.c0") - } - - func testMerge(_ lhsStr: String, _ rhsStr: String, - expect expectStr: String) { - var lhsParser = StringParser(lhsStr) - let lhs = try! lhsParser.parseProjectionPathFromSIL() - var rhsParser = StringParser(rhsStr) - let rhs = try! rhsParser.parseProjectionPathFromSIL() + if let expectStr = expectStr { var expectParser = StringParser(expectStr) let expect = try! expectParser.parseProjectionPathFromSIL() - - let result = lhs.merge(with: rhs) - assert(result == expect) - let result2 = rhs.merge(with: lhs) - assert(result2 == expect) - } - - func subtracting() { - testSubtract("s0", "s0.s1", expect: "s1") - testSubtract("s0", "s1", expect: nil) - testSubtract("s0.s1", "s0.s1", expect: "") - testSubtract("i*.s1", "i*.s1", expect: nil) - testSubtract("ct.s1.0.i3.x", "ct.s1.0.i3.x", expect: "") - testSubtract("c0.s1.0.i3", "c0.s1.0.i3.x", expect: "x") - testSubtract("s1.0.i3.x", "s1.0.i3", expect: nil) - testSubtract("v**.s1", "v**.s1", expect: nil) - testSubtract("i*", "i*", expect: nil) - } - - func testSubtract(_ lhsStr: String, _ rhsStr: String, expect expectStr: String?) { - var lhsParser = StringParser(lhsStr) - let lhs = try! lhsParser.parseProjectionPathFromSIL() - var rhsParser = StringParser(rhsStr) - let rhs = try! rhsParser.parseProjectionPathFromSIL() - - let result = lhs.subtract(from: rhs) - - if let expectStr = expectStr { - var expectParser = StringParser(expectStr) - let expect = try! expectParser.parseProjectionPathFromSIL() - assert(result! == expect) - } else { - assert(result == nil) - } + assert(result! == expect) + } else { + assert(result == nil) } + } - func matching() { - testMatch("ct", "c*", expect: true) - testMatch("c1", "c*", expect: true) - testMatch("s2", "v**", expect: true) - testMatch("1", "v**", expect: true) - testMatch("e1", "v**", expect: true) - testMatch("c*", "c1", expect: false) - testMatch("c*", "ct", expect: false) - testMatch("v**", "s0", expect: false) - testMatch("i1", "i1", expect: true) - testMatch("i1", "i*", expect: true) - testMatch("i*", "i1", expect: false) - - testMatch("s0.s1", "s0.s1", expect: true) - testMatch("s0.s2", "s0.s1", expect: false) - testMatch("s0", "s0.v**", expect: true) - testMatch("s0.s1", "s0.v**", expect: true) - testMatch("s0.1.e2", "s0.v**", expect: true) - testMatch("s0.v**.x.e2", "v**", expect: true) - testMatch("s0.v**", "s0.s1", expect: false) - testMatch("s0.s1.c*", "s0.v**", expect: false) - testMatch("s0.v**", "s0.**", expect: true) - testMatch("s1.v**", "s0.**", expect: false) - testMatch("s0.**", "s0.v**", expect: false) - testMatch("s0.s1", "s0.i*.s1", expect: true) - } - - func testMatch(_ lhsStr: String, _ rhsStr: String, expect: Bool) { - var lhsParser = StringParser(lhsStr) - let lhs = try! lhsParser.parseProjectionPathFromSIL() - var rhsParser = StringParser(rhsStr) - let rhs = try! rhsParser.parseProjectionPathFromSIL() - let result = lhs.matches(pattern: rhs) - assert(result == expect) - } - - func overlapping() { - testOverlap("s0.s1.s2", "s0.s1.s2", expect: true) - testOverlap("s0.s1.s2", "s0.s2.s2", expect: false) - testOverlap("s0.s1.s2", "s0.e1.s2", expect: false) - testOverlap("s0.s1.s2", "s0.s1", expect: true) - testOverlap("s0.s1.s2", "s1.s2", expect: false) - - testOverlap("s0.c*.s2", "s0.ct.s2", expect: true) - testOverlap("s0.c*.s2", "s0.c1.s2", expect: true) - testOverlap("s0.c*.s2", "s0.c1.c2.s2", expect: false) - testOverlap("s0.c*.s2", "s0.s2", expect: false) - - testOverlap("s0.v**.s2", "s0.s3.x", expect: true) - testOverlap("s0.v**.s2.c2", "s0.s3.c1", expect: false) - testOverlap("s0.v**.s2", "s1.s3", expect: false) - testOverlap("s0.v**.s2", "s0.v**.s3", expect: true) - - testOverlap("s0.**", "s0.s3.c1", expect: true) - testOverlap("**", "s0.s3.c1", expect: true) - - testOverlap("i1", "i*", expect: true) - testOverlap("i1", "v**", expect: true) - testOverlap("s0.i*.s1", "s0.s1", expect: true) - } - - func testOverlap(_ lhsStr: String, _ rhsStr: String, expect: Bool) { - var lhsParser = StringParser(lhsStr) - let lhs = try! lhsParser.parseProjectionPathFromSIL() - var rhsParser = StringParser(rhsStr) - let rhs = try! rhsParser.parseProjectionPathFromSIL() - let result = lhs.mayOverlap(with: rhs) - assert(result == expect) - let reversedResult = rhs.mayOverlap(with: lhs) - assert(reversedResult == expect) - } - - func predicates() { - testPredicate("v**", \.hasClassProjection, expect: false) - testPredicate("v**.c0.s1.v**", \.hasClassProjection, expect: true) - testPredicate("c0.**", \.hasClassProjection, expect: true) - testPredicate("c0.c1", \.hasClassProjection, expect: true) - testPredicate("ct", \.hasClassProjection, expect: true) - testPredicate("s0", \.hasClassProjection, expect: false) - - testPredicate("v**", \.mayHaveClassProjection, expect: false) - testPredicate("c0", \.mayHaveClassProjection, expect: true) - testPredicate("1", \.mayHaveClassProjection, expect: false) - testPredicate("**", \.mayHaveClassProjection, expect: true) - - testPredicate("v**", \.mayHaveTwoClassProjections, expect: false) - testPredicate("c0", \.mayHaveTwoClassProjections, expect: false) - testPredicate("**", \.mayHaveTwoClassProjections, expect: true) - testPredicate("v**.c*.s2.1.c0", \.mayHaveTwoClassProjections, expect: true) - testPredicate("c*.s2.1.c0.v**", \.mayHaveTwoClassProjections, expect: true) - testPredicate("v**.c*.**", \.mayHaveTwoClassProjections, expect: true) - } - - func testPredicate(_ pathStr: String, _ property: (SmallProjectionPath) -> Bool, expect: Bool) { - var parser = StringParser(pathStr) - let path = try! parser.parseProjectionPathFromSIL() - let result = property(path) - assert(result == expect) - } - - func path2path() { - testPath2Path("s0.e2.3.c4.s1", { $0.popAllValueFields() }, expect: "c4.s1") - testPath2Path("v**.c4.s1", { $0.popAllValueFields() }, expect: "c4.s1") - testPath2Path("**", { $0.popAllValueFields() }, expect: "**") - - testPath2Path("s0.e2.3.c4.s1.e2.v**.**", { $0.popLastClassAndValuesFromTail() }, expect: "s0.e2.3.c4.s1.e2.v**.**") - testPath2Path("s0.c2.3.c4.s1", { $0.popLastClassAndValuesFromTail() }, expect: "s0.c2.3") - testPath2Path("v**.c*.s1", { $0.popLastClassAndValuesFromTail() }, expect: "v**") - testPath2Path("s1.ct.v**", { $0.popLastClassAndValuesFromTail() }, expect: "s1") - testPath2Path("c0.c1.c2", { $0.popLastClassAndValuesFromTail() }, expect: "c0.c1") - testPath2Path("**", { $0.popLastClassAndValuesFromTail() }, expect: "**") - - testPath2Path("v**.c3", { $0.popIfMatches(.anyValueFields) }, expect: "v**.c3") - testPath2Path("**", { $0.popIfMatches(.anyValueFields) }, expect: "**") - testPath2Path("s0.c3", { $0.popIfMatches(.anyValueFields) }, expect: nil) - - testPath2Path("c0.s3", { $0.popIfMatches(.anyClassField) }, expect: nil) - testPath2Path("**", { $0.popIfMatches(.anyClassField) }, expect: "**") - testPath2Path("c*.e3", { $0.popIfMatches(.anyClassField) }, expect: "e3") - - testPath2Path("i*.e3.s0", { $0.popIfMatches(.enumCase, index: 3) }, expect: "s0") - testPath2Path("i1.e3.s0", { $0.popIfMatches(.enumCase, index: 3) }, expect: nil) - testPath2Path("i*.e3.s0", { $0.popIfMatches(.indexedElement, index: 0) }, expect: "i*.e3.s0") - } - - func testPath2Path(_ pathStr: String, _ transform: (SmallProjectionPath) -> SmallProjectionPath?, expect: String?) { - var parser = StringParser(pathStr) - let path = try! parser.parseProjectionPathFromSIL() - let result = transform(path) - if let expect = expect { - var expectParser = StringParser(expect) - let expectPath = try! expectParser.parseProjectionPathFromSIL() - assert(result == expectPath) - } else { - assert(result == nil) - } + func matching() { + testMatch("ct", "c*", expect: true) + testMatch("c1", "c*", expect: true) + testMatch("s2", "v**", expect: true) + testMatch("1", "v**", expect: true) + testMatch("e1", "v**", expect: true) + testMatch("c*", "c1", expect: false) + testMatch("c*", "ct", expect: false) + testMatch("v**", "s0", expect: false) + testMatch("i1", "i1", expect: true) + testMatch("i1", "i*", expect: true) + testMatch("i*", "i1", expect: false) + + testMatch("s0.s1", "s0.s1", expect: true) + testMatch("s0.s2", "s0.s1", expect: false) + testMatch("s0", "s0.v**", expect: true) + testMatch("s0.s1", "s0.v**", expect: true) + testMatch("s0.1.e2", "s0.v**", expect: true) + testMatch("s0.v**.x.e2", "v**", expect: true) + testMatch("s0.v**", "s0.s1", expect: false) + testMatch("s0.s1.c*", "s0.v**", expect: false) + testMatch("s0.v**", "s0.**", expect: true) + testMatch("s1.v**", "s0.**", expect: false) + testMatch("s0.**", "s0.v**", expect: false) + testMatch("s0.s1", "s0.i*.s1", expect: true) + testMatch("s0.b.s1", "s0.b.i*.s1", expect: true) + } + + func testMatch(_ lhsStr: String, _ rhsStr: String, expect: Bool) { + var lhsParser = StringParser(lhsStr) + let lhs = try! lhsParser.parseProjectionPathFromSIL() + var rhsParser = StringParser(rhsStr) + let rhs = try! rhsParser.parseProjectionPathFromSIL() + let result = lhs.matches(pattern: rhs) + assert(result == expect) + } + + func overlapping() { + testOverlap("s0.s1.s2", "s0.s1.s2", expect: true) + testOverlap("s0.s1.s2", "s0.s2.s2", expect: false) + testOverlap("s0.s1.s2", "s0.e1.s2", expect: false) + testOverlap("s0.s1.s2", "s0.s1", expect: true) + testOverlap("s0.s1.s2", "s1.s2", expect: false) + + testOverlap("s0.c*.s2", "s0.ct.s2", expect: true) + testOverlap("s0.c*.s2", "s0.c1.s2", expect: true) + testOverlap("s0.c*.s2", "s0.c1.c2.s2", expect: false) + testOverlap("s0.c*.s2", "s0.s2", expect: false) + + testOverlap("s0.v**.s2", "s0.s3.x", expect: true) + testOverlap("s0.v**.s2.c2", "s0.s3.c1", expect: false) + testOverlap("s0.v**.s2", "s1.s3", expect: false) + testOverlap("s0.v**.s2", "s0.v**.s3", expect: true) + + testOverlap("s0.**", "s0.s3.c1", expect: true) + testOverlap("**", "s0.s3.c1", expect: true) + + testOverlap("i1", "i*", expect: true) + testOverlap("i1", "v**", expect: true) + testOverlap("s0.i*.s1", "s0.s1", expect: true) + testOverlap("s0.b.s1", "s0.b.i*.s1", expect: true) + testOverlap("s0.b.i0.s1", "s0.b.i1.s1", expect: false) + testOverlap("s0.b.i2.s1", "s0.b.i1.s1", expect: false) + testOverlap("s0.b.s1", "s0.b.i0.s1", expect: true) + testOverlap("s0.b", "s0.b.i1", expect: false) + testOverlap("s0.b.i1", "s0.b", expect: false) + testOverlap("s0.b.i1", "s0", expect: true) + } + + func testOverlap(_ lhsStr: String, _ rhsStr: String, expect: Bool) { + var lhsParser = StringParser(lhsStr) + let lhs = try! lhsParser.parseProjectionPathFromSIL() + var rhsParser = StringParser(rhsStr) + let rhs = try! rhsParser.parseProjectionPathFromSIL() + let result = lhs.mayOverlap(with: rhs) + assert(result == expect) + let reversedResult = rhs.mayOverlap(with: lhs) + assert(reversedResult == expect) + } + + func predicates() { + testPredicate("v**", \.hasClassProjection, expect: false) + testPredicate("v**.c0.s1.v**", \.hasClassProjection, expect: true) + testPredicate("c0.**", \.hasClassProjection, expect: true) + testPredicate("c0.c1", \.hasClassProjection, expect: true) + testPredicate("ct", \.hasClassProjection, expect: true) + testPredicate("s0", \.hasClassProjection, expect: false) + + testPredicate("v**", \.mayHaveClassProjection, expect: false) + testPredicate("c0", \.mayHaveClassProjection, expect: true) + testPredicate("1", \.mayHaveClassProjection, expect: false) + testPredicate("**", \.mayHaveClassProjection, expect: true) + + testPredicate("v**", \.mayHaveTwoClassProjections, expect: false) + testPredicate("c0", \.mayHaveTwoClassProjections, expect: false) + testPredicate("**", \.mayHaveTwoClassProjections, expect: true) + testPredicate("v**.c*.s2.1.c0", \.mayHaveTwoClassProjections, expect: true) + testPredicate("c*.s2.1.c0.v**", \.mayHaveTwoClassProjections, expect: true) + testPredicate("v**.c*.**", \.mayHaveTwoClassProjections, expect: true) + } + + func testPredicate(_ pathStr: String, _ property: (SmallProjectionPath) -> Bool, expect: Bool) { + var parser = StringParser(pathStr) + let path = try! parser.parseProjectionPathFromSIL() + let result = property(path) + assert(result == expect) + } + + func path2path() { + testPath2Path("s0.b.e2.3.c4.s1", { $0.popAllValueFields() }, expect: "c4.s1") + testPath2Path("v**.c4.s1", { $0.popAllValueFields() }, expect: "c4.s1") + testPath2Path("**", { $0.popAllValueFields() }, expect: "**") + + testPath2Path("s0.e2.3.c4.s1.e2.v**.**", { $0.popLastClassAndValuesFromTail() }, expect: "s0.e2.3.c4.s1.e2.v**.**") + testPath2Path("s0.c2.3.c4.s1", { $0.popLastClassAndValuesFromTail() }, expect: "s0.c2.3") + testPath2Path("v**.c*.s1", { $0.popLastClassAndValuesFromTail() }, expect: "v**") + testPath2Path("s1.ct.v**", { $0.popLastClassAndValuesFromTail() }, expect: "s1") + testPath2Path("c0.c1.c2", { $0.popLastClassAndValuesFromTail() }, expect: "c0.c1") + testPath2Path("**", { $0.popLastClassAndValuesFromTail() }, expect: "**") + + testPath2Path("v**.c3", { $0.popIfMatches(.anyValueFields) }, expect: "v**.c3") + testPath2Path("**", { $0.popIfMatches(.anyValueFields) }, expect: "**") + testPath2Path("s0.c3", { $0.popIfMatches(.anyValueFields) }, expect: nil) + + testPath2Path("c0.s3", { $0.popIfMatches(.anyClassField) }, expect: nil) + testPath2Path("**", { $0.popIfMatches(.anyClassField) }, expect: "**") + testPath2Path("c*.e3", { $0.popIfMatches(.anyClassField) }, expect: "e3") + + testPath2Path("i*.e3.s0", { $0.popIfMatches(.enumCase, index: 3) }, expect: "s0") + testPath2Path("i1.e3.s0", { $0.popIfMatches(.enumCase, index: 3) }, expect: nil) + testPath2Path("i*.e3.s0", { $0.popIfMatches(.indexedElement, index: 0) }, expect: "i*.e3.s0") + } + + func testPath2Path(_ pathStr: String, _ transform: (SmallProjectionPath) -> SmallProjectionPath?, expect: String?) { + var parser = StringParser(pathStr) + let path = try! parser.parseProjectionPathFromSIL() + let result = transform(path) + if let expect = expect { + var expectParser = StringParser(expect) + let expectPath = try! expectParser.parseProjectionPathFromSIL() + assert(result == expectPath) + } else { + assert(result == nil) } } + + func indexedElements() { + let p1 = SmallProjectionPath(.indexedElement, index: 1) + let (k1, i1, s1) = p1.pop() + assert(k1 == .indexedElement && i1 == 1 && s1.isEmpty) + + let p2 = SmallProjectionPath(.indexedElement, index: -1) + let (k2, _, s2) = p2.pop() + assert(k2 == .anything && s2.isEmpty) + + let p3 = SmallProjectionPath(.indexedElement, index: 0xfffffffffffff) + let (k3, i3, s3) = p3.pop() + assert(k3 == .indexedElement && i3 == 0xfffffffffffff && s3.isEmpty) + + let p4 = p3.push(.indexedElement, index: Int.max) + let (k4, _, s4) = p4.pop() + assert(k4 == .anyIndexedElement && s4.isEmpty) + } } diff --git a/SwiftCompilerSources/Sources/SIL/Utilities/Test.swift b/SwiftCompilerSources/Sources/SIL/Utilities/Test.swift new file mode 100644 index 0000000000000..8206f5b7b40f8 --- /dev/null +++ b/SwiftCompilerSources/Sources/SIL/Utilities/Test.swift @@ -0,0 +1,251 @@ +//===----------- Test.swift - In-IR tests from Swift source ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 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 +// +//===----------------------------------------------------------------------===// +// +// TO ADD A NEW TEST, just add a new Test instance. +// - In the source file containing the functionality you want to test: +// let myNewTest = Test("my_new_test") { function, arguments, context in +// } +// - In SwiftCompilerSources/Sources/SIL/Test.swift's registerTests +// function, add a new argument to the variadic function: +// registerTests(..., myNewTest) +// +//===----------------------------------------------------------------------===// +// +// Provides a mechanism for writing tests against compiler code in the context +// of a function. The goal is to get the same effect as calling a function and +// checking its output. +// +// This is done via the specify_test instruction. Using one or more instances +// of it in your test case's SIL function, you can specify which test (instance +// of Test) should be run and what arguments should be provided to it. +// For full details of the specify_test instruction's grammar, +// see doc/SIL/Instructions.md. +// +// The test grabs the arguments it expects out of the TestArguments instance +// it is provided. It calls some function or functions. It then prints out +// interesting results. These results can then be FileCheck'd. +// +// CASE STUDY: +// Here's an example of how it works: +// 0) A source file, NeatUtils.swift contains +// +// fileprivate func myNeatoUtility(int: Int, value: Value, function: Function) { ... } +// +// and +// +// let myNeatoUtilityTest = Test("my_neato_utility") { function, arguments, test in +// // The code here is described in detail below. +// // See 4). +// let count = arguments.takeInt() +// let target = arguments.takeValue() +// let callee = arguments.takeFunction() +// // See 5) +// myNeatoUtility(int: count, value: target, function: callee) +// // See 6) +// print(function) +// } +// 1) A test test/SILOptimizer/interesting_functionality_unit.sil runs the +// TestRunner pass: +// // RUN: %target-sil-opt -test-runner %s -o /dev/null 2>&1 | %FileCheck %s +// // REQUIRES: swift_in_compiler +// 2) A function in interesting_functionality_unit.sil contains the +// specify_test instruction. +// sil @f : $() -> () { +// ... +// specify_test "my_neato_utility 43 %2 @function[other_fun]" +// ... +// } +// 3) TestRunner finds the Test instance myNeatoUtilityTest registered +// under the name "my_neato_utility", and calls run() on it, passing the +// passing first the function, last the Test instance, AND in the +// middle, most importantly a TestArguments instance that contains +// +// (43 : Int, someValue : Value, other_fun : Function) +// +// 4) myNeatoUtilityTest calls takeUInt(), takeValue(), and takeFunction() on +// the test::Arguments instance. +// let count = arguments.takeInt() +// let target = arguments.takeValue() +// let callee = arguments.takeFunction() +// 5) myNeatoUtilityTest calls myNeatoUtility, passing these values along. +// myNeatoUtility(int: count, value: target, function: callee) +// 6) myNeatoUtilityTest then dumps out the current function, which was modified +// in the process. +// print(function) +// 7) The test file test/SILOptimizer/interesting_functionality_unit.sil matches +// the +// expected contents of the modified function: +// // CHECK-LABEL: sil @f +// // CHECK-NOT: function_ref @other_fun +// +//===----------------------------------------------------------------------===// + +import Basic +import SILBridging + +/// The primary interface to in-IR tests. +public struct Test { + let name: String + let invocation: TestInvocation + + public init(_ name: String, invocation: @escaping TestInvocation) { + self.name = name + self.invocation = invocation + } +} + +/// The type of the closure passed to a Test. +public typealias TestInvocation = @convention(thin) (Function, TestArguments, TestContext) -> () + + +public struct TestContext: MutatingContext { + public let _bridged: BridgedContext + public let notifyInstructionChanged: (Instruction) -> () = { inst in } + + fileprivate init(bridged: BridgedContext) { self._bridged = bridged} +} + +/// Wraps the arguments specified in the specify_test instruction. +public struct TestArguments { + public var bridged: BridgedTestArguments + fileprivate init(bridged: BridgedTestArguments) { + self.bridged = bridged + } + + public var hasUntaken: Bool { bridged.hasUntaken() } + public func takeString() -> StringRef { StringRef(bridged: bridged.takeString()) } + public func takeBool() -> Bool { bridged.takeBool() } + public func takeInt() -> Int { bridged.takeInt() } + public func takeOperand() -> Operand { Operand(bridged: bridged.takeOperand()) } + public func takeValue() -> Value { bridged.takeValue().value } + public func takeInstruction() -> Instruction { bridged.takeInstruction().instruction } + public func takeArgument() -> Argument { bridged.takeArgument().argument } + public func takeBlock() -> BasicBlock { bridged.takeBlock().block } + public func takeFunction() -> Function { bridged.takeFunction().function } +} + +extension BridgedTestArguments { + public var native: TestArguments { TestArguments(bridged: self) } +} + +/// Registration of each test in the SIL module. +public func registerTests() { + // Register each test. + registerTests( + parseTestSpecificationTest, + smallProjectionPathTest, + getAccessBaseTest, + borrowIntroducersTest, + enclosingValuesTest, + forwardingDefUseTest, + forwardingUseDefTest + ) + + registerTestThunk(testThunk) +} + +private func registerTests(_ tests: Test...) { + tests.forEach { registerTest($0) } +} + +private func registerTest(_ test: Test) { + test.name._withBridgedStringRef { ref in + registerTest(ref, castToOpaquePointer(fromInvocation: test.invocation)) + } +} + +/// The function called by the swift::test::FunctionTest which invokes the +/// actual test function. +/// +/// This function is necessary because tests need to be written in terms of +/// native Swift types (Function, TestArguments, TestContext) +/// rather than their bridged variants, but such a function isn't representable +/// in C++. This thunk unwraps the bridged types and invokes the real function. +private func testThunk( + _ erasedInvocation: UnsafeMutableRawPointer, + _ function: BridgedFunction, + _ arguments: BridgedTestArguments, + _ bridgedContext: BridgedContext) { + let invocation = castToInvocation(fromOpaquePointer: erasedInvocation) + let context = TestContext(bridged: bridgedContext) + invocation(function.function, arguments.native, context) +} + +/// Bitcast a thin test closure to void *. +/// +/// Needed so that the closure can be represented in C++ for storage in the test +/// registry. +private func castToOpaquePointer(fromInvocation invocation: TestInvocation) -> UnsafeMutableRawPointer { + return unsafeBitCast(invocation, to: UnsafeMutableRawPointer.self) +} + +/// Bitcast a void * to a thin test closure. +/// +/// Needed so that the closure stored in the C++ test registry can be invoked via the testThunk. +private func castToInvocation(fromOpaquePointer erasedInvocation: UnsafeMutableRawPointer) -> TestInvocation { + return unsafeBitCast(erasedInvocation, to: TestInvocation.self) +} + +// Arguments: +// - string: list of characters, each of which specifies subsequent arguments +// - A: (block) argument +// - F: function +// - B: block +// - I: instruction +// - V: value +// - O: operand +// - b: boolean +// - u: unsigned +// - s: string +// - ... +// - an argument of the type specified in the initial string +// - ... +// Dumps: +// - for each argument (after the initial string) +// - its type +// - something to identify the instance (mostly this means calling dump) +let parseTestSpecificationTest = Test("test_specification_parsing") { function, arguments, context in + let expectedFields = arguments.takeString() + for expectedField in expectedFields.string { + switch expectedField { + case "A": + let argument = arguments.takeArgument() + print("argument:\n\(argument)") + case "F": + let function = arguments.takeFunction() + print("function: \(function.name)") + case "B": + let block = arguments.takeBlock() + print("block:\n\(block)") + case "I": + let instruction = arguments.takeInstruction() + print("instruction: \(instruction)") + case "V": + let value = arguments.takeValue() + print("value: \(value)") + case "O": + let operand = arguments.takeOperand() + print("operand: \(operand)") + case "u": + let u = arguments.takeInt() + print("uint: \(u)") + case "b": + let b = arguments.takeBool() + print("bool: \(b)") + case "s": + let s = arguments.takeString() + print("string: \(s)") + default: + fatalError("unknown field type was expected?!"); + } + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/Verifier.swift b/SwiftCompilerSources/Sources/SIL/Utilities/Verifier.swift similarity index 83% rename from SwiftCompilerSources/Sources/Optimizer/Utilities/Verifier.swift rename to SwiftCompilerSources/Sources/SIL/Utilities/Verifier.swift index 42f5bba0f7099..6fbd6c5b96893 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/Verifier.swift +++ b/SwiftCompilerSources/Sources/SIL/Utilities/Verifier.swift @@ -10,24 +10,29 @@ // //===----------------------------------------------------------------------===// -import SIL -import OptimizerBridging +import SILBridging +/// To add verification for a specific instruction, let the instruction class conform to +/// this protocol and implement the `verify` method. private protocol VerifiableInstruction : Instruction { - func verify(_ context: FunctionPassContext) + func verify(_ context: VerifierContext) } private func require(_ condition: Bool, _ message: @autoclosure () -> String, atInstruction: Instruction? = nil) { if !condition { let msg = message() msg._withBridgedStringRef { stringRef in - verifierError(stringRef, atInstruction.bridged, Optional.none.bridged) + BridgedVerifier.verifierError(stringRef, atInstruction.bridged, Optional.none.bridged) } } } +struct VerifierContext: Context { + let _bridged: BridgedContext +} + extension Function { - func verify(_ context: FunctionPassContext) { + func verify(_ context: VerifierContext) { for block in blocks { for arg in block.arguments { arg.verify(context) @@ -56,23 +61,24 @@ private extension Instruction { func checkGuaranteedResults() { for result in results where result.ownership == .guaranteed { - require(BeginBorrowValue(result) != nil || self is ForwardingInstruction, + require(BeginBorrowValue(result) != nil || self is ForwardingInstruction || result.isGuaranteedApplyResult, "\(result) must either be a BeginBorrowValue or a ForwardingInstruction") } } } private extension Argument { - func verify(_ context: FunctionPassContext) { + func verify(_ context: VerifierContext) { if let phi = Phi(self), phi.value.ownership == .guaranteed { phi.verifyBorrowedFromUse() - require(phi.isReborrow == phi.hasBorrowEndingUse || - // In a dead-end block an end_borrow might have been deleted. - // TODO: this check is not needed anymore once we have complete OSSA lifetimes. - (isReborrow && context.deadEndBlocks.isDeadEnd(parentBlock)), + // TODO: enable this check once we have complete OSSA lifetimes. + // In a dead-end block an end_borrow might have been deleted. + /* + require(phi.isReborrow == phi.hasBorrowEndingUse, "\(self) has stale reborrow flag"); + */ } } @@ -95,7 +101,7 @@ private extension Phi { } extension BorrowedFromInst : VerifiableInstruction { - func verify(_ context: FunctionPassContext) { + func verify(_ context: VerifierContext) { for ev in enclosingValues { require(ev.isValidEnclosingValueInBorrowedFrom, "invalid enclosing value in borrowed-from: \(ev)") @@ -135,7 +141,7 @@ private extension Value { } extension LoadBorrowInst : VerifiableInstruction { - func verify(_ context: FunctionPassContext) { + func verify(_ context: VerifierContext) { if isUnchecked { return } @@ -148,16 +154,26 @@ extension LoadBorrowInst : VerifiableInstruction { } } +extension VectorBaseAddrInst : VerifiableInstruction { + func verify(_ context: VerifierContext) { + require(vector.type.isBuiltinFixedArray, + "vector operand of vector_element_addr must be a Builtin.FixedArray") + require(type == vector.type.builtinFixedArrayElementType(in: parentFunction, + maximallyAbstracted: true).addressType, + "result of vector_element_addr has wrong type") + } +} + // Used to check if any instruction is mutating the memory location within the liverange of a `load_borrow`. // Note that it is not checking if an instruction _may_ mutate the memory, but it's checking if any instruction // _definitely_ will mutate the memory. // Otherwise the risk would be too big for false alarms. It also means that this verification is not perfect and // might miss some subtle violations. private struct MutatingUsesWalker : AddressDefUseWalker { - let context: FunctionPassContext + let context: VerifierContext var mutatingInstructions: InstructionSet - init(_ context: FunctionPassContext) { + init(_ context: VerifierContext) { self.context = context self.mutatingInstructions = InstructionSet(context) } @@ -230,7 +246,7 @@ private extension Operand { case let copy as SourceDestAddrInstruction: if self == copy.destinationOperand { return true - } else if self == copy.sourceOperand && copy.isTakeOfSrc { + } else if self == copy.sourceOperand && copy.isTakeOfSource { return true } return false @@ -258,9 +274,9 @@ private extension Operand { } func registerVerifier() { - BridgedUtilities.registerVerifier( - { (bridgedCtxt: BridgedPassContext, bridgedFunction: BridgedFunction) in - let context = FunctionPassContext(_bridged: bridgedCtxt) + BridgedVerifier.registerVerifier( + { (bridgedCtxt: BridgedContext, bridgedFunction: BridgedFunction) in + let context = VerifierContext(_bridged: bridgedCtxt) bridgedFunction.function.verify(context) } ) diff --git a/SwiftCompilerSources/Sources/SIL/Utilities/WalkUtils.swift b/SwiftCompilerSources/Sources/SIL/Utilities/WalkUtils.swift index a5ecef2512688..59c27a7cf291a 100644 --- a/SwiftCompilerSources/Sources/SIL/Utilities/WalkUtils.swift +++ b/SwiftCompilerSources/Sources/SIL/Utilities/WalkUtils.swift @@ -39,10 +39,11 @@ public enum WalkResult { } extension Sequence { + // Walk each element until the walk aborts. public func walk( - _ predicate: (Element) throws -> WalkResult + _ walker: (Element) throws -> WalkResult ) rethrows -> WalkResult { - return try contains { try predicate($0) == .abortWalk } ? .abortWalk : .continueWalk + return try contains { try walker($0) == .abortWalk } ? .abortWalk : .continueWalk } } @@ -508,6 +509,12 @@ extension AddressDefUseWalker { } else { return unmatchedPath(address: operand, path: path) } + case let vba as VectorBaseAddrInst: + if let path = path.popIfMatches(.vectorBase, index: 0) { + return walkDownUses(ofAddress: vba, path: path) + } else { + return unmatchedPath(address: operand, path: path) + } case is InitEnumDataAddrInst, is UncheckedTakeEnumDataAddrInst: let ei = instruction as! SingleValueInstruction if let path = path.popIfMatches(.enumCase, index: (instruction as! EnumInstruction).caseIndex) { @@ -530,8 +537,12 @@ extension AddressDefUseWalker { return walkDownUses(ofAddress: ia, path: subPath.push(.anyIndexedElement, index: 0)) } return walkDownUses(ofAddress: ia, path: path) - case let mmc as MarkUnresolvedNonCopyableValueInst: - return walkDownUses(ofAddress: mmc, path: path) + case is MarkUninitializedInst, + is MoveOnlyWrapperToCopyableAddrInst, + is CopyableToMoveOnlyWrapperAddrInst, + is MarkUnresolvedNonCopyableValueInst, + is DropDeinitInst: + return walkDownUses(ofAddress: instruction as! SingleValueInstruction, path: path) case let ba as BeginAccessInst: // Don't treat `end_access` as leaf-use. Just ignore it. return walkDownNonEndAccessUses(of: ba, path: path) @@ -813,24 +824,27 @@ extension AddressUseDefWalker { return walkUp(address: sea.struct, path: path.push(.structField, index: sea.fieldIndex)) case let tea as TupleElementAddrInst: return walkUp(address: tea.tuple, path: path.push(.tupleField, index: tea.fieldIndex)) + case let vba as VectorBaseAddrInst: + return walkUp(address: vba.vector, path: path.push(.vectorBase, index: 0)) case let ida as InitEnumDataAddrInst: return walkUp(address: ida.operand.value, path: path.push(.enumCase, index: ida.caseIndex)) case let uteda as UncheckedTakeEnumDataAddrInst: return walkUp(address: uteda.operand.value, path: path.push(.enumCase, index: uteda.caseIndex)) case is InitExistentialAddrInst, is OpenExistentialAddrInst: return walkUp(address: (def as! Instruction).operands[0].value, path: path.push(.existential, index: 0)) - case is BeginAccessInst, is MarkUnresolvedNonCopyableValueInst: - return walkUp(address: (def as! Instruction).operands[0].value, path: path) case let ia as IndexAddrInst: if let idx = ia.constantIndex { return walkUp(address: ia.base, path: path.push(.indexedElement, index: idx)) } else { return walkUp(address: ia.base, path: path.push(.anyIndexedElement, index: 0)) } - case is MarkDependenceInst, is MarkUninitializedInst: - return walkUp(address: (def as! Instruction).operands[0].value, path: path) - case is MoveOnlyWrapperToCopyableAddrInst, - is CopyableToMoveOnlyWrapperAddrInst: + case is BeginAccessInst, + is MarkDependenceInst, + is MarkUninitializedInst, + is MoveOnlyWrapperToCopyableAddrInst, + is CopyableToMoveOnlyWrapperAddrInst, + is MarkUnresolvedNonCopyableValueInst, + is DropDeinitInst: return walkUp(address: (def as! Instruction).operands[0].value, path: path) default: return rootDef(address: def, path: path) diff --git a/SwiftCompilerSources/Sources/SIL/VTable.swift b/SwiftCompilerSources/Sources/SIL/VTable.swift index 89dabfdef7d48..7f10933291491 100644 --- a/SwiftCompilerSources/Sources/SIL/VTable.swift +++ b/SwiftCompilerSources/Sources/SIL/VTable.swift @@ -87,6 +87,7 @@ public struct VTable : CustomStringConvertible, NoReflectionChildren { public var `class`: ClassDecl { bridged.getClass().getAs(ClassDecl.self) } + /// Returns the concrete class type if this is a specialized vTable. public var specializedClassType: Type? { bridged.getSpecializedClassType().typeOrNil } public var isSpecialized: Bool { specializedClassType != nil } diff --git a/SwiftCompilerSources/Sources/SIL/Value.swift b/SwiftCompilerSources/Sources/SIL/Value.swift index 829a3cd41b048..a346cb621ba80 100644 --- a/SwiftCompilerSources/Sources/SIL/Value.swift +++ b/SwiftCompilerSources/Sources/SIL/Value.swift @@ -243,9 +243,9 @@ extension Value { /// struct S { var c1: C; var c2: C } /// let s: S /// - /// `s.allContainedAddresss` refers to `s.c1.x`, `s.c1.y`, `s.c2.x` and `s.c2.y` + /// `s.allContainedAddresses` refers to `s.c1.x`, `s.c1.y`, `s.c2.x` and `s.c2.y` /// - public var allContainedAddresss: ProjectedValue { + public var allContainedAddresses: ProjectedValue { if type.isAddress { // This is the regular case: the path selects any sub-fields of an address. return at(SmallProjectionPath(.anyValueFields)) @@ -298,6 +298,10 @@ public final class Undef : Value { public var hasTrivialNonPointerType: Bool { false } public var isLexical: Bool { false } + + public static func get(type: Type, _ context: some MutatingContext) -> Undef { + context._bridged.getSILUndef(type.bridged).value as! Undef + } } final class PlaceholderValue : Value { diff --git a/SwiftCompilerSources/Sources/SIL/WitnessTable.swift b/SwiftCompilerSources/Sources/SIL/WitnessTable.swift index 88df8fc194ab8..cb779bb033e6b 100644 --- a/SwiftCompilerSources/Sources/SIL/WitnessTable.swift +++ b/SwiftCompilerSources/Sources/SIL/WitnessTable.swift @@ -31,7 +31,7 @@ public struct WitnessTable : CustomStringConvertible, NoReflectionChildren { case associatedType(requirement: AssociatedTypeDecl, witness: CanonicalType) /// A witness table entry describing the witness for an associated type's protocol requirement. - case associatedConformance(requirement: CanonicalType, substType: CanonicalType, witness: Conformance) + case associatedConformance(requirement: CanonicalType, witness: Conformance) /// A witness table entry referencing the protocol conformance for a refined base protocol. case baseProtocol(requirement: ProtocolDecl, witness: Conformance) @@ -48,7 +48,6 @@ public struct WitnessTable : CustomStringConvertible, NoReflectionChildren { witness: CanonicalType(bridged: bridged.getAssociatedTypeWitness())) case .associatedConformance: self = .associatedConformance(requirement: CanonicalType(bridged: bridged.getAssociatedConformanceRequirement()), - substType: CanonicalType(bridged: bridged.getAssociatedConformanceSubstType()), witness: Conformance(bridged: bridged.getAssociatedConformanceWitness())) case .baseProtocol: self = .baseProtocol(requirement: bridged.getBaseProtocolRequirement().getAs(ProtocolDecl.self), @@ -71,9 +70,8 @@ public struct WitnessTable : CustomStringConvertible, NoReflectionChildren { OptionalBridgedFunction(obj: witness?.bridged.obj)) case .associatedType(let requirement, let witness): return BridgedWitnessTableEntry.createAssociatedType(requirement.bridged, witness.bridged) - case .associatedConformance(let requirement, let substType, let witness): + case .associatedConformance(let requirement, let witness): return BridgedWitnessTableEntry.createAssociatedConformance(requirement.bridged, - substType.bridged, witness.bridged) case .baseProtocol(let requirement, let witness): return BridgedWitnessTableEntry.createBaseProtocol(requirement.bridged, witness.bridged) @@ -113,6 +111,9 @@ public struct WitnessTable : CustomStringConvertible, NoReflectionChildren { public var isDefinition: Bool { !bridged.isDeclaration() } + // True, if this is a specialized witness table (currently only used in embedded mode). + public var isSpecialized: Bool { bridged.isSpecialized() } + public var description: String { return String(taking: bridged.getDebugDescription()) } diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index bf43145b7c690..be40f43902092 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -220,12 +220,24 @@ set(SWIFT_BENCH_MODULES set(SWIFT_MULTISOURCE_SWIFT_BENCHES multi-source/PrimsSplit + multi-source/Monoids ) set(PrimsSplit_sources multi-source/PrimsSplit/Prims.swift multi-source/PrimsSplit/Prims_main.swift) +set(Monoids_sources + multi-source/Monoids/Automaton.swift + multi-source/Monoids/Benchmark.swift + multi-source/Monoids/Enumeration.swift + multi-source/Monoids/Monoids.swift + multi-source/Monoids/Presentation.swift + multi-source/Monoids/RewritingSystem.swift + multi-source/Monoids/Solver.swift + multi-source/Monoids/Strategy.swift + multi-source/Monoids/Trie.swift) + set(BENCH_DRIVER_LIBRARY_FLAGS) if (SWIFT_RUNTIME_ENABLE_LEAK_CHECKER) set(BENCH_DRIVER_LIBRARY_FLAGS -DSWIFT_RUNTIME_ENABLE_LEAK_CHECKER) diff --git a/benchmark/Package.swift b/benchmark/Package.swift index 4715683480358..32afaae13b436 100644 --- a/benchmark/Package.swift +++ b/benchmark/Package.swift @@ -178,17 +178,22 @@ targets += multiSourceLibraries.map { lib in dependencies: [ .target(name: "TestsUtils") ], - path: lib.parentSubDir) + path: "\(lib.parentSubDir)/\(lib.name)") } //===--- // Top Level Definition // -let p = Package( +var p = Package( name: "swiftbench", products: products, targets: targets, swiftLanguageVersions: [.v4], cxxLanguageStandard: .cxx20 ) + +// Let's build for Swift 5.5-aligned runtimes. +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +p.platforms = [.macOS(.v12), .iOS(.v15), .watchOS(.v8), .tvOS(.v15)] +#endif diff --git a/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake index 7c5387bb5ccd6..d7b7803adcee2 100644 --- a/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake +++ b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake @@ -363,6 +363,10 @@ function (swift_benchmark_compile_archopts) list(APPEND common_options "-g") endif() + if("${optflag}" STREQUAL "Onone") + list(APPEND common_options "-DDEBUG") + endif() + if (is_darwin) list(APPEND common_options "-I" "${srcdir}/utils/ObjectiveCTests" @@ -400,6 +404,10 @@ function (swift_benchmark_compile_archopts) "-target" "${target}" "-${driver_opt}") + if(${optflag} STREQUAL "Onone") + list(APPEND common_options_driver "-DDEBUG") + endif() + if(SWIFT_BENCHMARK_GENERATE_DEBUG_INFO) list(APPEND common_options_driver "-g") endif() @@ -716,9 +724,6 @@ function(swift_benchmark_compile) if(NOT SWIFT_BENCHMARK_BUILT_STANDALONE) set(stdlib_dependencies "swift-frontend" "swiftCore-${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}") - if((SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) AND SWIFT_BUILD_SDK_OVERLAY) - list(APPEND stdlib_dependencies "swiftDarwin-${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}") - endif() foreach(stdlib_dependency ${UNIVERSAL_LIBRARY_NAMES_${SWIFT_BENCHMARK_COMPILE_PLATFORM}}) string(FIND "${stdlib_dependency}" "Unittest" find_output) if("${find_output}" STREQUAL "-1") diff --git a/benchmark/multi-source/Monoids/Automaton.swift b/benchmark/multi-source/Monoids/Automaton.swift new file mode 100644 index 0000000000000..7c75553692c65 --- /dev/null +++ b/benchmark/multi-source/Monoids/Automaton.swift @@ -0,0 +1,153 @@ +/// This file implements an algorithm to compute the number of elements in a +/// monoid (or determine it is infinite), given a complete presentation. + +/// A finite state automaton, given by a set of vertices and edges. +struct Automaton { + var states: [Word] = [] + var transitions: [(Word, Symbol, Word)] = [] +} + +extension Automaton { + var hasStar: Bool { + for state in states { + var visited = Set() + + func rec(_ state: Word) -> Bool { + for (from, _, to) in transitions { + if from == state { + if visited.contains(to) { + return true + } else { + visited.insert(to) + if rec(to) { return true } + visited.remove(to) + } + } + } + + return false + } + + visited.insert(state) + if rec(state) { return true } + visited.remove(state) + } + + return false + } + + /// If this automaton is star-free, count the number of unique words accepted. + var countWords: Int { + func R(_ q: Word) -> [Word] { + var result: [Word] = [] + + for (from, _, to) in transitions { + if to == q { + result.append(from) + } + } + + return result + } + + func T(_ q: Word, _ p: Word) -> Int { + var letters = Set() + for (from, x, to) in transitions { + if from == q && to == p { + letters.insert(x) + } + } + return letters.count + } + + func N(_ q: Word) -> Int { + if q == [] { + return 1 + } + + var result = 0 + for p in R(q) { + result += N(p) * T(p, q) + } + return result + } + + var result = 0 + + for q in states { + result += N(q) + } + + return result + } +} + +extension RewritingSystem { + /// Constructs an automaton to recognize the complement of this regular set: + /// + /// .*(x1|x2|...).* + /// + /// where 'words' is [x1, x2, ...]. + /// + /// This is Lemma 2.1.3 in: + /// + /// String Rewriting Systems, R.V. Book, F. Otto 1993. Springer New York. + func buildAutomaton() -> Automaton { + // Proper prefixes of each word. + var prefixes = Set() + + var result = Automaton() + + func isIrreducible(_ word: Word) -> Bool { + return reduceOne(word) == nil + } + + prefixes.insert([]) + for rule in presentation.rules { + let word = rule.lhs + for i in 0 ..< word.count { + let prefix = Word(word[0 ..< i]) + prefixes.insert(prefix) + } + } + + result.states = prefixes.sorted { compare($0, $1, order: .shortlex) == .lessThan } + + for prefix in prefixes { + for x in 0 ..< UInt8(alphabet) { + let word = prefix + [x] + + if prefixes.contains(word) { + result.transitions.append((prefix, x, word)) + continue + } + + if !isIrreducible(word) { + continue + } + + for i in 1 ... word.count { + let suffix = Word(word[i...]) + + if prefixes.contains(suffix) { + result.transitions.append((prefix, x, suffix)) + break + } + } + } + } + + return result + } + + /// Returns the number of irreducible words in this monoid presentation, or + /// nil if this set is infinite. + var cardinality: Int? { + precondition(state == .complete) + let automaton = buildAutomaton() + if automaton.hasStar { + return nil + } + return automaton.countWords + } +} diff --git a/benchmark/multi-source/Monoids/Benchmark.swift b/benchmark/multi-source/Monoids/Benchmark.swift new file mode 100644 index 0000000000000..4d391af453f67 --- /dev/null +++ b/benchmark/multi-source/Monoids/Benchmark.swift @@ -0,0 +1,14 @@ +import TestsUtils + +public let benchmarks = [ + BenchmarkInfo( + name: "Monoids", + runFunction: run_Monoids, + tags: [.algorithm, .miniapplication, .long]) +] + +func run_Monoids(_ n: Int) async { + for _ in 0 ... n { + await run(output: false) + } +} diff --git a/benchmark/multi-source/Monoids/Enumeration.swift b/benchmark/multi-source/Monoids/Enumeration.swift new file mode 100644 index 0000000000000..d51b8e7619cde --- /dev/null +++ b/benchmark/multi-source/Monoids/Enumeration.swift @@ -0,0 +1,171 @@ +/// This file implements algorithms for enumerating all monoid presentations +/// up to a given length. + +func nextSymbol(_ s: inout Symbol, alphabet: Int) -> Bool { + precondition(alphabet > 0) + if s == alphabet - 1 { + s = 0 + return true + } + s += 1 + return false +} + +func nextWord(_ word: inout Word, alphabet: Int) -> Bool { + var carry = true + for i in word.indices.reversed() { + carry = nextSymbol(&word[i], alphabet: alphabet) + if !carry { break } + } + return carry +} + +func nextRule(_ rule: inout Rule, alphabet: Int) -> Bool { + if nextWord(&rule.rhs, alphabet: alphabet) { + rule.rhs = Word(repeating: 0, count: rule.rhs.count) + if nextWord(&rule.lhs, alphabet: alphabet) { + rule.lhs = Word(repeating: 0, count: rule.lhs.count) + return true + } + } + return false +} + +func nextPresentation(_ p: inout Presentation, alphabet: Int) -> Bool { + precondition(!p.rules.isEmpty) + var carry = true + for i in p.rules.indices.reversed() { + carry = nextRule(&p.rules[i], alphabet: alphabet) + if !carry { break } + } + return carry +} + +struct RuleShape { + var lhs: Int + var rhs: Int + + var rule: Rule { + return Rule(lhs: Word(repeating: 0, count: lhs), + rhs: Word(repeating: 0, count: rhs)) + } +} + +struct PresentationShape { + var rules: [RuleShape] + + func presentation(alphabet: Int) -> Presentation { + return Presentation(alphabet: alphabet, + rules: rules.map { $0.rule }) + } +} + +func enumerateAll(alphabet: Int, shapes: [PresentationShape], output: Bool) + -> [Presentation] { + var filteredLHS = 0 + var filteredRHS = 0 + var filteredSymmetry = 0 + var total = 0 + + var instances: [Presentation] = [] + var unique: Set = [] + + let perms = allPermutations(alphabet) + + for shape in shapes { + var p = shape.presentation(alphabet: alphabet) + var done = false + +loop: while !done { + defer { + done = nextPresentation(&p, alphabet: alphabet) + } + + total += 1 + + for n in 0 ..< p.rules.count - 1 { + if compare(p.rules[n].lhs, p.rules[n + 1].lhs, order: .shortlex) != .lessThan { + filteredLHS += 1 + continue loop + } + } + + for rule in p.rules { + precondition(rule.rhs.count <= rule.lhs.count) + if compare(rule.rhs, rule.lhs, order: .shortlex) != .lessThan { + filteredRHS += 1 + continue loop + } + } + + if unique.contains(p) { + filteredSymmetry += 1 + continue + } + + for perm in perms { + let permuted = p.permuted(perm) + unique.insert(permuted.sorted(order: .shortlex)) + } + + instances.append(p) + } + } + + if output { + print("# Total \(total)") + print("# Discarded lhs:\(filteredLHS),rhs:\(filteredRHS)," + + "symmetry:\(filteredSymmetry)") + } + + return instances +} + +func ruleShapes(_ n: Int) -> [RuleShape] { + precondition(n > 0) + var result: [RuleShape] = [] + for i in 0 ..< n { + let j = n - i + + // Don't consider rules with a shorter left-hand side. + if j < i { continue } + + // Don't consider rules of the form x=1 or x=y where x, y are generators. + if j == 1 && i <= 1 { continue } + + result.append(RuleShape(lhs: j, rhs: i)) + } + return result +} + +func presentationShapes(rules: Int, ofLength n: Int) -> [PresentationShape] { + precondition(n > 0) + precondition(rules > 0) + + if rules == 1 { + return ruleShapes(n).map { + PresentationShape(rules: [$0]) + } + } + + var result: [PresentationShape] = [] + for i in 1 ..< n { + let next = presentationShapes(rules: rules - 1, ofLength: i) + for x in ruleShapes(n - i) { + for y in next { + if x.lhs <= y.rules.first!.lhs { + result.append(PresentationShape(rules: [x] + y.rules)) + } + } + } + } + return result +} + +func presentationShapes(rules: Int, upToLength n: Int) -> [PresentationShape] { + var shapes: [PresentationShape] = [] + for i in 1 ... n { + shapes.append(contentsOf: presentationShapes(rules: rules, ofLength: i)) + } + return shapes +} diff --git a/benchmark/multi-source/Monoids/Monoids.swift b/benchmark/multi-source/Monoids/Monoids.swift new file mode 100644 index 0000000000000..dc9d1097e2027 --- /dev/null +++ b/benchmark/multi-source/Monoids/Monoids.swift @@ -0,0 +1,33 @@ +/// This is the main entry point. +/// +/// We enumerate all 2-generator, 2-relation monoids of length up to 10: +/// +/// where |u| + |v| + |w| + |x| <= 10 +/// +/// We attempt to build an FCRS for each one by trying various strategies, +/// which ultimately succeeds for all but three instances. +func run(output: Bool) async { + let shapes = presentationShapes(rules: 2, upToLength: 10) + + let alphabet = 2 + let instances = enumerateAll(alphabet: alphabet, shapes: shapes, output: output) + + var solver = Solver(alphabet: alphabet, instances: instances, output: output) + await solver.solve() + + // These we know we can't solve. + let expectUnsolved: [Presentation] = ["bab=aaa,bbbb=1", "aaaa=1,abbba=b", "aaa=a,abba=bb"] + + // Make sure everything else was solved. + let unsolved = solver.subset.map { instances[$0] } + if unsolved != expectUnsolved { + fatalError("Expected \(expectUnsolved), but got \(unsolved)") + } + + // Also check finite monoid results. + let expectFinite = [1: 1188, 2: 2059, 3: 1233, 4: 1644, 5: 1019, 6: 1630, 7: 686, 8: 884, 9: 493, 10: 615, 11: 191, 12: 317, 13: 79, 14: 134, 15: 62, 16: 158, 17: 16, 18: 60, 19: 2, 20: 52, 21: 38, 22: 12, 24: 31, 25: 5, 26: 11, 27: 42, 28: 10, 30: 52, 32: 5, 34: 8, 36: 17, 39: 4, 42: 4, 44: 8, 48: 12, 50: 12, 52: 4, 55: 4, 56: 8, 60: 18, 64: 14, 81: 6, 84: 22, 96: 1, 100: 4, 105: 2, 120: 7, 129: 2, 147: 6, 160: 2, 165: 2, 192: 2, 195: 2, 320: 2, 324: 2, 339: 4, 605: 2, 1083: 2] + let finite = solver.finite + if finite != expectFinite { + fatalError("Expected \(expectFinite), but got \(finite)") + } +} diff --git a/benchmark/multi-source/Monoids/Presentation.swift b/benchmark/multi-source/Monoids/Presentation.swift new file mode 100644 index 0000000000000..356c8bc78131d --- /dev/null +++ b/benchmark/multi-source/Monoids/Presentation.swift @@ -0,0 +1,369 @@ +/// This file defines the data types we use for words, rules, and +/// monoid presentations. Also, a few other fundamental algorithms +/// for working with permutations and reduction orders. + +typealias Symbol = UInt8 + +let symbols: [Character] = ["a", "b", "c"] + +func printSymbol(_ s: Symbol) -> Character { + return symbols[Int(s)] +} + +func parseSymbol(_ c: Character) -> Symbol { + return Symbol(symbols.firstIndex(of: c)!) +} + +typealias Word = [Symbol] + +func printWord(_ word: Word) -> String { + if word.isEmpty { return "1" } + return String(word.map { printSymbol($0) }) +} + +func parseWord(_ str: String) -> Word { + if str == "" || str == "1" { return [] } + return str.map { parseSymbol($0) } +} + +struct Rule: Hashable { + var lhs: Word + var rhs: Word +} + +extension Rule: ExpressibleByStringLiteral, CustomStringConvertible { + init(_ str: String) { + let pair = str.split(separator: "=") + precondition(pair.count == 2) + self.lhs = parseWord(String(pair[0])) + self.rhs = parseWord(String(pair[1])) + } + + init(stringLiteral: String) { + self.init(stringLiteral) + } + + var description: String { + return "\(printWord(lhs))=\(printWord(rhs))" + } +} + +struct Presentation: Hashable { + var alphabet: Int + var rules: [Rule] + + var longestRule: Int { + var result = 0 + for rule in rules { + result = max(result, rule.lhs.count) + result = max(result, rule.rhs.count) + } + return result + } +} + +func printRules(_ rules: [Rule]) -> String { + return rules.map { $0.description }.joined(separator: ",") +} + +extension Presentation: ExpressibleByStringLiteral, CustomStringConvertible { + init(_ str: String) { + self.alphabet = 2 + self.rules = str.split(separator: ",").map { Rule(String($0)) } + } + + init(stringLiteral: String) { + self.init(stringLiteral) + } + + var description: String { + return printRules(rules) + } +} + +typealias Permutation = [Int] + +func identityPermutation(_ n: Int) -> Permutation { + return Permutation(0 ..< n) +} + +func inversePermutation(_ perm: Permutation) -> Permutation { + var result = Permutation(repeating: 0, count: perm.count) + for (i, j) in perm.enumerated() { + result[j] = i + } + return result +} + +// TAOCP 4A 7.2.1.2 Algorithm L +func nextPermutation(_ perm: inout Permutation) -> Bool { + var j = perm.count - 2 + while j >= 0 && perm[j] >= perm[j + 1] { + j -= 1 + } + if j < 0 { return true } + + var l = perm.count - 1 + while perm[j] >= perm[l] { l -= 1 } + perm.swapAt(l, j) + + l = perm.count - 1 + var k = j + 1 + while k < l { + perm.swapAt(k, l) + k += 1 + l -= 1 + } + + return false +} + +func allPermutations(_ alphabet: Int) -> [Permutation] { + var perm = identityPermutation(alphabet) + var perms: [Permutation] = [] + repeat { + perms.append(perm) + } while !nextPermutation(&perm) + return perms +} + +extension Word { + func permuted(_ perm: Permutation) -> Word { + return map { Symbol(perm[Int($0)]) } + } +} + +extension Rule { + func permuted(_ perm: Permutation) -> Rule { + return Rule(lhs: lhs.permuted(perm), rhs: rhs.permuted(perm)) + } +} + +extension Presentation { + func permuted(_ perm: Permutation) -> Presentation { + return Presentation(alphabet: alphabet, + rules: rules.map { $0.permuted(perm) }) + } +} + +enum CompareResult { + case lessThan + case equal + case greaterThan +} + +enum Order: Hashable { + case shortlex + case permutation(Permutation) + case wreath([Int], Permutation) + + var simplified: Order { + switch self { + case .shortlex: + return self + case .permutation(let perm): + // shortlex with identity permutation avoids some indirection + if perm == identityPermutation(perm.count) { + return .shortlex + } + return self + case .wreath(_, _): + return self + } + } + + func removeGenerator(_ a: Symbol) -> Order { + func updatePermutation(_ perm: Permutation, removing i: Int) -> Permutation { + return Permutation(perm[0 ..< i] + perm[(i + 1)...]).map { + return $0 > perm[i] ? $0 - 1 : $0 + } + } + + switch self { + case .shortlex: + return self + + case .permutation(let perm): + return .permutation(updatePermutation(perm, removing: Int(a))) + + case .wreath(let degrees, let perm): + var newDegrees = Array(degrees[0 ..< Int(a)] + degrees[(Int(a) + 1)...]) + let oldDegree = degrees[Int(a)] + if newDegrees.firstIndex(of: oldDegree) == nil { + newDegrees = newDegrees.map { $0 > oldDegree ? $0 - 1 : $0 } + } + let newPerm = updatePermutation(perm, removing: Int(a)) + if newDegrees.max()! == 0 { return .permutation(newPerm) } + return .wreath(newDegrees, newPerm) + } + } +} + +func shortlex(_ lhs: Word, _ lhsFrom: Int, _ lhsTo: Int, + _ rhs: Word, _ rhsFrom: Int, _ rhsTo: Int, + perm: Permutation) -> CompareResult { + let lhsCount = (lhsTo - lhsFrom) + let rhsCount = (rhsTo - rhsFrom) + if lhsCount != rhsCount { + return lhsCount < rhsCount ? .lessThan : .greaterThan + } + + for i in 0 ..< lhsCount { + let x = lhs[lhsFrom + i] + let y = rhs[rhsFrom + i] + if x != y { + return perm[Int(x)] < perm[Int(y)] ? .lessThan : .greaterThan + } + } + + return .equal +} + +// The "wreath product" or "recursive path" order: +// +// Sims, C. C. (1994). Computation with Finitely Presented Groups. +// Cambridge: Cambridge University Press. +// +func wreath(_ lhs: Word, _ lhsFrom: Int, _ lhsTo: Int, + _ rhs: Word, _ rhsFrom: Int, _ rhsTo: Int, + degrees: [Int], perm: Permutation) -> CompareResult { + var i = lhsFrom + var j = rhsFrom + + while true { + if i == lhsTo { + if j == rhsTo { return .equal } + return .lessThan + } else if j == rhsTo { + return .greaterThan + } + + if lhs[i] != rhs[j] { break } + i += 1 + j += 1 + } + + func maxDegree(_ word: Word, _ from: Int, _ to: Int) + -> (degree: Int, count: Int, symbol: Symbol?) { + var degree = -1, count = 0 + var symbol: Symbol? = nil + + for s in word[from ..< to] { + if degrees[Int(s)] > degree { + degree = degrees[Int(s)] + count = 1 + symbol = s + } else if degrees[Int(s)] == degree { + count += 1 + if symbol != s { symbol = nil } + } + } + + return (degree, count, symbol) + } + + let (lhsMaxDegree, lhsCount, lhsHeadSymbol) = maxDegree(lhs, i, lhsTo) + let (rhsMaxDegree, rhsCount, rhsHeadSymbol) = maxDegree(rhs, j, rhsTo) + if lhsMaxDegree < rhsMaxDegree { + return .lessThan + } else if lhsMaxDegree > rhsMaxDegree { + return .greaterThan + } else if lhsCount < rhsCount { + return .lessThan + } else if lhsCount > rhsCount { + return .greaterThan + } + + if lhsHeadSymbol != nil && rhsHeadSymbol != nil { + if lhsHeadSymbol != rhsHeadSymbol { + return perm[Int(lhsHeadSymbol!)] < perm[Int(rhsHeadSymbol!)] + ? .lessThan : .greaterThan + } + } else { + if lhsMaxDegree == 0 { + return shortlex(lhs, i, lhsTo, rhs, j, rhsTo, perm: perm) + } else { + let lhsHeadWord = lhs[i ..< lhsTo].filter { degrees[Int($0)] == lhsMaxDegree } + let rhsHeadWord = rhs[j ..< rhsTo].filter { degrees[Int($0)] == rhsMaxDegree } + + let result = shortlex(lhsHeadWord, 0, lhsHeadWord.count, + rhsHeadWord, 0, rhsHeadWord.count, + perm: perm) + if result != .equal { return result } + } + } + + if lhsMaxDegree == 0 { return .equal } + + var ii = i, jj = j + while i < lhsTo { + while i < lhsTo && degrees[Int(lhs[i])] != lhsMaxDegree { i += 1 } + while j < rhsTo && degrees[Int(rhs[j])] != rhsMaxDegree { j += 1 } + + let result = wreath(lhs, ii, i, rhs, jj, j, degrees: degrees, perm: perm) + if result != .equal { return result } + + i += 1; ii = i + j += 1; jj = j + } + + precondition(j == rhsTo) + return .equal +} + +func compare(_ lhs: Word, _ rhs: Word, order: Order) -> CompareResult { + switch order { + case .shortlex: + if lhs.count != rhs.count { + return lhs.count < rhs.count ? .lessThan : .greaterThan + } + + for i in lhs.indices { + let x = lhs[i] + let y = rhs[i] + if x != y { + return x < y ? .lessThan : .greaterThan + } + } + + return .equal + + case .permutation(let perm): + return shortlex(lhs, 0, lhs.count, rhs, 0, rhs.count, perm: perm) + + case .wreath(let degrees, let perm): + return wreath(lhs, 0, lhs.count, rhs, 0, rhs.count, + degrees: degrees, perm: perm) + } +} + +func compare(_ lhs: Rule, _ rhs: Rule, order: Order) -> CompareResult { + let result = compare(lhs.lhs, rhs.lhs, order: order) + if result != .equal { + return result + } + + return compare(lhs.rhs, rhs.rhs, order: order) +} + +extension Rule { + func oriented(order: Order) -> Rule? { + switch compare(lhs, rhs, order: order) { + case .equal: + return nil + case .lessThan: + return Rule(lhs: rhs, rhs: lhs) + case .greaterThan: + return self + } + } +} + +extension Presentation { + func sorted(order: Order) -> Presentation { + let sortedRules = + rules.map { $0.oriented(order: order)! } + .sorted { compare($0, $1, order: order) == .lessThan } + return Presentation(alphabet: alphabet, rules: sortedRules) + } +} diff --git a/benchmark/multi-source/Monoids/README.md b/benchmark/multi-source/Monoids/README.md new file mode 100644 index 0000000000000..d33f7eb41e242 --- /dev/null +++ b/benchmark/multi-source/Monoids/README.md @@ -0,0 +1,26 @@ +# Monoids Benchmark + +This benchmark solves the "word problem" in a bunch of monoids simultaneously, using Swift concurrency (or really, just `Task`). It exercises the standard library data structures heavily. You can also run "sh compile.sh" inside the source directory to build a standalone binary separately from the benchmark harness. The standalone binary prints results to standard output. + +More specifically, this program enumerates two-generator two-relation monoid presentations up to length 10, and then applies the Knuth-Bendix algorithm to each one: + + where |u| + |v| + |w| + |x| <= 10 + +This takes a few seconds to finish and solves all but three instances. The three it cannot solve are: + + + + + +In addition to Knuth-Bendix completion, there are some other interesting algorithms here as well: +- Shortlex order with arbitrary permutation of alphabet +- Wreath product order (also known as recursive path order) with arbitrary degree mapping +- Enumerating all words, permutations, monoid presentations +- Inverse of a permutation +- Computing number of irreducible words in complete presentation (= cardinality of presented monoid) with finite state automata + +The Knuth-Bendix implementation is pretty fast. It uses a trie to speed up reduction and finding overlaps. + +The main "entry point" is `func run()` in Monoids.swift. + +A formal write-up is here: https://factorcode.org/slava/aaaaabbabb.pdf diff --git a/benchmark/multi-source/Monoids/RewritingSystem.swift b/benchmark/multi-source/Monoids/RewritingSystem.swift new file mode 100644 index 0000000000000..1d78c9163960d --- /dev/null +++ b/benchmark/multi-source/Monoids/RewritingSystem.swift @@ -0,0 +1,292 @@ +/// This file implements Knuth-Bendix completion and the normal form algorithm. + +enum RewritingError: Error { + case tooManyRounds + case tooManyRules + case tooManyNodes + case ruleTooLong + case tooManySteps + case reducedWordTooLong +} + +let debug = false + +func log(_ str: @autoclosure () -> String) { + if debug { + print(str()) + } +} + +struct RewritingSystem { + var state: State = .initial + + enum State { + case initial + case complete + case failed + } + + var alphabet: Int + var rules: [Rule] = [] + var trie: Trie + + // Limits for completion + struct Limits: Hashable { + var maxRounds = 100 + var maxRules = 200 + var maxLength = 100 + var maxReductionLength = 100 + var maxReductionSteps = 1 << 24 + } + + var limits = Limits() + + var checkedRulesUpTo = 0 // Completion progress + var reducedRules: [UInt32] = [] // Bitmap of reduced rules + + typealias CriticalPair = (i: Int, from: Int, j: Int) + var criticalPairs: [CriticalPair] = [] // Temporary array for completion + + var stats = Stats() + + struct Stats { + var numRounds = 0 + var numRulesRemaining = 0 // Number of rules that were not reduced away + var numReductionSteps = 0 + } + + init(alphabet: Int) { + self.alphabet = alphabet + self.trie = Trie(alphabet: self.alphabet) + + criticalPairs.reserveCapacity(128) + } + + mutating func addRules(_ rules: [Rule], order: Order) + throws(RewritingError) { + for var rule in rules { + _ = try addRule(&rule, order: order) + } + } + + func reduceOne(_ word: Word, excluding: Int? = nil) -> (Int, Int)? { + var from = 0 + + while from < word.count { + if let n = trie.lookup(word, from) { + if n != excluding { return (from, n) } + } + + from += 1 + } + + return nil + } + + func reduce(_ word: inout Word, stats: inout Stats) throws(RewritingError) { + var count = 0 + + repeat { + guard let (from, n) = reduceOne(word) else { return } + + let index = word.startIndex + from + word.replaceSubrange(index ..< index + rules[n].lhs.count, + with: rules[n].rhs) + stats.numReductionSteps += (from + rules[n].lhs.count) + if stats.numReductionSteps > limits.maxReductionSteps { throw .tooManySteps } + + if count > limits.maxReductionLength { throw .tooManySteps } + + // FIXME: Load bearing + if word.count > limits.maxLength { throw .reducedWordTooLong } + + count += 1 + } while true + } + + mutating func addOrientedRule(_ rule: Rule) throws(RewritingError) { + let longestSide = max(rule.lhs.count, rule.rhs.count) + if longestSide > limits.maxLength { throw .ruleTooLong } + + if stats.numRulesRemaining == limits.maxRules { throw .tooManyRules } + + log("Adding rule \(rules.count) = \(rule)") + try trie.insert(rule.lhs, rules.count) + + rules.append(rule) + stats.numRulesRemaining += 1 + } + + mutating func addRule(_ rule: inout Rule, order: Order) + throws(RewritingError) -> Bool { + try reduce(&rule.lhs, stats: &stats) + try reduce(&rule.rhs, stats: &stats) + + switch compare(rule.lhs, rule.rhs, order: order) { + case .equal: + return false + + case .lessThan: + swap(&rule.lhs, &rule.rhs) + fallthrough + + case .greaterThan: + try addOrientedRule(rule) + return true + } + } + + mutating func resolveOverlap(i: Int, from: Int, j: Int, order: Order) + throws(RewritingError) -> Bool { + let lhs = rules[i] + let rhs = rules[j] + + log("Critical pair: \(i) vs \(j) at \(from)") + log("\(printWord(rules[i].lhs))") + log("\(String(repeating: " ", count: from))\(printWord(rules[j].lhs))") + + var rule = Rule(lhs: [], rhs: []) + + let end = lhs.lhs.count + if from + rhs.lhs.count < end { + rule.lhs = lhs.rhs + + rule.rhs.reserveCapacity(lhs.lhs.count - rhs.lhs.count + rhs.rhs.count) + + rule.rhs.append(contentsOf: lhs.lhs[0 ..< from]) + rule.rhs.append(contentsOf: rhs.rhs) + rule.rhs.append(contentsOf: lhs.lhs[(from + rhs.lhs.count)...]) + } else { + rule.lhs.reserveCapacity(lhs.rhs.count + rhs.lhs.count - lhs.lhs.count + from) + rule.lhs.append(contentsOf: lhs.rhs) + rule.lhs.append(contentsOf: rhs.lhs[(lhs.lhs.count - from)...]) + + rule.rhs.reserveCapacity(from + rhs.rhs.count) + rule.rhs.append(contentsOf: lhs.lhs[.. lhs.lhs.count { return } + } + + criticalPairs.append((i: i, from: from, j: j)) + } + + from += 1 + } + } + + mutating func completeOne(order: Order) throws(RewritingError) -> Bool { + precondition(state == .initial) + + precondition(criticalPairs.isEmpty) + + for i in rules.indices { + processRule(i) + } + + checkedRulesUpTo = rules.count + stats.numRounds += 1 + + reduceLeft() + + var confluent = true + + do { + log("Resolving critical pairs...") + for (i, from, j) in criticalPairs { + if try resolveOverlap(i: i, from: from, j: j, order: order) { + confluent = false + } + } + criticalPairs.removeAll(keepingCapacity: true) + log("All critical pairs resolved") + + try reduceRight() + } catch let e { + state = .failed + throw e + } + + if confluent { + state = .complete + return true + } + + if stats.numRounds > limits.maxRounds { + state = .failed + throw .tooManyRounds + } + + return false + } + + mutating func complete(order: Order) throws(RewritingError) { + while try !completeOne(order: order) {} + } + + func isReduced(_ rule: Int) -> Bool { + let i = (rule >> 5) + let j = (rule & 31) + if i >= reducedRules.count { return false } + return (reducedRules[i] & (1 << j)) != 0 + } + + mutating func setReduced(_ rule: Int) { + let i = (rule >> 5) + let j = (rule & 31) + while i >= reducedRules.count { reducedRules.append(0) } + reducedRules[i] |= (1 << j) + } + + mutating func reduceLeft() { + if rules.isEmpty { return } + log("Reducing left-hand sides...") + for (n, rule) in rules.enumerated() { + if !isReduced(n) && reduceOne(rule.lhs, excluding: n) != nil { + log("Reduced \(n) = \(rule)") + setReduced(n) + trie.remove(rule.lhs, n) + stats.numRulesRemaining -= 1 + continue + } + } + + precondition(stats.numRulesRemaining > 0) + } + + mutating func reduceRight() throws(RewritingError) { + for n in rules.indices { + if !isReduced(n) { + try reduce(&rules[n].rhs, stats: &stats) + } + } + } + + /// Returns a complete presentation once the rewriting system is complete. + var presentation: Presentation { + var result: [Rule] = [] + for (n, rule) in rules.enumerated() { + if !isReduced(n) { + result.append(rule) + } + } + return Presentation(alphabet: alphabet, rules: result) + } +} diff --git a/benchmark/multi-source/Monoids/Solver.swift b/benchmark/multi-source/Monoids/Solver.swift new file mode 100644 index 0000000000000..46b54534be866 --- /dev/null +++ b/benchmark/multi-source/Monoids/Solver.swift @@ -0,0 +1,291 @@ +/// This file implements the driver loop which attempts Knuth-Bendix completion +/// with various strategies on all instances in parallel. + +let numTasks = 32 + +struct Dispatcher { + let subset: [Int] + let strategies: [Strategy] + var currentStrategy = 0 + var currentInstance = 0 + + mutating func next() -> (instance: Int, strategy: Int)? { + if subset.isEmpty || currentStrategy == strategies.count { return nil } + + defer { + currentInstance += 1 + if currentInstance == subset.count { + currentInstance = 0 + currentStrategy += 1 + } + } + + return (instance: subset[currentInstance], + strategy: currentStrategy) + } +} + +struct Solver { + let alphabet: Int + let instances: [Presentation] + + // This is a list of indices of all remaining unsolved instances. + var subset: [Int] + + // Histogram of finite monoid cardinality. + var finite: [Int: Int] = [:] + + var factors: [Order: [Int: [Word]]] = [:] + var maxFactors: [Int] = [] + + var output: Bool + + init(alphabet: Int, instances: [Presentation], output: Bool) { + self.alphabet = alphabet + self.instances = instances + self.subset = Array(instances.indices) + self.output = output + } + + mutating func solve() async { + if output { + print("# Remaining \(subset.count)") + print("# n:\tpresentation:\tcardinality:\tcomplete:\tstrategy:") + } + + // The shortlex order with identity permutation of generators solves + // almost everything. + await attempt([Strategy()]) + + if output { + print("# Remaining \(subset.count)") + print("# Attempting more reduction orders") + } + + var orderMix: [Int: [Order]] = [:] + for i in [0, 1] { + orderMix[alphabet + i] = getExhaustiveOrderMix(alphabet, i) + } + + do { + var strategies: [Strategy] = [] + + let strategy = Strategy() + let orders = orderMix[alphabet]! + + // We already did the first one. + for order in orders[1...] { + strategies.append(strategy.withOrder(order)) + } + + await attempt(strategies) + } + + if output { + print("# Remaining \(subset.count)") + print("# Attempting to add a generator") + } + + do { + collectFactors(orderMix[alphabet]!) + + var strategies: [Strategy] = [] + + for factorLength in 2 ..< maxFactors.count { + for frequency in [0, 1] { + let strategy = Strategy(factorLength: factorLength, + frequency: frequency) + for order in orderMix[alphabet + 1]! { + strategies.append(strategy.withOrder(order)) + } + } + } + + await attempt(strategies) + } + + if output { + print("# Remaining \(subset.count)") + + for n in subset { + let instance = instances[n] + print("\(n + 1)\t\(instance)\thard") + } + + print("# Finite monoids: ", terminator: "") + print(finite.sorted { $0.0 < $1.0 } + .map { "\($0): \($1)" } + .joined(separator: ", ")) + } + } + + mutating func collectFactors(_ orders: [Order]) { + for order in orders { + for n in subset { + factors[order, default: [:]][n] = + collectFactors(n, instances[n], order: order.simplified) + } + } + } + + mutating func collectFactors(_ n: Int, _ p: Presentation, order: Order) -> [Word] { + // FIXME: The 6 is a magic number. + let words = p.collectFactors(order: order, upToLength: 6) + if !words.isEmpty { + let longestFactor = words.map { $0.count }.max()! + for i in 2 ... longestFactor { + let factorsOfLength = words.filter { $0.count == i } + while maxFactors.count <= i { + maxFactors.append(0) + } + maxFactors[i] = max(maxFactors[i], factorsOfLength.count) + } + } + return words + } + + func prepare(_ instance: Int, _ strategy: Strategy) -> Strategy? { + var strategy = strategy + + var factorsOfLength: [Word] = [] + if let length = strategy.factorLength { + let order = strategy.order.removeGenerator(Symbol(alphabet)) + factorsOfLength = (factors[order]!)[instance]! + + let factorsOfLength = factorsOfLength.filter { $0.count == length } + if strategy.frequency >= factorsOfLength.count { return nil } + + // Add a new generator 'c' and a rule 'c=x' for a magic factor 'x'. + let newFactor = factorsOfLength[strategy.frequency] + let newGenerator = [Symbol(alphabet)] + + // If 'c' is just going to reduce to 'x' there's no point in + // considering it further. + if compare(newFactor, newGenerator, order: strategy.order) == .lessThan { + return nil + } + + strategy.extra = [Rule(lhs: newFactor, rhs: newGenerator)] + } + + strategy.order = strategy.order.simplified + return strategy + } + + mutating func attempt(_ strategies: [Strategy]) async { + if subset.isEmpty { return } + + await withTaskGroup(of: (Int, Int, Solution?).self) { group in + var dispatcher = Dispatcher(subset: subset, strategies: strategies) + var solved: [Int: (Int, Solution)] = [:] + var pending: [Int: Int] = [:] + + func startTask() { + while true { + guard let (instance, strategyIndex) = dispatcher.next() else { + return + } + + if solved[instance] != nil { + continue + } + + guard let strategy = prepare(instance, strategies[strategyIndex]) else { + continue + } + + pending[strategyIndex, default: 0] += 1 + + let p = instances[instance] + + group.addTask { () -> (Int, Int, Solution?) in + if let solution = try? p.complete(strategy) { + return (instance, strategyIndex, solution) + } + return (instance, strategyIndex, nil) + } + + return + } + } + + func completeTask(_ instance: Int, _ strategyIndex: Int, _ solution: Solution?) { + pending[strategyIndex, default: 0] -= 1 + precondition(pending[strategyIndex]! >= 0) + + if let solution { + // The lowest-numbered strategy is the 'official' solution for the instance. + var betterStrategy = true + if let (oldStrategyIndex, _) = solved[instance] { + precondition(oldStrategyIndex != strategyIndex) + if oldStrategyIndex < strategyIndex { + betterStrategy = false + } + } + + if betterStrategy { + solved[instance] = (strategyIndex, solution) + } + } + + while retireStrategy() {} + } + + func retireStrategy() -> Bool { + // Check if nothing remains. + guard let minPending = pending.keys.min() else { return false } + + // Check if we're going to dispatch more instances with this strategy. + precondition(minPending <= dispatcher.currentStrategy) + if minPending == dispatcher.currentStrategy { return false } + + // Check if we're still waiting for instances to finish with this + // strategy. + if pending[minPending]! > 0 { return false } + + // Otherwise, retire this strategy. + pending[minPending] = nil + + // Print out all instances solved by this strategy and remove them + // from the list. + subset = subset.filter { n in + guard let (strategyIndex, solution) = solved[n] else { return true } + guard strategyIndex == minPending else { return true } + + // Print the instance and solution. + var str = "\(n + 1)\t\(instances[n])\t" + + if let cardinality = solution.cardinality { + str += "finite:\(cardinality)" + finite[cardinality, default: 0] += 1 + } else { + str += "infinite" + } + str += "\tfcrs:\(solution.presentation)" + + // Print the extra generators that were added, if any. + if !solution.extra.isEmpty { + str += "\t\(printRules(solution.extra))" + } + + if output { + print(str) + } + + return false + } + + return true + } + + for _ in 0 ..< numTasks { + startTask() + } + + for await (instance, strategyIndex, solution) in group { + startTask() + completeTask(instance, strategyIndex, solution) + } + } + } +} diff --git a/benchmark/multi-source/Monoids/Standalone.swift b/benchmark/multi-source/Monoids/Standalone.swift new file mode 100644 index 0000000000000..3cfcc2e792903 --- /dev/null +++ b/benchmark/multi-source/Monoids/Standalone.swift @@ -0,0 +1,13 @@ +// Only generate main entry point if we're not being built as part of the +// benchmark harness. In this case you get a binary that runs the same +// workload, except it also prints results to standard output. + +#if !canImport(TestsUtils) + +@main struct Main { + static func main() async { + await run(output: true) + } +} + +#endif \ No newline at end of file diff --git a/benchmark/multi-source/Monoids/Strategy.swift b/benchmark/multi-source/Monoids/Strategy.swift new file mode 100644 index 0000000000000..9318f901c5ab5 --- /dev/null +++ b/benchmark/multi-source/Monoids/Strategy.swift @@ -0,0 +1,104 @@ +/// Generate a bunch of reduction orders. +func getExhaustiveOrderMix(_ alphabet: Int, _ extraAlphabet: Int) -> [Order] { + var result: [Order] = [] + + let permutations = allPermutations(alphabet + extraAlphabet) + + for perm in permutations { + result.append(.permutation(perm)) + } + + for perm in permutations { + let degrees = perm.map { Int($0) } + result.append(.wreath(degrees, perm)) + } + + return result +} + +/// Parameters for completion. +struct Strategy: Sendable { + var extra: [Rule] = [] + var factorLength: Int? = nil + var frequency: Int = 0 + var order: Order = .shortlex + + func withOrder(_ order: Order) -> Self { + var result = self + result.order = order + return result + } +} + +extension Presentation { + func collectFactors(order: Order, upToLength: Int) -> [Word] { + let strategy = Strategy() + var rws = RewritingSystem(alphabet: alphabet) + + do { + try rws.addRules(rules, order: strategy.order) + + for _ in [0, 1] { + if try rws.completeOne(order: strategy.order) { break } + } + } catch {} + + return rws.collectFactors(upToLength: upToLength, order: strategy.order) + } +} + +extension Word { + func collectFactors(_ table: inout [Word: Int], length: Int) { + precondition(length >= 2) + + if length > count { return } + + for i in 0 ... count - length { + table[Word(self[i ..< i + length]), default: 0] += 1 + } + } +} + +extension RewritingSystem { + func collectFactors(upToLength: Int, order: Order) -> [Word] { + var table: [Word: Int] = [:] + for n in rules.indices { + if isReduced(n) { continue } + let lhs = rules[n].lhs + for length in 2 ... upToLength { + lhs.collectFactors(&table, length: length) + } + } + + return table.sorted { + $0.1 > $1.1 || ($0.1 == $1.1 && + compare($0.0, $1.0, order: order) == .greaterThan) + }.map { $0.key } + } +} + +struct Solution { + let extra: [Rule] + let cardinality: Int? + let presentation: Presentation +} + +extension RewritingSystem { + func formSolution(_ strategy: Strategy) -> Solution { + let p = presentation.sorted(order: strategy.order) + return Solution(extra: strategy.extra, + cardinality: cardinality, + presentation: p) + } +} + +extension Presentation { + func complete(_ strategy: Strategy) throws(RewritingError) -> Solution { + var rws = RewritingSystem(alphabet: alphabet + strategy.extra.count) + try rws.addRules(rules, order: strategy.order) + try rws.addRules(strategy.extra, order: strategy.order) + + try rws.complete(order: strategy.order) + return rws.formSolution(strategy) + } +} diff --git a/benchmark/multi-source/Monoids/Trie.swift b/benchmark/multi-source/Monoids/Trie.swift new file mode 100644 index 0000000000000..f88fefca43c5b --- /dev/null +++ b/benchmark/multi-source/Monoids/Trie.swift @@ -0,0 +1,136 @@ +struct Trie { + typealias Node = Int16 + + var values: [Node] = [] + var children: [Node] = [] + var freeList: [Node] = [] + + let emptyNode: [Node] + + init(alphabet: Int) { + self.emptyNode = Array(repeating: -1, count: alphabet) + _ = try! createNode() // The root node + } + + mutating func createNode() throws(RewritingError) -> Node { + if !freeList.isEmpty { + let result = Int(freeList.removeLast()) + values.replaceSubrange(result ..< result + emptyNode.count, with: emptyNode) + children.replaceSubrange(result ..< result + emptyNode.count, with: emptyNode) + return Node(result) + } + + let result = values.count + if result + emptyNode.count >= 32000 { + throw RewritingError.tooManyNodes + } + values.append(contentsOf: emptyNode) + children.append(contentsOf: emptyNode) + precondition(values.count == children.count) + precondition(values.count % emptyNode.count == 0) + return Node(result) + } + + mutating func reclaimNode(_ node: Node) { + freeList.append(node) + } + + mutating func insert(_ key: Word, _ value: Int) throws(RewritingError) { + var node = 0 + for i in 0 ..< key.count - 1 { + let s = key[i] + if children[node + Int(s)] == -1 { + children[node + Int(s)] = try createNode() + } + node = Int(children[node + Int(s)]) + } + values[node + Int(key.last!)] = Node(value) + } + + func lookup(_ key: Word, _ i: Int) -> Int? { + var node = 0 + for s in key[i ..< key.count] { + let n = Int(values[node + Int(s)]) + if n != -1 { return n } + node = Int(children[node + Int(s)]) + if node == -1 { return nil } + } + return nil + } + + // Visits all keys that are equal to a prefix of key[i ...], as well as + // all keys whose prefix is equal to key[i ...]. + func visitOverlaps(_ key: Word, _ from: Int, callback: (Int) -> ()) { + var node = 0 + for s in key[from...] { + let n = Int(values[node + Int(s)]) + if n != -1 { callback(n) } + node = Int(children[node + Int(s)]) + if node == -1 { return } + } + + if node == 0 { return } + + var stack: [Int] = [node] + + repeat { + let node = stack.removeLast() + + for s in (0 ..< emptyNode.count) { + let n = Int(values[node + s]) + if n != -1 { callback(n) } + let child = Int(children[node + s]) + if child != -1 { stack.append(child) } + } + } while !stack.isEmpty + } + + func isEmptyNode(_ node: Int) -> Bool { + for i in 0 ..< emptyNode.count { + if values[node + i] != -1 || + children[node + i] != -1 { + return false + } + } + + return true + } + + mutating func remove(_ key: Word, _ value: Int) { + var node = 0 + var stack: [Int] = [] // path to current node from root + + for i in 0 ..< key.count - 1 { + let s = key[i] + precondition(children[node + Int(s)] != -1) + + stack.append(node) + node = Int(children[node + Int(s)]) + } + + let j = node + Int(key.last!) + precondition(values[j] == value) + values[j] = -1 + + // Remove any newly-empty nodes, up to the root. + repeat { + if !isEmptyNode(node) { return } + + reclaimNode(Node(node)) + + let parent = stack.removeLast() + + var sawThis = false + for i in 0 ..< emptyNode.count { + if Int(children[parent + i]) == node { + children[parent + i] = -1 + sawThis = true + break + } + } + precondition(sawThis) + + node = parent + } while !stack.isEmpty + } +} diff --git a/benchmark/multi-source/Monoids/compile.sh b/benchmark/multi-source/Monoids/compile.sh new file mode 100644 index 0000000000000..3e10c2ed2e8cb --- /dev/null +++ b/benchmark/multi-source/Monoids/compile.sh @@ -0,0 +1 @@ +xcrun swiftc -O Automaton.swift Enumeration.swift Monoids.swift Presentation.swift RewritingSystem.swift Solver.swift Standalone.swift Strategy.swift Trie.swift -O -swift-version 6 -g -wmo -parse-as-library -o Monoids $@ diff --git a/benchmark/scripts/build_script_helper.py b/benchmark/scripts/build_script_helper.py index cfec15145ac1d..82feb524cd630 100755 --- a/benchmark/scripts/build_script_helper.py +++ b/benchmark/scripts/build_script_helper.py @@ -25,8 +25,15 @@ def perform_build(args, swiftbuild_path, config, binary_name, opt_flag): "-Xswiftc", "-align-module-to-page-size", "-Xswiftc", - opt_flag, + opt_flag ] + + if config == "debug": + swiftbuild_args += [ + "-Xswiftc", + "-DDEBUG" + ] + if args.verbose: swiftbuild_args.append("--verbose") subprocess.call(swiftbuild_args) diff --git a/benchmark/scripts/test_Benchmark_Driver.py b/benchmark/scripts/test_Benchmark_Driver.py index 53f0883092ee4..90efd61bb7301 100644 --- a/benchmark/scripts/test_Benchmark_Driver.py +++ b/benchmark/scripts/test_Benchmark_Driver.py @@ -112,7 +112,7 @@ def test_optimization_argument(self): [ "error:", "argument -o/--optimization: invalid choice: 'bogus'", - "(choose from 'O', 'Onone', 'Osize')", + "(choose from ", ], err.getvalue(), ) diff --git a/benchmark/scripts/test_compare_perf_tests.py b/benchmark/scripts/test_compare_perf_tests.py index 72eca5e52b603..db64c6695e672 100644 --- a/benchmark/scripts/test_compare_perf_tests.py +++ b/benchmark/scripts/test_compare_perf_tests.py @@ -860,7 +860,7 @@ def test_format_argument(self): ) self.assertIn( "error: argument --format: invalid choice: 'bogus' " - "(choose from 'markdown', 'git', 'html')", + "(choose from ", err.getvalue(), ) diff --git a/benchmark/single-source/CountAlgo.swift b/benchmark/single-source/CountAlgo.swift index 7361c2b5befb3..7f7ea77d33993 100644 --- a/benchmark/single-source/CountAlgo.swift +++ b/benchmark/single-source/CountAlgo.swift @@ -26,7 +26,7 @@ public let benchmarks = [ @inline(never) public func run_CountAlgoArray(_ N: Int) { for _ in 1...10*N { - CheckResults(numbers.count(where: { $0 & 4095 == 0 }) == 25) + check(numbers.count(where: { $0 & 4095 == 0 }) == 25) } } @@ -34,7 +34,7 @@ public func run_CountAlgoArray(_ N: Int) { public func run_CountAlgoString(_ N: Int) { let vowels = Set("aeiou") for _ in 1...N { - CheckResults(text.count(where: vowels.contains) == 2014) + check(text.count(where: vowels.contains) == 2014) } } diff --git a/benchmark/single-source/Diffing.swift b/benchmark/single-source/Diffing.swift index c84cd92f4ef2a..bc5f3936b5f57 100644 --- a/benchmark/single-source/Diffing.swift +++ b/benchmark/single-source/Diffing.swift @@ -49,6 +49,16 @@ public let benchmarks = [ runFunction: { diff($0, from: unabridgedLorem, to: loremIpsum) }, tags: t, setUpFunction: { blackHole((unabridgedLorem, loremIpsum)) }), + BenchmarkInfo( + name: "Diffing.Large.Similar", + runFunction: { diff($0, from: bigUnabridgedLorem, to: bigLoremIpsum) }, + tags: t + [.skip], + setUpFunction: { blackHole((bigUnabridgedLorem, bigLoremIpsum)) }), + BenchmarkInfo( + name: "Diffing.Large.Disparate", + runFunction: { diff($0, from: bigNumbersAndSymbols, to: bigAlphabets) }, + tags: t + [.skip], + setUpFunction: { blackHole((bigNumbersAndSymbols, bigAlphabets)) }), ] let numbersAndSymbols = Array("0123456789`~!@#$%^&*()+=_-\"'?/<,>.\\{}'") @@ -59,6 +69,10 @@ let typingPangram = Array("The quick brown fox jumps over the lazy dog") let loremIpsum = Array("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") let unabridgedLorem = Array("Lorem ipsum, quia dolor sit amet consectetur adipisci[ng] velit, sed quia non-numquam [do] eius modi tempora inci[di]dunt, ut labore et dolore magnam aliqua.") let loremReversed = Array(loremIpsum.reversed()) +let bigLoremIpsum = Array(repeatElement(loremIpsum, count: 100).joined()) +let bigUnabridgedLorem = Array(repeatElement(unabridgedLorem, count: 100).joined()) +let bigNumbersAndSymbols = Array(repeatElement(numbersAndSymbols, count: 250).joined()) +let bigAlphabets = Array(repeatElement(alphabets, count: 250).joined()) @inline(never) func diff(_ n: Int, from older: [Character], to newer: [Character]) { if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { diff --git a/benchmark/single-source/DiffingMyers.swift b/benchmark/single-source/DiffingMyers.swift index 12f584b75719a..bb7132657e515 100644 --- a/benchmark/single-source/DiffingMyers.swift +++ b/benchmark/single-source/DiffingMyers.swift @@ -187,9 +187,9 @@ fileprivate func myers( * necessary) is significantly less than the worst-case n² memory use of the * descent algorithm. */ - func _withContiguousStorage( - for values: C, - _ body: (UnsafeBufferPointer) throws -> R + func _withContiguousStorage( + for values: Col, + _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R { if let result = try values.withContiguousStorageIfAvailable(body) { return result } let array = ContiguousArray(values) diff --git a/benchmark/single-source/ObjectiveCBridging.swift b/benchmark/single-source/ObjectiveCBridging.swift index c0e9d1a083292..fa9b6d3280cba 100644 --- a/benchmark/single-source/ObjectiveCBridging.swift +++ b/benchmark/single-source/ObjectiveCBridging.swift @@ -97,6 +97,27 @@ public let benchmarks = [ BenchmarkInfo(name: "NSArray.bridged.repeatedBufferAccess", runFunction: run_BridgedNSArrayRepeatedBufferAccess, tags: t, setUpFunction: setup_bridgedArrays), + BenchmarkInfo(name: "NSDictionary.bridged.enumerate", + runFunction: run_BridgedNSDictionaryEnumerate, tags: t, + setUpFunction: setup_bridgedDictionaries), + BenchmarkInfo(name: "NSString.bridged.byteCount.ascii.ascii", + runFunction: run_BridgedNSStringLengthASCII_ASCII, tags: ts, + setUpFunction: setup_bridgedStrings), + BenchmarkInfo(name: "NSString.bridged.byteCount.ascii.utf8", + runFunction: run_BridgedNSStringLengthASCII_UTF8, tags: ts, + setUpFunction: setup_bridgedStrings), + BenchmarkInfo(name: "NSString.bridged.byteCount.ascii.utf16", + runFunction: run_BridgedNSStringLengthASCII_UTF16, tags: ts, + setUpFunction: setup_bridgedStrings), + BenchmarkInfo(name: "NSString.bridged.byteCount.ascii.macroman", + runFunction: run_BridgedNSStringLengthASCII_MacRoman, tags: ts, + setUpFunction: setup_bridgedStrings), + BenchmarkInfo(name: "NSString.bridged.byteCount.utf8.utf8", + runFunction: run_BridgedNSStringLengthUTF8_UTF8, tags: ts, + setUpFunction: setup_bridgedStrings), + BenchmarkInfo(name: "NSString.bridged.byteCount.utf8.utf16", + runFunction: run_BridgedNSStringLengthUTF8_UTF16, tags: ts, + setUpFunction: setup_bridgedStrings), ] #if _runtime(_ObjC) @@ -794,9 +815,12 @@ public func run_UnicodeStringFromCodable(_ n: Int) { #if _runtime(_ObjC) var bridgedArray:NSArray! = nil +var bridgedDictionaryOfNumbersToNumbers:NSDictionary! = nil var bridgedArrayMutableCopy:NSMutableArray! = nil var nsArray:NSArray! = nil var nsArrayMutableCopy:NSMutableArray! = nil +var bridgedASCIIString:NSString! = nil +var bridgedUTF8String:NSString! = nil #endif public func setup_bridgedArrays() { @@ -804,11 +828,29 @@ public func setup_bridgedArrays() { var arr = Array(repeating: NSObject(), count: 100) as [AnyObject] bridgedArray = arr as NSArray bridgedArrayMutableCopy = (bridgedArray.mutableCopy() as! NSMutableArray) + nsArray = NSArray(objects: &arr, count: 100) nsArrayMutableCopy = (nsArray.mutableCopy() as! NSMutableArray) #endif } +public func setup_bridgedDictionaries() { + var numDict = Dictionary() + for i in 0 ..< 100 { + numDict[i] = i + } + bridgedDictionaryOfNumbersToNumbers = numDict as NSDictionary +} + +public func setup_bridgedStrings() { + #if _runtime(_ObjC) + let str = Array(repeating: "The quick brown fox jumps over the lazy dog.", count: 100).joined() + bridgedASCIIString = str as NSString + let str2 = Array(repeating: "The quick brown fox jumps over the lazy dög.", count: 100).joined() + bridgedUTF8String = str2 as NSString + #endif +} + @inline(never) public func run_BridgedNSArrayObjectAtIndex(_ n: Int) { #if _runtime(_ObjC) @@ -820,11 +862,28 @@ public func run_BridgedNSArrayObjectAtIndex(_ n: Int) { #endif } +private func dictionaryApplier( + _ keyPtr: UnsafeRawPointer?, + _ valuePtr :UnsafeRawPointer?, + _ contextPtr: UnsafeMutableRawPointer? +) -> Void {} + +@inline(never) +public func run_BridgedNSDictionaryEnumerate(_ n: Int) { + #if _runtime(_ObjC) + let cf = bridgedDictionaryOfNumbersToNumbers as CFDictionary + for _ in 0 ..< n * 50 { + // Use CF to prevent Swift from providing an override, forcing going through ObjC bridging + CFDictionaryApplyFunction(cf, dictionaryApplier, nil) + } + #endif +} + @inline(never) public func run_BridgedNSArrayBufferAccess(_ n: Int) { #if _runtime(_ObjC) for _ in 0 ..< n { - for i in 0..<1000 { + for _ in 0..<1000 { let tmp = nsArray as! [NSObject] blackHole(tmp) blackHole(tmp.withContiguousStorageIfAvailable { @@ -841,7 +900,7 @@ public func run_BridgedNSArrayRepeatedBufferAccess(_ n: Int) { for _ in 0 ..< n { let tmp = nsArray as! [NSObject] blackHole(tmp) - for i in 0..<1000 { + for _ in 0..<1000 { blackHole(tmp.withContiguousStorageIfAvailable { $0[0] }) @@ -883,3 +942,40 @@ public func run_RealNSArrayMutableCopyObjectAtIndex(_ n: Int) { #endif } +@inline(__always) +fileprivate func run_BridgedNSStringLength(_ asciiBase: Bool, _ enc: UInt, _ n: Int) { + let str = asciiBase ? bridgedASCIIString : bridgedUTF8String + for _ in 0 ..< n * 100 { + blackHole(str!.lengthOfBytes(using: enc)) + } +} + +@inline(never) +public func run_BridgedNSStringLengthASCII_ASCII(_ n: Int) { + run_BridgedNSStringLength(true, 1 /* NSASCIIStringEncoding */, n) +} + +@inline(never) +public func run_BridgedNSStringLengthASCII_UTF8(_ n: Int) { + run_BridgedNSStringLength(true, 4 /* NSUTF8StringEncoding */, n) +} + +@inline(never) +public func run_BridgedNSStringLengthASCII_UTF16(_ n: Int) { + run_BridgedNSStringLength(true, 10 /* NSUnicodeStringEncoding */, n) +} + +@inline(never) +public func run_BridgedNSStringLengthASCII_MacRoman(_ n: Int) { + run_BridgedNSStringLength(true, 30 /* NSMacOSRomanStringEncoding */, n) +} + +@inline(never) +public func run_BridgedNSStringLengthUTF8_UTF8(_ n: Int) { + run_BridgedNSStringLength(false, 4 /* NSUTF8StringEncoding */, n) +} + +@inline(never) +public func run_BridgedNSStringLengthUTF8_UTF16(_ n: Int) { + run_BridgedNSStringLength(false, 10 /* NSUnicodeStringEncoding */, n) +} diff --git a/benchmark/utils/DriverUtils.swift b/benchmark/utils/DriverUtils.swift index 733c022b60d83..5785a10dc8174 100644 --- a/benchmark/utils/DriverUtils.swift +++ b/benchmark/utils/DriverUtils.swift @@ -23,6 +23,12 @@ import LibProc import TestsUtils +/// Sorry. +private func ??(_ x: T?, _ y: @autoclosure () async -> T) async -> T { + if let x { return x } + return await y() +} + struct MeasurementMetadata { // Note: maxRSS and pages subtract the RSS measured // after the benchmark driver setup has finished. @@ -198,10 +204,16 @@ struct TestConfig { action = c.action ?? .run allowNondeterministicHashing = c.allowNondeterministicHashing ?? false jsonOutput = c.jsonOutput ?? false + + var skipTags: Set + skipTags = c.tags ?? [.unstable, .skip] +#if DEBUG + skipTags.insert(.long) +#endif tests = TestConfig.filterTests(registeredBenchmarks, tests: c.tests ?? [], tags: c.tags ?? [], - skipTags: c.skipTags ?? [.unstable, .skip]) + skipTags: skipTags) if tests.count > 0 { testNameLength = tests.map{$0.info.name.count}.sorted().reversed().first! @@ -481,13 +493,13 @@ final class TestRunner { } /// Measure the `fn` and return the average sample time per iteration (μs). - func measure(_ name: String, fn: (Int) -> Void, numIters: Int) -> Double { + func measure(_ name: String, fn: (Int) async -> Void, numIters: Int) async -> Double { #if SWIFT_RUNTIME_ENABLE_LEAK_CHECKER name.withCString { p in startTrackingObjects(p) } #endif startMeasurement() - fn(numIters) + await fn(numIters) stopMeasurement() #if SWIFT_RUNTIME_ENABLE_LEAK_CHECKER @@ -502,7 +514,7 @@ final class TestRunner { } /// Run the benchmark and return the measured results. - func run(_ test: BenchmarkInfo) -> BenchResults? { + func run(_ test: BenchmarkInfo) async -> BenchResults? { // Before we do anything, check that we actually have a function to // run. If we don't it is because the benchmark is not supported on // the platform and we should skip it. @@ -528,8 +540,8 @@ final class TestRunner { } // Determine number of iterations for testFn to run for desired time. - func iterationsPerSampleTime() -> (numIters: Int, oneIter: Double) { - let oneIter = measure(test.name, fn: testFn, numIters: 1) + func iterationsPerSampleTime() async -> (numIters: Int, oneIter: Double) { + let oneIter = await measure(test.name, fn: testFn, numIters: 1) if oneIter > 0 { let timePerSample = c.sampleTime * 1_000_000.0 // microseconds (μs) return (max(Int(timePerSample / oneIter), 1), oneIter) @@ -540,8 +552,8 @@ final class TestRunner { // Determine the scale of measurements. Re-use the calibration result if // it is just one measurement. - func calibrateMeasurements() -> Int { - let (numIters, oneIter) = iterationsPerSampleTime() + func calibrateMeasurements() async -> Int { + let (numIters, oneIter) = await iterationsPerSampleTime() if numIters == 1 { addSample(oneIter) } else { resetMeasurements() } // for accurate yielding reports return numIters @@ -549,19 +561,19 @@ final class TestRunner { let numIters = min( // Cap to prevent overflow on 32-bit systems when scaled Int.max / 10_000, // by the inner loop multiplier inside the `testFn`. - c.numIters ?? calibrateMeasurements()) + await c.numIters ?? (await calibrateMeasurements())) - let numSamples = c.numSamples ?? + let numSamples = await c.numSamples ?? // Compute the number of samples to measure for `sample-time`, // clamped in (`min-samples`, 200) range, if the `num-iters` are fixed. - max(c.minSamples ?? 1, min(200, c.numIters == nil ? 1 : - calibrateMeasurements())) + (max(await c.minSamples ?? 1, min(200, c.numIters == nil ? 1 : + await calibrateMeasurements()))) samples.reserveCapacity(numSamples) logVerbose(" Collecting \(numSamples) samples.") logVerbose(" Measuring with scale \(numIters).") for _ in samples.count.. () + private var _runFunction: (Int) async -> () /// A function that invokes the specific benchmark routine. - public var runFunction: ((Int) -> ())? { + public var runFunction: ((Int) async -> ())? { if !shouldRun { return nil } @@ -171,7 +174,7 @@ public struct BenchmarkInfo { /// to be interrupted by a context switch. public var legacyFactor: Int? - public init(name: String, runFunction: @escaping (Int) -> (), tags: [BenchmarkCategory], + public init(name: String, runFunction: @escaping (Int) async -> (), tags: [BenchmarkCategory], setUpFunction: (() -> ())? = nil, tearDownFunction: (() -> ())? = nil, unsupportedPlatforms: BenchmarkPlatformSet = [], diff --git a/benchmark/utils/main.swift b/benchmark/utils/main.swift index 869a782053138..f19e3ea8951d8 100644 --- a/benchmark/utils/main.swift +++ b/benchmark/utils/main.swift @@ -117,6 +117,7 @@ import LuhnAlgoLazy import MapReduce import Memset import MirrorTest +import Monoids import MonteCarloE import MonteCarloPi import NaiveRangeReplaceableCollectionConformance @@ -317,6 +318,7 @@ register(LuhnAlgoLazy.benchmarks) register(MapReduce.benchmarks) register(Memset.benchmarks) register(MirrorTest.benchmarks) +register(Monoids.benchmarks) register(MonteCarloE.benchmarks) register(MonteCarloPi.benchmarks) register(NaiveRangeReplaceableCollectionConformance.benchmarks) @@ -419,4 +421,4 @@ register(Walsh.benchmarks) register(WordCount.benchmarks) register(XorLoop.benchmarks) -main() +await main() diff --git a/cmake/SwiftVersion.cmake b/cmake/SwiftVersion.cmake index 95bea344619fa..1408188dcbe40 100644 --- a/cmake/SwiftVersion.cmake +++ b/cmake/SwiftVersion.cmake @@ -2,6 +2,6 @@ # can be reused when a new version of Swift comes out (assuming the user hasn't # manually set it as part of their own CMake configuration). set(SWIFT_VERSION_MAJOR 6) -set(SWIFT_VERSION_MINOR 2) +set(SWIFT_VERSION_MINOR 3) set(SWIFT_VERSION "${SWIFT_VERSION_MAJOR}.${SWIFT_VERSION_MINOR}") diff --git a/cmake/caches/Windows-aarch64.cmake b/cmake/caches/Windows-aarch64.cmake index 1eb8b6e432646..22c36750d3830 100644 --- a/cmake/caches/Windows-aarch64.cmake +++ b/cmake/caches/Windows-aarch64.cmake @@ -13,10 +13,6 @@ set(LLVM_ENABLE_RUNTIMES compiler-rt CACHE STRING "") -# NOTE(compnerd) always enable assertions, the toolchain will not provide enough -# context to resolve issues otherwise and may silently generate invalid output. -set(LLVM_ENABLE_ASSERTIONS YES CACHE BOOL "") - set(ENABLE_X86_RELAX_RELOCATIONS YES CACHE BOOL "") # NOTE(compnerd) we can hardcode the default target triple since the cache files diff --git a/cmake/caches/Windows-x86_64.cmake b/cmake/caches/Windows-x86_64.cmake index 0074813978d94..84903f1499740 100644 --- a/cmake/caches/Windows-x86_64.cmake +++ b/cmake/caches/Windows-x86_64.cmake @@ -13,10 +13,6 @@ set(LLVM_ENABLE_RUNTIMES compiler-rt CACHE STRING "") -# NOTE(compnerd) always enable assertions, the toolchain will not provide enough -# context to resolve issues otherwise and may silently generate invalid output. -set(LLVM_ENABLE_ASSERTIONS YES CACHE BOOL "") - set(ENABLE_X86_RELAX_RELOCATIONS YES CACHE BOOL "") # NOTE(compnerd) we can hardcode the default target triple since the cache files diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 9dc517f62b6d7..ed28ab1891dba 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -324,6 +324,9 @@ function(_add_host_variant_c_compile_flags target) target_compile_definitions(${target} PRIVATE $<$:_LARGEFILE_SOURCE _FILE_OFFSET_BITS=64>) endif() + + target_compile_definitions(${target} PRIVATE + $<$,$>:SWIFT_ENABLE_SWIFT_IN_SWIFT>) endfunction() function(_add_host_variant_link_flags target) @@ -547,6 +550,19 @@ function(_add_swift_runtime_link_flags target relpath_to_lib_dir bootstrapping) else() get_filename_component(swift_bin_dir ${SWIFT_EXEC_FOR_SWIFT_MODULES} DIRECTORY) get_filename_component(swift_dir ${swift_bin_dir} DIRECTORY) + + # Detect and handle swiftly-managed hosts. + if(swift_bin_dir MATCHES ".*/swiftly/bin") + execute_process(COMMAND swiftly use --print-location + OUTPUT_VARIABLE swiftly_dir + ERROR_VARIABLE err) + if(err) + message(SEND_ERROR "Failed to find swiftly Swift compiler") + endif() + string(STRIP "${swiftly_dir}" swiftly_dir) + set(swift_dir "${swiftly_dir}/usr") + endif() + endif() set(host_lib_dir "${swift_dir}/lib/swift/${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}") else() @@ -608,6 +624,19 @@ function(_add_swift_runtime_link_flags target relpath_to_lib_dir bootstrapping) else() get_filename_component(swift_bin_dir ${SWIFT_EXEC_FOR_SWIFT_MODULES} DIRECTORY) get_filename_component(swift_dir ${swift_bin_dir} DIRECTORY) + + # Detect and handle swiftly-managed hosts. + if(swift_bin_dir MATCHES ".*/swiftly/bin") + execute_process(COMMAND swiftly use --print-location + OUTPUT_VARIABLE swiftly_dir + ERROR_VARIABLE err) + if(err) + message(SEND_ERROR "Failed to find swiftly Swift compiler") + endif() + string(STRIP "${swiftly_dir}" swiftly_dir) + set(swift_dir "${swiftly_dir}/usr") + endif() + set(host_lib_dir "${swift_dir}/lib/swift/${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}") target_link_directories(${target} PRIVATE ${host_lib_dir}) @@ -965,6 +994,11 @@ function(add_swift_host_tool executable) endif() endif() + # Opt-out of OpenBSD BTCFI if instructed where it is enforced by default. + if(SWIFT_HOST_VARIANT_SDK STREQUAL "OPENBSD" AND SWIFT_HOST_VARIANT_ARCH STREQUAL "aarch64" AND NOT SWIFT_OPENBSD_BTCFI) + target_link_options(${executable} PRIVATE "LINKER:-z,nobtcfi") + endif() + if(SWIFT_BUILD_SWIFT_SYNTAX) set(extra_relative_rpath "") if(NOT "${ASHT_BOOTSTRAPPING}" STREQUAL "") diff --git a/cmake/modules/DarwinSDKs.cmake b/cmake/modules/DarwinSDKs.cmake index 20d555b74751c..5d4d1ea59e800 100644 --- a/cmake/modules/DarwinSDKs.cmake +++ b/cmake/modules/DarwinSDKs.cmake @@ -12,7 +12,8 @@ is_sdk_requested(OSX swift_build_osx) if(swift_build_osx) configure_sdk_darwin( OSX "OS X" "${SWIFT_DARWIN_DEPLOYMENT_VERSION_OSX}" - macosx macosx macos "${SUPPORTED_OSX_ARCHS}") + macosx macosx macos macOS "${SUPPORTED_OSX_ARCHS}" + "${SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_OSX}") configure_target_variant(OSX-DA "OS X Debug+Asserts" OSX DA "Debug+Asserts") configure_target_variant(OSX-RA "OS X Release+Asserts" OSX RA "Release+Asserts") configure_target_variant(OSX-R "OS X Release" OSX R "Release") @@ -22,27 +23,29 @@ is_sdk_requested(FREESTANDING swift_build_freestanding) if(swift_build_freestanding AND (SWIFT_FREESTANDING_FLAVOR STREQUAL "apple")) set(SWIFT_FREESTANDING_SDK "" CACHE STRING "Which SDK to use when building the FREESTANDING stdlib") + set(SWIFT_FREESTANDING_DEPLOYMENT_VERSION "" CACHE STRING + "The deployment version to use when building the FREESTANDING stdlib") set(SWIFT_FREESTANDING_TRIPLE_NAME "" CACHE STRING "Which triple name (e.g. 'none-macho') to use when building the FREESTANDING stdlib") set(SWIFT_FREESTANDING_MODULE_NAME "" CACHE STRING "Which .swiftmodule name (e.g. 'freestanding') to use when building the FREESTANDING stdlib") + set(SWIFT_FREESTANDING_AVAILABILITY_NAME "" CACHE STRING + "Which @availability name (e.g. 'macOS') to use when building the FREESTANDING stdlib") set(SWIFT_FREESTANDING_ARCHS "" CACHE STRING "Which architectures to build when building the FREESTANDING stdlib") configure_sdk_darwin( - FREESTANDING "FREESTANDING" "" + FREESTANDING "FREESTANDING" "${SWIFT_FREESTANDING_DEPLOYMENT_VERSION}" "${SWIFT_FREESTANDING_SDK}" - "${SWIFT_FREESTANDING_TRIPLE_NAME}" "${SWIFT_FREESTANDING_MODULE_NAME}" "${SWIFT_FREESTANDING_ARCHS}") + "${SWIFT_FREESTANDING_TRIPLE_NAME}" "${SWIFT_FREESTANDING_MODULE_NAME}" + "${SWIFT_FREESTANDING_AVAILABILITY_NAME}" "${SWIFT_FREESTANDING_ARCHS}" + "${SWIFT_FREESTANDING_DEPLOYMENT_VERSION}") set(SWIFT_SDK_FREESTANDING_LIB_SUBDIR "freestanding") configure_target_variant(FREESTANDING-DA "FREESTANDING Debug+Asserts" FREESTANDING DA "Debug+Asserts") configure_target_variant(FREESTANDING-RA "FREESTANDING Release+Asserts" FREESTANDING RA "Release+Asserts") configure_target_variant(FREESTANDING-R "FREESTANDING Release" FREESTANDING R "Release") configure_target_variant(FREESTANDING-S "FREESTANDING MinSizeRelease" FREESTANDING S "MinSizeRelease") - if(SWIFT_BUILD_SDK_OVERLAY) - set(SWIFT_FREESTANDING_TEST_DEPENDENCIES "Darwin") - else() - set(SWIFT_FREESTANDING_TEST_DEPENDENCIES "") - endif() + set(SWIFT_FREESTANDING_TEST_DEPENDENCIES "") endif() # Compatible cross-compile SDKS for Darwin OSes: IOS, IOS_SIMULATOR, TVOS, @@ -53,7 +56,8 @@ is_sdk_requested(IOS swift_build_ios) if(swift_build_ios) configure_sdk_darwin( IOS "iOS" "${SWIFT_DARWIN_DEPLOYMENT_VERSION_IOS}" - iphoneos ios ios "${SUPPORTED_IOS_ARCHS}") + iphoneos ios ios iOS "${SUPPORTED_IOS_ARCHS}" + "${SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_IOS}") configure_target_variant(IOS-DA "iOS Debug+Asserts" IOS DA "Debug+Asserts") configure_target_variant(IOS-RA "iOS Release+Asserts" IOS RA "Release+Asserts") configure_target_variant(IOS-R "iOS Release" IOS R "Release") @@ -63,8 +67,9 @@ is_sdk_requested(IOS_SIMULATOR swift_build_ios_simulator) if(swift_build_ios_simulator) configure_sdk_darwin( IOS_SIMULATOR "iOS Simulator" "${SWIFT_DARWIN_DEPLOYMENT_VERSION_IOS}" - iphonesimulator ios ios-simulator - "${SUPPORTED_IOS_SIMULATOR_ARCHS}") + iphonesimulator ios ios-simulator iOS + "${SUPPORTED_IOS_SIMULATOR_ARCHS}" + "${SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_IOS}") configure_target_variant( IOS_SIMULATOR-DA "iOS Debug+Asserts" IOS_SIMULATOR DA "Debug+Asserts") configure_target_variant( @@ -77,7 +82,8 @@ is_sdk_requested(TVOS swift_build_tvos) if(swift_build_tvos) configure_sdk_darwin( TVOS "tvOS" "${SWIFT_DARWIN_DEPLOYMENT_VERSION_TVOS}" - appletvos tvos tvos "${SUPPORTED_TVOS_ARCHS}") + appletvos tvos tvos tvOS "${SUPPORTED_TVOS_ARCHS}" + "${SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_TVOS}") configure_target_variant(TVOS-DA "tvOS Debug+Asserts" TVOS DA "Debug+Asserts") configure_target_variant(TVOS-RA "tvOS Release+Asserts" TVOS RA "Release+Asserts") configure_target_variant(TVOS-R "tvOS Release" TVOS R "Release") @@ -87,8 +93,9 @@ is_sdk_requested(TVOS_SIMULATOR swift_build_tvos_simulator) if(swift_build_tvos_simulator) configure_sdk_darwin( TVOS_SIMULATOR "tvOS Simulator" "${SWIFT_DARWIN_DEPLOYMENT_VERSION_TVOS}" - appletvsimulator tvos tvos-simulator - "${SUPPORTED_TVOS_SIMULATOR_ARCHS}") + appletvsimulator tvos tvos-simulator tvOS + "${SUPPORTED_TVOS_SIMULATOR_ARCHS}" + "${SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_TVOS}") configure_target_variant( TVOS_SIMULATOR-DA "tvOS Debug+Asserts" TVOS_SIMULATOR DA "Debug+Asserts") configure_target_variant( @@ -101,7 +108,8 @@ is_sdk_requested(WATCHOS swift_build_watchos) if(swift_build_watchos) configure_sdk_darwin( WATCHOS "watchOS" "${SWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS}" - watchos watchos watchos "${SUPPORTED_WATCHOS_ARCHS}") + watchos watchos watchos watchOS "${SUPPORTED_WATCHOS_ARCHS}" + "${SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_WATCHOS}") configure_target_variant(WATCHOS-DA "watchOS Debug+Asserts" WATCHOS DA "Debug+Asserts") configure_target_variant(WATCHOS-RA "watchOS Release+Asserts" WATCHOS RA "Release+Asserts") configure_target_variant(WATCHOS-R "watchOS Release" WATCHOS R "Release") @@ -111,8 +119,9 @@ is_sdk_requested(WATCHOS_SIMULATOR swift_build_watchos_simulator) if(swift_build_watchos_simulator) configure_sdk_darwin( WATCHOS_SIMULATOR "watchOS Simulator" "${SWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS}" - watchsimulator watchos watchos-simulator - "${SUPPORTED_WATCHOS_SIMULATOR_ARCHS}") + watchsimulator watchos watchos-simulator watchOS + "${SUPPORTED_WATCHOS_SIMULATOR_ARCHS}" + "${SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_WATCHOS}") configure_target_variant(WATCHOS_SIMULATOR-DA "watchOS Debug+Asserts" WATCHOS_SIMULATOR DA "Debug+Asserts") configure_target_variant(WATCHOS_SIMULATOR-RA "watchOS Release+Asserts" WATCHOS_SIMULATOR RA "Release+Asserts") configure_target_variant(WATCHOS_SIMULATOR-R "watchOS Release" WATCHOS_SIMULATOR R "Release") @@ -122,7 +131,8 @@ is_sdk_requested(XROS swift_build_xros) if(swift_build_xros) configure_sdk_darwin( XROS "xrOS" "${SWIFT_DARWIN_DEPLOYMENT_VERSION_XROS}" - xros xros xros "${SUPPORTED_XROS_ARCHS}") + xros xros xros visionOS "${SUPPORTED_XROS_ARCHS}" + "${SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_XROS}") configure_target_variant(XROS-DA "xrOS Debug+Asserts" XROS DA "Debug+Asserts") configure_target_variant(XROS-RA "xrOS Release+Asserts" XROS RA "Release+Asserts") configure_target_variant(XROS-R "xrOS Release" XROS R "Release") @@ -132,8 +142,9 @@ is_sdk_requested(XROS_SIMULATOR swift_build_xros_simulator) if(swift_build_xros_simulator) configure_sdk_darwin( XROS_SIMULATOR "xrOS Simulator" "${SWIFT_DARWIN_DEPLOYMENT_VERSION_XROS}" - xrsimulator xros xros-simulator - "${SUPPORTED_XROS_SIMULATOR_ARCHS}") + xrsimulator xros xros-simulator visionOS + "${SUPPORTED_XROS_SIMULATOR_ARCHS}" + "${SWIFT_DARWIN_TEST_DEPLOYMENT_VERSION_XROS}") configure_target_variant( XROS_SIMULATOR-DA "xrOS Simulator Debug+Asserts" XROS_SIMULATOR DA "Debug+Asserts") diff --git a/cmake/modules/SwiftCXXUtils.cmake b/cmake/modules/SwiftCXXUtils.cmake index bdc289985fc39..ea28fafd3de35 100644 --- a/cmake/modules/SwiftCXXUtils.cmake +++ b/cmake/modules/SwiftCXXUtils.cmake @@ -1,6 +1,5 @@ # Platforms that use libstdc++ as the system-wide default C++ standard library. set(SWIFT_LIBSTDCXX_PLATFORMS "LINUX" - "FREEBSD" "CYGWIN" "HAIKU") diff --git a/cmake/modules/SwiftConfigureSDK.cmake b/cmake/modules/SwiftConfigureSDK.cmake index 53036dafcf192..7bed56fb6ca66 100644 --- a/cmake/modules/SwiftConfigureSDK.cmake +++ b/cmake/modules/SwiftConfigureSDK.cmake @@ -32,6 +32,7 @@ function(_report_sdk prefix) message(STATUS " Version: ${SWIFT_SDK_${prefix}_VERSION}") message(STATUS " Build number: ${SWIFT_SDK_${prefix}_BUILD_NUMBER}") message(STATUS " Deployment version: ${SWIFT_SDK_${prefix}_DEPLOYMENT_VERSION}") + message(STATUS " Deployment version for tests: ${SWIFT_SDK_${prefix}_TEST_DEPLOYMENT_VERSION}") message(STATUS " Triple name: ${SWIFT_SDK_${prefix}_TRIPLE_NAME}") message(STATUS " Simulator: ${SWIFT_SDK_${prefix}_IS_SIMULATOR}") endif() @@ -53,7 +54,7 @@ function(_report_sdk prefix) endforeach() elseif("${prefix}" STREQUAL "ANDROID") if(NOT "${SWIFT_ANDROID_NDK_PATH}" STREQUAL "") - message(STATUS " NDK: $ENV{SWIFT_ANDROID_NDK_PATH}") + message(STATUS " NDK: ${SWIFT_ANDROID_NDK_PATH}") endif() if(NOT "${SWIFT_ANDROID_NATIVE_SYSROOT}" STREQUAL "") message(STATUS " Sysroot: ${SWIFT_ANDROID_NATIVE_SYSROOT}") @@ -136,7 +137,9 @@ endfunction() # deployment_version # Deployment version # xcrun_name # SDK name to use with xcrun # triple_name # The name used in Swift's -triple +# availability_name # The name used in Swift's @availability # architectures # A list of architectures this SDK supports +# test_deployment_version # Deployment versions to be used for tests # ) # # Sadly there are three OS naming conventions. @@ -165,9 +168,12 @@ endfunction() # SWIFT_SDK_${prefix}_ARCH_${ARCH}_TRIPLE Triple name # SWIFT_SDK_${prefix}_ARCH_${ARCH}_MODULE Module triple name for this SDK # SWIFT_SDK_${prefix}_USE_BUILD_ID Whether to pass --build-id to the linker +# SWIFT_SDK_${prefix}_AVAILABILITY_NAME Name to use in @availability +# macro(configure_sdk_darwin prefix name deployment_version xcrun_name - triple_name module_name architectures) + triple_name module_name availability_name architectures + test_deployment_version) # Note: this has to be implemented as a macro because it sets global # variables. @@ -199,8 +205,14 @@ macro(configure_sdk_darwin # Set other variables. set(SWIFT_SDK_${prefix}_NAME "${name}") set(SWIFT_SDK_${prefix}_DEPLOYMENT_VERSION "${deployment_version}") + if(NOT "${test_deployment_version}" STREQUAL "") + set(SWIFT_SDK_${prefix}_TEST_DEPLOYMENT_VERSION "${test_deployment_version}") + else() + set(SWIFT_SDK_${prefix}_TEST_DEPLOYMENT_VERSION "${deployment_version}") + endif() set(SWIFT_SDK_${prefix}_LIB_SUBDIR "${xcrun_name}") set(SWIFT_SDK_${prefix}_TRIPLE_NAME "${triple_name}") + set(SWIFT_SDK_${prefix}_AVAILABILITY_NAME "${availability_name}") set(SWIFT_SDK_${prefix}_OBJECT_FORMAT "MACHO") set(SWIFT_SDK_${prefix}_USE_ISYSROOT TRUE) set(SWIFT_SDK_${prefix}_SHARED_LIBRARY_PREFIX "lib") @@ -417,7 +429,7 @@ macro(configure_sdk_unix name architectures) message(FATAL_ERROR "unknown arch for ${prefix}: ${arch}") endif() elseif("${prefix}" STREQUAL "FREEBSD") - if(NOT arch MATCHES "(arm64|x86_64)") + if(NOT arch MATCHES "(aarch64|x86_64)") message(FATAL_ERROR "unsupported arch for FreeBSD: ${arch}") endif() @@ -428,7 +440,8 @@ macro(configure_sdk_unix name architectures) string(REGEX REPLACE "[-].*" "" freebsd_system_version ${CMAKE_SYSTEM_VERSION}) message(STATUS "FreeBSD Version: ${freebsd_system_version}") - set(SWIFT_SDK_FREEBSD_ARCH_${arch}_TRIPLE "${arch}-unknown-freebsd") + set(SWIFT_SDK_FREEBSD_ARCH_${arch}_TRIPLE "${arch}-unknown-freebsd${freebsd_system_version}") + set(SWIFT_SDK_FREEBSD_ARCH_${arch}_MODULE "${arch}-unknown-freebsd") elseif("${prefix}" STREQUAL "OPENBSD") if(NOT arch STREQUAL "x86_64" AND NOT arch STREQUAL "aarch64") message(FATAL_ERROR "unsupported arch for OpenBSD: ${arch}") @@ -439,6 +452,8 @@ macro(configure_sdk_unix name architectures) set(SWIFT_SDK_OPENBSD_ARCH_${arch}_TRIPLE "${arch}-unknown-openbsd${openbsd_system_version}") + add_link_options("LINKER:-z,origin") + if(CMAKE_SYSROOT) set(SWIFT_SDK_OPENBSD_ARCH_${arch}_PATH "${CMAKE_SYSROOT}${SWIFT_SDK_OPENBSD_ARCH_${arch}_PATH}" CACHE INTERNAL "sysroot path" FORCE) endif() @@ -460,7 +475,7 @@ macro(configure_sdk_unix name architectures) if(SWIFT_ENABLE_WASI_THREADS) set(SWIFT_SDK_WASI_ARCH_wasm32_TRIPLE "wasm32-unknown-wasip1-threads") else() - set(SWIFT_SDK_WASI_ARCH_wasm32_TRIPLE "wasm32-unknown-wasi") + set(SWIFT_SDK_WASI_ARCH_wasm32_TRIPLE "wasm32-unknown-wasip1") endif() elseif("${prefix}" STREQUAL "EMSCRIPTEN") set(SWIFT_SDK_EMSCRIPTEN_ARCH_${arch}_TRIPLE "${arch}-unknown-emscripten") diff --git a/cmake/modules/SwiftSetIfArchBitness.cmake b/cmake/modules/SwiftSetIfArchBitness.cmake index cbf16c7e754d3..8268b33f4192d 100644 --- a/cmake/modules/SwiftSetIfArchBitness.cmake +++ b/cmake/modules/SwiftSetIfArchBitness.cmake @@ -27,15 +27,15 @@ function(set_if_arch_bitness var_name) "${SIA_ARCH}" STREQUAL "powerpc") set("${var_name}" "${SIA_CASE_32_BIT}" PARENT_SCOPE) elseif("${SIA_ARCH}" STREQUAL "x86_64" OR - "${SIA_ARCH}" STREQUAL "amd64" OR - "${SIA_ARCH}" STREQUAL "arm64" OR - "${SIA_ARCH}" STREQUAL "arm64e" OR - "${SIA_ARCH}" STREQUAL "aarch64" OR - "${SIA_ARCH}" STREQUAL "powerpc64" OR - "${SIA_ARCH}" STREQUAL "powerpc64le" OR - "${SIA_ARCH}" STREQUAL "s390x" OR - "${SIA_ARCH}" STREQUAL "riscv64" OR - "${SIA_ARCH}" STREQUAL "wasm64") + "${SIA_ARCH}" STREQUAL "amd64" OR + "${SIA_ARCH}" STREQUAL "arm64" OR + "${SIA_ARCH}" STREQUAL "arm64e" OR + "${SIA_ARCH}" STREQUAL "aarch64" OR + "${SIA_ARCH}" STREQUAL "powerpc64" OR + "${SIA_ARCH}" STREQUAL "powerpc64le" OR + "${SIA_ARCH}" STREQUAL "s390x" OR + "${SIA_ARCH}" STREQUAL "riscv64" OR + "${SIA_ARCH}" STREQUAL "wasm64") set("${var_name}" "${SIA_CASE_64_BIT}" PARENT_SCOPE) else() message(FATAL_ERROR "Unknown architecture: ${SIA_ARCH}") diff --git a/cmake/modules/SwiftUtils.cmake b/cmake/modules/SwiftUtils.cmake index daeb693fb7b0d..34f8017d6ccf5 100644 --- a/cmake/modules/SwiftUtils.cmake +++ b/cmake/modules/SwiftUtils.cmake @@ -115,6 +115,19 @@ function(get_bootstrapping_swift_lib_dir bs_lib_dir bootstrapping) # FIXME: This assumes the ABI hasn't changed since the builder. get_filename_component(swift_bin_dir ${CMAKE_Swift_COMPILER} DIRECTORY) get_filename_component(swift_dir ${swift_bin_dir} DIRECTORY) + + # Detect and handle swiftly-managed hosts. + if(swift_bin_dir MATCHES ".*/swiftly/bin") + execute_process(COMMAND swiftly use --print-location + OUTPUT_VARIABLE swiftly_dir + ERROR_VARIABLE err) + if(err) + message(SEND_ERROR "Failed to find swiftly Swift compiler") + endif() + string(STRIP "${swiftly_dir}" swiftly_dir) + set(swift_dir "${swiftly_dir}/usr") + endif() + set(bs_lib_dir "${swift_dir}/lib/swift/${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}") endif() endif() diff --git a/docs/ABI/KeyPaths.md b/docs/ABI/KeyPaths.md index 7adf350258ee5..d742ab50c5296 100644 --- a/docs/ABI/KeyPaths.md +++ b/docs/ABI/KeyPaths.md @@ -92,7 +92,7 @@ Value in bits 24...30 | Description - A **class stored property** component, when given a reference to a class instance, can project the component value inside the class instance at a fixed offset. The *payload* - *payload* contains the offset in bytes of the projected field from the + contains the offset in bytes of the projected field from the address point of the object, or the special value `0xFF_FFFF`, which indicates that the offset is too large to pack into the payload and is stored in the next 32 bits after the header. diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 1dba89461ded3..c4beec306e9cb 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -66,9 +66,9 @@ part of a symbolic reference. symbolic-reference ::= [\x01-\x17] .{4} // Relative symbolic reference #if sizeof(void*) == 8 - symbolic-reference ::= [\x18-\x1F] .{8} // Absolute symbolic reference + symbolic-reference ::= [\x18-\x1F] .{8} // Absolute symbolic reference for 64-bit pointers #elif sizeof(void*) == 4 - symbolic-reference ::= [\x18-\x1F] .{4} // Absolute symbolic reference + symbolic-reference ::= [\x18-\x1F] .{4} // Absolute symbolic reference for 32-bit pointers #endif Symbolic references are only valid in compiler-emitted metadata structures @@ -110,7 +110,7 @@ The following symbolic reference kinds are currently implemented: #endif #if SWIFT_RUNTIME_VERSION >= 5.TBD - objective-c-protocol-relative-reference ::= `\x0C` .{4} // Reference points directly to a objective-c protcol reference + objective-c-protocol-relative-reference ::= '\x0C' .{4} // Reference points directly to a objective-c protcol reference #endif A mangled name may also include ``\xFF`` bytes, which are only used for @@ -151,7 +151,7 @@ Globals global ::= protocol 'Hr' // protocol descriptor runtime record global ::= nominal-type 'Hn' // nominal type descriptor runtime record #if SWIFT_RUNTIME_VERSION >= 5.1 - global ::= opaque-type 'Ho' // opaque type descriptor runtime record + global ::= opaque-type-decl-name 'Ho' // opaque type descriptor runtime record #endif global ::= protocol-conformance 'Hc' // protocol conformance runtime record global ::= global 'HF' // accessible function runtime record @@ -250,14 +250,11 @@ types where the metadata itself has unknown layout.) global ::= global specialization // function specialization global ::= global 'Tm' // merged function global ::= entity // some identifiable thing - global ::= from-type to-type generic-signature? 'TR' // reabstraction thunk - global ::= impl-function-type type 'Tz' index? // objc-to-swift-async completion handler block implementation - global ::= impl-function-type type 'TZ' index? // objc-to-swift-async completion handler block implementation (predefined by runtime) - global ::= from-type to-type generic-signature? 'TR' // reabstraction thunk - global ::= impl-function-type type generic-signature? 'Tz' // objc-to-swift-async completion handler block implementation - global ::= impl-function-type type generic-signature? 'TZ' // objc-to-swift-async completion handler block implementation (predefined by runtime) - global ::= from-type to-type self-type generic-signature? 'Ty' // reabstraction thunk with dynamic 'Self' capture - global ::= from-type to-type generic-signature? 'Tr' // obsolete mangling for reabstraction thunk + global ::= type type generic-signature? 'TR' // reabstraction thunk + global ::= impl-function-type type generic-signature? 'Tz' index? // objc-to-swift-async completion handler block implementation + global ::= impl-function-type type generic-signature? 'TZ' index? // objc-to-swift-async completion handler block implementation predefined by runtime + global ::= type type type generic-signature? 'Ty' // reabstraction thunk with dynamic 'Self' capture + global ::= type type generic-signature? 'Tr' // obsolete mangling for reabstraction thunk global ::= entity generic-signature? type type* 'TK' // key path getter global ::= entity generic-signature? type type* 'Tk' // key path setter global ::= entity generic-signature? type type* 'Tkmu' // key path unapplied method @@ -266,9 +263,9 @@ types where the metadata itself has unknown layout.) global ::= type generic-signature 'Th' // key path hasher global ::= global generic-signature? 'TJ' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' // autodiff function global ::= global generic-signature? 'TJV' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' // autodiff derivative vtable thunk - global ::= from-type to-type 'TJO' AUTODIFF-FUNCTION-KIND // autodiff self-reordering reabstraction thunk - global ::= from-type 'TJS' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' INDEX-SUBSET 'P' // autodiff linear map subset parameters thunk - global ::= global to-type 'TJS' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' INDEX-SUBSET 'P' // autodiff derivative function subset parameters thunk + global ::= type type 'TJO' AUTODIFF-FUNCTION-KIND // autodiff self-reordering reabstraction thunk + global ::= type 'TJS' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' INDEX-SUBSET 'P' // autodiff linear map subset parameters thunk + global ::= global type 'TJS' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' INDEX-SUBSET 'P' // autodiff derivative function subset parameters thunk global ::= protocol 'TL' // protocol requirements base descriptor global ::= assoc-type-name 'Tl' // associated type descriptor @@ -280,9 +277,9 @@ types where the metadata itself has unknown layout.) REABSTRACT-THUNK-TYPE ::= 'R' // reabstraction thunk REABSTRACT-THUNK-TYPE ::= 'r' // reabstraction thunk (obsolete) - global ::= reabstraction-thunk type 'TU' // reabstraction thunk with global actor constraint + global ::= global type 'TU' // reabstraction thunk with global actor constraint -The `from-type` and `to-type` in a reabstraction thunk helper function +All reabstraction thunks have the "from" and "to" types in that order, and are always non-polymorphic ```` types. :: @@ -331,6 +328,7 @@ with a differentiable function used for differentiable programming. global ::= generic-signature? type 'WOe' // Outlined consume global ::= generic-signature? type 'WOr' // Outlined retain global ::= generic-signature? type 'WOs' // Outlined release + global ::= generic-signature? type 'WOB' // Outlined initializeWithTake, not using value witness global ::= generic-signature? type 'WOb' // Outlined initializeWithTake global ::= generic-signature? type 'WOc' // Outlined initializeWithCopy global ::= generic-signature? type 'WOC' // Outlined initializeWithCopy, not using value witness @@ -340,9 +338,9 @@ with a differentiable function used for differentiable programming. global ::= generic-signature? type 'WOF' // Outlined assignWithCopy, not using value witness global ::= generic-signature? type 'WOh' // Outlined destroy global ::= generic-signature? type 'WOH' // Outlined destroy, not using value witness - global ::= generic-signature? type 'WOi` // Outlined store enum tag - global ::= generic-signature? type 'WOj` // Outlined enum destructive project - global ::= generic-signature? type 'WOg` // Outlined enum get tag + global ::= generic-signature? type 'WOi' // Outlined store enum tag + global ::= generic-signature? type 'WOj' // Outlined enum destructive project + global ::= generic-signature? type 'WOg' // Outlined enum get tag Entities ~~~~~~~~ @@ -356,8 +354,10 @@ Entities curry-thunk ::= 'Tc' label-list ::= empty-list // represents complete absence of parameter labels - label-list ::= ('_' | identifier)* // '_' is inserted as placeholder for empty label, + label-list ::= label* // '_' is inserted as placeholder for empty label, // since the number of labels should match the number of parameters + label ::= '_' // empty label + label ::= identifier // label // The leading type is the function type entity-spec ::= label-list type file-discriminator? 'fC' // allocating constructor @@ -368,6 +368,7 @@ Entities entity-spec ::= entity 'fa' // runtime discoverable attribute generator entity-spec ::= 'fi' // non-local variable initializer entity-spec ::= 'fP' // property wrapper backing initializer + entity-spec ::= 'fF' // property wrapped field init accessor entity-spec ::= 'fW' // property wrapper init from projected value entity-spec ::= 'fD' // deallocating destructor; untyped entity-spec ::= 'fZ' // isolated deallocating destructor; untyped @@ -400,6 +401,8 @@ Entities ACCESSOR ::= 'p' // pseudo accessor referring to the storage itself ACCESSOR ::= 'x' // modify ACCESSOR ::= 'y' // read + ACCESSOR ::= 'b' // borrow + ACCESSOR ::= 'z' // mutate ADDRESSOR-KIND ::= 'u' // unsafe addressor (no owner) ADDRESSOR-KIND ::= 'O' // owning addressor (non-native owner), not used anymore @@ -601,6 +604,8 @@ Types any-generic-type ::= standard-substitutions + nominal-type ::= any-generic-type // nominal type + standard-substitutions ::= 'S' KNOWN-TYPE-KIND // known nominal type substitution standard-substitutions ::= 'S' NATURAL KNOWN-TYPE-KIND // repeated known type substitutions of the same kind @@ -723,8 +728,9 @@ Types type ::= '$' 'n'? INDEX // integer type #endif - bound-generic-type ::= type 'y' (type* '_')* type* retroactive-conformance* 'G' // one type-list per nesting level of type + bound-generic-type ::= type bound-generic-args 'G' // one type-list per nesting level of type bound-generic-type ::= substitution + bound-generic-args ::= 'y' (type* '_')* type* retroactive-conformance* // generic arguments FUNCTION-KIND ::= 'f' // @thin function type FUNCTION-KIND ::= 'U' // uncurried function type (currently not used) @@ -737,7 +743,7 @@ Types FUNCTION-KIND ::= 'A' // @auto_closure function type (escaping) FUNCTION-KIND ::= 'E' // function type (noescape) - C-TYPE ::= NATURAL CHAR* // raw Itanium mangling + C-TYPE ::= NATURAL IDENTIFIER-STRING // raw Itanium mangling function-signature ::= result-type params-type async? sendable? throws? differentiable? function-isolation? sending-result? // results and parameters @@ -761,7 +767,7 @@ Types sending-result ::= 'YT' // -> sending T #endif #if SWIFT_RUNTIME_VERSION >= 6.2 - function-isolation :== 'YC' // @execution(caller) on function type + function-isolation :== 'YC' // nonisolated(nonsending) on function type #endif differentiable ::= 'Yjf' // @differentiable(_forward) on function type differentiable ::= 'Yjr' // @differentiable(reverse) on function type @@ -784,7 +790,7 @@ The 6.0 Swift runtime supports demangling ``sending-result``, but has a bug when METATYPE-REPR ::= 'o' // ObjC metatype representation existential-layout ::= protocol-list 'p' // existential layout - existential-layout ::= protocol-list superclass 'Xc' // existential layout with superclass + existential-layout ::= protocol-list type 'Xc' // existential layout with superclass existential-layout ::= protocol-list 'Xl' // existential layout with AnyObject type ::= associated-type @@ -800,11 +806,10 @@ The 6.0 Swift runtime supports demangling ``sending-result``, but has a bug when type ::= assoc-type-name 'Qz' // shortcut for 'Qyz' type ::= assoc-type-list 'QY' GENERIC-PARAM-INDEX // associated type at depth type ::= assoc-type-list 'QZ' // shortcut for 'QYz' - type ::= opaque-type-decl-name bound-generic-args 'Qo' INDEX // opaque type - - type ::= pack-type 'Qe' INDEX // pack element type + + type ::= type 'Qe' INDEX // pack element type - type ::= pattern-type count-type 'Qp' // pack expansion type + type ::= type type 'Qp' // pack expansion type (pattern, count) type ::= pack-element-list 'QP' // pack type type ::= pack-element-list 'QS' DIRECTNESS // SIL pack type @@ -813,7 +818,7 @@ The 6.0 Swift runtime supports demangling ``sending-result``, but has a bug when #if SWIFT_RUNTIME_VERSION >= 5.2 type ::= type assoc-type-name 'Qx' // associated type relative to base `type` - type ::= type assoc-type-list 'QX' // associated type relative to base `type` + type ::= type assoc-type-list 'QX' // associated type relative to base `type` list #endif #if SWIFT_RUNTIME_VERSION >= 5.7 @@ -829,7 +834,7 @@ The 6.0 Swift runtime supports demangling ``sending-result``, but has a bug when associated-type ::= type identifier 'Qa' // associated type assoc-type-name ::= identifier // associated type name without protocol - assoc-type-name ::= identifier protocol 'P' // + assoc-type-name ::= identifier protocol 'P' // associated type name with protocol empty-list ::= 'y' @@ -844,7 +849,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? ISOLATION? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? SENDABLE? ASYNC? SENDING-RESULT? (PARAM-CONVENTION PARAM-DIFFERENTIABILITY?)* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION RESULT-DIFFERENTIABILITY?)? + FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUB? PSEUDO-GENERIC? CALLEE-ESCAPE? ISOLATION? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? SENDABLE? ASYNC? SENDING-RESULT? (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 @@ -900,6 +905,15 @@ mangled in to disambiguate. PARAM-CONVENTION ::= 'p' // pack guaranteed PARAM-CONVENTION ::= 'm' // pack inout + #if SWIFT_RUNTIME_VERSION >= 6.0 + SENDING-PARAM ::= 'T' // sending parameter + #endif + + #if SWIFT_RUNTIME_VERSION >= 6.2 + ISOLATED-PARAM ::= 'I' // @isolated parameter + IMPLICIT-LEADING-PARAM ::= 'L' // @implicit_leading parameter + #endif + PARAM-DIFFERENTIABILITY ::= 'w' // @noDerivative RESULT-CONVENTION ::= 'r' // indirect @@ -952,10 +966,10 @@ productions: :: any-generic-type ::= context decl-name 'a' // typealias type - type ::= base-type "XSq" // sugared Optional type - type ::= base-type "XSa" // sugared Array type - type ::= key-type value-type "XSD" // sugared Dictionary type - type ::= count-type element-type "XSA" // sugared InlineArray type + type ::= type 'XSq' // sugared Optional type + type ::= type 'XSa' // sugared Array type + type ::= type type 'XSD' // sugared Dictionary type (key, value) + type ::= type type 'XSA' // sugared InlineArray type (count, element) Generics ~~~~~~~~ @@ -1004,7 +1018,7 @@ Property behaviors are implemented using private protocol conformances. dependent-associated-conformance 'HA' DEPENDENT-CONFORMANCE-INDEX dependent-associated-conformance ::= type protocol - dependent-protocol-conformance ::= dependent-protocol-conformance opaque-type 'HO' + dependent-protocol-conformance ::= dependent-protocol-conformance type 'HO' pack-protocol-conformance ::= any-protocol-conformance-list 'HX' @@ -1040,7 +1054,7 @@ now codified into the ABI; the index 0 is therefore reserved. generic-param-marker ::= generic-param-pack-marker generic-param-marker ::= generic-param-value-marker - generic-param-pack-marker ::= 'Rv' GENERIC_PARAM-INDEX // generic parameter pack marker + generic-param-pack-marker ::= 'Rv' GENERIC-PARAM-INDEX // generic parameter pack marker #if SWIFT_RUNTIME_VERSION >= 6.TBD generic-param-value-marker ::= type 'RV' GENERIC-PARAM-INDEX // generic parameter value marker @@ -1084,9 +1098,9 @@ now codified into the ABI; the index 0 is therefore reserved. LAYOUT-CONSTRAINT ::= 'T' // Trivial LAYOUT-CONSTRAINT ::= 'C' // Class LAYOUT-CONSTRAINT ::= 'D' // NativeClass - LAYOUT-CONSTRAINT ::= 'E' LAYOUT-SIZE-AND-ALIGNMENT // Trivial of exact size + LAYOUT-CONSTRAINT ::= 'E' LAYOUT-SIZE-AND-ALIGNMENT // Trivial of exact size and alignment LAYOUT-CONSTRAINT ::= 'e' LAYOUT-SIZE // Trivial of exact size - LAYOUT-CONSTRAINT ::= 'M' LAYOUT-SIZE-AND-ALIGNMENT // Trivial of size at most N bits + LAYOUT-CONSTRAINT ::= 'M' LAYOUT-SIZE-AND-ALIGNMENT // Trivial of size and alignment at most N bits LAYOUT-CONSTRAINT ::= 'm' LAYOUT-SIZE // Trivial of size at most N bits LAYOUT-CONSTRAINT ::= 'U' // Unknown layout LAYOUT-CONSTRAINT ::= 'B' // BridgeObject @@ -1230,7 +1244,7 @@ Substitutions :: substitution ::= 'A' INDEX // substitution of N+26 - substitution ::= 'A' SUBST_IDX* LAST-SUBST-IDX // One or more consecutive substitutions of N < 26 + substitution ::= 'A' SUBST-IDX* LAST-SUBST-IDX // One or more consecutive substitutions of N < 26 SUBST-IDX ::= [a-z] SUBST-IDX ::= NATURAL [a-z] LAST-SUBST-IDX ::= [A-Z] @@ -1281,14 +1295,14 @@ Numbers and Indexes INDEX ::= '_' // 0 INDEX ::= NATURAL '_' // N+1 NATURAL ::= [1-9] [0-9]* - NATURAL_ZERO ::= [0-9]+ + NATURAL-ZERO ::= [0-9]+ ```` is a production for encoding numbers in contexts that can't end in a digit; it's optimized for encoding smaller numbers. :: - INDEX-SUBSET ::= ('S' | 'U')+ + INDEX-SUBSET ::= [SU]+ ```` is encoded like a bit vector and is optimized for encoding indices with a small upper bound. @@ -1348,6 +1362,7 @@ Some kinds need arguments, which precede ``Tf``. ARG-SPEC-KIND ::= 'n' // Unmodified argument ARG-SPEC-KIND ::= 'c' // Consumes n 'type' arguments which are closed over types in argument order // and one 'identifier' argument which is the closure symbol name + ARG-SPEC-KIND ::= 'C' NATURAL-ZERO // the same closure as a previous argument ARG-SPEC-KIND ::= 'p' CONST-PROP // Constant propagated argument ARG-SPEC-KIND ::= 'e' 'D'? 'G'? 'X'? // Generic argument, with optional dead, owned=>guaranteed or exploded-specifier ARG-SPEC-KIND ::= 'd' 'G'? 'X'? // Dead argument, with optional owned=>guaranteed or exploded-specifier @@ -1358,14 +1373,15 @@ Some kinds need arguments, which precede ``Tf``. CONST-PROP ::= 'f' // Consumes one identifier argument which is a function symbol name CONST-PROP ::= 'g' // Consumes one identifier argument which is a global symbol name - CONST-PROP ::= 'i' NATURAL_ZERO // 64-bit-integer - CONST-PROP ::= 'd' NATURAL_ZERO // float-as-64-bit-integer + CONST-PROP ::= 'i' NATURAL-ZERO // 64-bit-integer + CONST-PROP ::= 'd' NATURAL-ZERO // float-as-64-bit-integer CONST-PROP ::= 's' ENCODING // string literal. Consumes one identifier argument. CONST-PROP ::= 'k' // keypath. Consumes one identifier - the SHA1 of the keypath and two types (root and value). + CONST-PROP ::= 'S' CONST-PROP* // struct with operands. Consumes one type argument - the struct type. ENCODING ::= 'b' // utf8 ENCODING ::= 'w' // utf16 - ENCODING ::= 'c' // utf16 + ENCODING ::= 'c' // objc If the first character of the string literal is a digit ``[0-9]`` or an underscore ``_``, the identifier for the string literal is prefixed with an diff --git a/docs/Android.md b/docs/Android.md index 879a5d5d998d3..918909b07756f 100644 --- a/docs/Android.md +++ b/docs/Android.md @@ -22,21 +22,17 @@ on an Android device, it takes a lot more than just the Swift stdlib to write an app. You'd need some sort of framework to build a user interface for your application, which the Swift stdlib does not provide. -Alternatively, one could theoretically call into Java interfaces from Swift, -but unlike as with Objective-C, the Swift compiler does nothing to facilitate -Swift-to-Java bridging. - ## Prerequisites To follow along with this guide, you'll need: 1. A Linux environment capable of building Swift from source, preferably - Ubuntu 22.04 or Ubuntu 20.04. Before attempting to build for Android, + Ubuntu 24.04 or Ubuntu 22.04. Before attempting to build for Android, please make sure you are able to build for Linux by following the instructions in the Swift project README. 2. The latest build of the Swift compiler for your Linux distro, available at - https://www.swift.org/download/ or sometimes your distro package manager. -3. The latest version of the Android LTS NDK (r27c at the time of this writing), + https://www.swift.org/install/linux/ or sometimes your distro package manager. +3. The latest version of the Android LTS NDK (r27d at the time of this writing), available to download here: https://developer.android.com/ndk/downloads 4. An Android device with remote debugging enabled or the emulator. We require @@ -54,9 +50,9 @@ and the prebuilt Swift toolchain (add --skip-early-swift-driver if you already have a Swift toolchain in your path): ``` -$ NDK_PATH=path/to/android-ndk-r27c -$ SWIFT_PATH=path/to/swift-DEVELOPMENT-SNAPSHOT-2024-11-09-a-ubuntu22.04/usr/bin -$ git checkout swift-DEVELOPMENT-SNAPSHOT-2024-11-09-a +$ NDK_PATH=path/to/android-ndk-r27d +$ SWIFT_PATH=path/to/swift-DEVELOPMENT-SNAPSHOT-2025-08-07-a-ubuntu24.04/usr/bin +$ git checkout swift-DEVELOPMENT-SNAPSHOT-2025-08-07-a $ utils/build-script \ -R \ # Build in ReleaseAssert mode. --android \ # Build for Android. @@ -83,11 +79,12 @@ Then use the standalone Swift stdlib from the previous step to compile a Swift source file, targeting Android: ``` -$ NDK_PATH="path/to/android-ndk-r27c" -$ SWIFT_PATH=path/to/swift-DEVELOPMENT-SNAPSHOT-2024-11-09-a-ubuntu22.04/usr/bin +$ NDK_PATH="path/to/android-ndk-r27d" +$ SWIFT_PATH=path/to/swift-DEVELOPMENT-SNAPSHOT-2025-08-07-a-ubuntu24.04/usr/bin $ $SWIFT_PATH/swiftc \ # The prebuilt Swift compiler you downloaded # The location of the tools used to build Android binaries -tools-directory ${NDK_PATH}/toolchains/llvm/prebuilt/linux-x86_64/bin/ \ + -disallow-use-new-driver \ # Work around a bug in the driver, a fix is in the works -target aarch64-unknown-linux-android21 \ # Targeting Android aarch64 at API 21 -sdk ${NDK_PATH}/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ # The SDK is the Android unified sysroot and the resource-dir is where you just built the Swift stdlib. -resource-dir build/Ninja-ReleaseAssert/swift-linux-x86_64/lib/swift @@ -135,7 +132,7 @@ $ adb push build/Ninja-ReleaseAssert/swift-linux-x86_64/lib/swift/android/libBlo In addition, you'll also need to copy the Android NDK's libc++: ``` -$ adb push /path/to/android-ndk-r27c/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so /data/local/tmp +$ adb push /path/to/android-ndk-r27d/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so /data/local/tmp ``` Finally, you'll need to copy the `hello` executable you built in the @@ -178,7 +175,7 @@ $ utils/build-script \ -R \ # Build in ReleaseAssert mode. -T \ # Run all tests, including on the Android device (add --host-test to only run Android tests on the Linux host). --android \ # Build for Android. - --android-ndk ~/android-ndk-r27c \ # Path to an Android NDK. + --android-ndk ~/android-ndk-r27d \ # Path to an Android NDK. --android-arch aarch64 \ # Optionally specify Android architecture, alternately armv7 or x86_64 --android-api-level 21 ``` diff --git a/docs/ContinuousIntegration.md b/docs/ContinuousIntegration.md index 7660ab4f91c8d..b7885cc507288 100644 --- a/docs/ContinuousIntegration.md +++ b/docs/ContinuousIntegration.md @@ -126,6 +126,7 @@ A validation test on Linux does the following: Platform | Comment | Check Status ------------ | ------- | ------------ macOS platform | @swift-ci Please benchmark | Swift Benchmark on macOS Platform (many runs - rigorous) +macOS platform (Apple Silicon) | @swift-ci Apple Silicon benchmark | Swift Benchmark on macOS Platform using Apple Silicon (many runs - rigorous) macOS platform | @swift-ci Please smoke benchmark | Swift Benchmark on macOS Platform (few runs - soundness) ### Linting @@ -160,10 +161,9 @@ You can also build a toolchain for a specific Linux distribution Distro | Comment | Check Status -------------- | ------------------------------------------------ | ---------------------------------------------- UBI9 | @swift-ci Please Build Toolchain UBI9 | Swift Build Toolchain UBI9 (x86_64) -CentOS 7 | @swift-ci Please Build Toolchain CentOS 7 | Swift Build Toolchain CentOS 7 (x86_64) -Ubuntu 18.04 | @swift-ci Please Build Toolchain Ubuntu 18.04 | Swift Build Toolchain Ubuntu 18.04 (x86_64) Ubuntu 20.04 | @swift-ci Please Build Toolchain Ubuntu 20.04 | Swift Build Toolchain Ubuntu 20.04 (x86_64) Ubuntu 22.04 | @swift-ci Please Build Toolchain Ubuntu 22.04 | Swift Build Toolchain Ubuntu 22.04 (x86_64) +Ubuntu 24.04 | @swift-ci Please Build Toolchain Ubuntu 24.04 | Swift Build Toolchain Ubuntu 24.04 (x86_64) Amazon Linux 2 | @swift-ci Please Build Toolchain Amazon Linux 2 | Swift Build Toolchain Amazon Linux 2 (x86_64) ### Build and Test Stdlib against Snapshot Toolchain diff --git a/docs/Diagnostics.md b/docs/Diagnostics.md index 6acf30d89160e..b40f632f0f2ec 100644 --- a/docs/Diagnostics.md +++ b/docs/Diagnostics.md @@ -38,10 +38,26 @@ Clang also has a kind of diagnostic called a "remark", which represents informat - When applicable, phrase diagnostics as rules rather than reporting that the compiler failed to do something. Example: - - Normal: "cannot call 'super.init' outside of an initializer" - - Better: "'super.init' cannot be called outside of an initializer" - -- When referring to attributes by name, use *either* "the 'foo' attribute" or "'@foo'", rather than "the '@foo' attribute". + - Normal: `cannot call 'super.init' outside of an initializer` + - Better: `'super.init' cannot be called outside of an initializer` + +- Use `'` to quote attributes, symbol names, and any other text that is + referred to as code or a token. + Some idiomatic Swift tokens, such as `protocol`, by themselves are or appear + in Swift terms of art. + Terms of art are written in plain text and must not be quoted. + If you are not certain whether a Swift token has an associated term of art, + consult the [book]. + For example: + - `'mutating' is only valid on methods`, but + `cannot call mutating method on immutable value`. + - `'@autoclosure' only applies to function types`. + - `subscript access can throw but is not marked with 'try'`. + - `expected '{' after 'defer'`. + - `type 'S' does not conform to protocol 'Sequence'`. + +- When referring to attributes by name, use *either* `attribute 'foo'` or + `'@foo'`, rather than `attribute '@foo'`. - Match the tone and phrasing of other diagnostics. Some common phrases: @@ -53,6 +69,8 @@ Clang also has a kind of diagnostic called a "remark", which represents informat - If possible, it is best to include the name of the type or function that has the error, e.g. "non-actor type 'Nope' cannot ..." is better than "non-actor type cannot ...". It helps developers relate the error message to the specific type the error is about, even if the error would highlight the appropriate line / function in other ways. +[book]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language + ### Locations and Highlights ### - Diagnostics are always emitted at a particular line and column. Try to be specific. @@ -98,6 +116,14 @@ Diagnostic group documentation should: - Include references to relevant chapters of _The Swift Programming Language_. - Be written in Markdown, but avoid excessive markup which negatively impacts the terminal UX. +The diagnostic group documentation supports generating a DocC bundle for hosting. You can currently find the compiler diagnostics documentation at https://docs.swift.org/compiler/documentation/diagnostics/ + +To generate this documentation locally, run the following command from the root of the repository: +``` +docc preview --allow-arbitrary-catalog-directories userdocs/diagnostics +``` + + ### Quick-Start Guide for Contributing New Diagnostic Groups ### Adding new diagnostic groups is a great way to get familiar with the process of contributing to Swift, while also making a big impact! @@ -109,7 +135,7 @@ To add a new diagnostics group: - An entry in a `Diagnostics*.def` file describing the diagnostic. If there are any closely related diagnostics the note should also be attached to, they can usually be found nearby. - Each point in the compiler source where the diagnostic is emitted. This can be helpful in determining the exact circumstances which cause it to be emitted. 4. Add a new Markdown file in the `userdocs/diagnostics/` directory in the swift repository containing the documentation. When writing a note, keep the writing guidelines from the section above in mind. The existing notes in the directory are another useful guide. -5. Create a new entry in `DiagnosticGroups.def` that provides the name for your new diagnostic group along with the name of the file you added in step (4). +5. Create a new entry in `DiagnosticGroups.def` that provides the name for your new diagnostic group along with the name of the file you added in step (4), without an extension. 6. Find each diagnostic you want to make part of this group in the various `Diagnostics*.def` files. For each diagnostic, replace the `ERROR` or `WARNING` with `GROUPED_ERROR` or `GROUPED_WARNING`, respectively, and put the diagnostic group name after the string literal for the diagnostic message. 7. If possible, rebuild the compiler and try recompiling your test program. Your new diagnostic group should appear as `[#]` at the end of the diagnostic, with a link to the diagnostic file. 8. That's it! The new diagnostic group is now ready to be submitted as a pull request on GitHub. @@ -130,7 +156,10 @@ If you run into any issues or have questions while following the steps above, fe - `%%` - Emits a literal percent sign. -There are several format specifiers that are specific to `Decl` parameters: +The following subsections describe format specifiers that are specific to +diagnostic arguments of a given type. + +#### `Decl` - `%kind0` - Prefixes the declaration's name with its descriptive decl kind (e.g. `instance method 'foo(x:)'`). @@ -140,6 +169,11 @@ There are several format specifiers that are specific to `Decl` parameters: - `%kindbase0` - Combines `kind` and `base` (e.g. `instance method 'foo'`). +#### `TypeAttribute` + +- `%kind0` - Replaced with `attribute 'foo'`, whereas `%0` is replaced with + `'@foo'`. + Note: If your diagnostic could apply to accessors, be careful how you format the declaration's name; accessors have an empty name, so you need to display their accessor kind and the name of their storage decl instead. Inserting the name with a `Decl *` parameter will handle these complications automatically; if you want to use `DeclName` or `Identifier` instead, you'll probably need a separate version of the diagnostic for accessors. ### Diagnostic Verifier ### diff --git a/docs/EmbeddedSwift/ABI.md b/docs/EmbeddedSwift/ABI.md index f3be5ec377fdc..a5542b068a54c 100644 --- a/docs/EmbeddedSwift/ABI.md +++ b/docs/EmbeddedSwift/ABI.md @@ -1,58 +1,3 @@ # Embedded Swift -- ABI -**⚠️ Embedded Swift is experimental. This document might be out of date with latest development.** - -**‼️ Use the latest downloadable 'Trunk Development' snapshot from swift.org to use Embedded Swift. Public releases of Swift do not yet support Embedded Swift.** - -For an introduction and motivation into Embedded Swift, please see "[A Vision for Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md)", a Swift Evolution document highlighting the main goals and approaches. - -## ABI stability - -The ABI of code generated by Embedded Swift is not currently stable. For a concrete compiler version, it will be consistent, but do not mix code built with different compiler versions. - -Similarly, do not mix Embedded Swift code with full Swift code, as the ABIs are different. Details are described in the following sections. - -## Symbol mangling under Embedded Swift - -Since Swift 5.0, the stable ABI mangling schema uses the `$s` prefix on all Swift symbols. Because Embedded Swift's ABI differs from the stable ABI, and furthermore because it's not expected to be stable, Embedded Swift uses a `$e` mangling prefix. The logic and structure of the mangling stays the same, the only difference is the prefix. - -## Calling convention of Embedded Swift - -As of today, Embedded Swift has identical calling convention to full Swift. However, this does not need to continue in the future, and there should not be expectations that the ABI of Embedded Swift is compatible with full Swift. - -The compiler respects the ABIs and calling conventions of C and C++ when interoperating with code in those languages. Calling C/C++ functions from Embedded Swift code is supported, and similarly exporting Swift code via `@_extern`, `@_cdecl` or `@_expose` will match the right calling conventions that C/C++ expects. - -## Metadata ABI of Embedded Swift - -Embedded Swift eliminates almost all metadata compared to full Swift. However, class and existential metadata are still used, because those serve as vtables and witness tables for dynamic dispatch of methods to implement runtime polymorphism with classes and existentials. - -### Class Metadata ABI - -The layout of Embedded Swift's class metadata is *different* from full Swift: - -- The **super pointer** pointing to the class metadata record for the superclass is stored at **offset 0**. If the class is a root class, it is null. -- The **destructor pointer** is stored at **offset 1**. This function is invoked by Swift's deallocator when the class instance is destroyed. -- The **ivar destroyer** is stored at **offset 2**. This function is invoked to destroy instance members when creation of the object is cancelled (e.g. in a failable initializer). -- Lastly, the **vtable** is stored at **offset 3**: For each Swift class in the class's inheritance hierarchy, in order starting - from the root class and working down to the most derived class, the function pointers to the implementation of every method of the class in declaration order in stored. - -### Witness Tables ABI - -The layout of Embedded Swift's witness tables is *different* from full Swift: - -- The first word is always a null pointer (TODO: it can be eliminated) -- The following words are witness table entries which can be one of the following: - - A method witness: a pointer to the witness function. - - An associated conformance witness: a pointer to the witness table of the associated conformance - -Note that witness tables in Embedded Swift do not contain associated type entries. - -Witness functions are always specialized for concrete types. This also means that parameters and return values are passed directly (if possible). - -## Heap object layout in Embedded Swift - -Heap objects have the following layout in Embedded Swift: - -- The **isa pointer** (pointer to the class metadata) is stored at **offset 0**. -- The **refcount** is stored inline at **offset 1**. -- Normal stored properties follow. +Embedded Swift documentation has moved to: [Swift.org](https://docs.swift.org/embedded/documentation/embedded). diff --git a/docs/EmbeddedSwift/EmbeddedSwiftStatus.md b/docs/EmbeddedSwift/EmbeddedSwiftStatus.md index 07b017d436037..df2f798634b21 100644 --- a/docs/EmbeddedSwift/EmbeddedSwiftStatus.md +++ b/docs/EmbeddedSwift/EmbeddedSwiftStatus.md @@ -1,77 +1,3 @@ # Embedded Swift -- Status -**⚠️ Embedded Swift is experimental. This document might be out of date with latest development.** - -**‼️ Use the latest downloadable 'Trunk Development' snapshot from swift.org to use Embedded Swift. Public releases of Swift do not yet support Embedded Swift.** - -For an introduction and motivation into Embedded Swift, please see "[A Vision for Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md)", a Swift Evolution document highlighting the main goals and approaches. - -## Embedded Swift Language Features - -| **Language Feature** | **Currently Supported In Embedded Swift** | -|-------------------------------------------------------|------------------------------------------------------------------------------------| -| *Anything not listed below* | Yes | -| Library Evolution (resilience) | No, intentionally unsupported long-term | -| Objective-C interoperability | No, intentionally unsupported long-term | -| Non-WMO builds | No, intentionally unsupported long-term (WMO should be used) | -| Existentials (values of protocol types) | Only class-bound existentials (for protocols derived from AnyObject) are supported | -| Any | No, currently disallowed | -| AnyObject | Yes | -| Metatypes | No, currently only allowed as unused arguments (type hints) | -| Untyped throwing | No, intentionally unsupported long-term (typed throwing should be used instead) | -| Weak references, unowned references | No | -| Non-final generic class methods | No, intentionally unsupported long-term | -| Parameter packs (variadic generics) | No, not yet supported | - -## Embedded Standard Library Breakdown - -This status table describes which of the following standard library features can be used in Embedded Swift: - -| **Swift Standard Library Feature** | **Currently Supported In Embedded Swift?** | -|------------------------------------------------------------|-----------------------------------------------------| -| Array (dynamic heap-allocated container) | Yes | -| Array slices | Yes | -| assert, precondition, fatalError | Partial, only StaticStrings can be used as a failure message | -| Bool, Integer types, Float types | Yes | -| Codable, Encodable, Decodable | No | -| Collection + related protocols | Yes | -| Collection algorithms (sort, reverse) | Yes | -| CustomStringConvertible, CustomDebugStringConvertible | Yes, except those that require reflection (e.g. Array's .description) | -| Dictionary (dynamic heap-allocated container) | Yes | -| Floating-point conversion to string | No | -| Floating-point parsing | No | -| FixedWidthInteger + related protocols | Yes | -| Hashable, Equatable, Comparable protocols | Yes | -| InputStream, OutputStream | No | -| Integer conversion to string | Yes | -| Integer parsing | No | -| KeyPaths | Partial (only compile-time constant key paths to stored properties supported, only usable in MemoryLayout and UnsafePointer APIs) | -| Lazy collections | No | -| ManagedBuffer | Yes | -| Mirror (runtime reflection) | No, intentionally unsupported long-term | -| Objective-C bridging | No, intentionally unsupported long-term | -| Optional | Yes | -| print / debugPrint | Partial (only String, string interpolation, StaticStrings, integers, pointers and booleans) | -| Range, ClosedRange, Stride | Yes | -| Result | Yes | -| Set (dynamic heap-allocated container) | Yes | -| SIMD types | Yes | -| StaticString | Yes | -| String (dynamic) | Yes | -| String interpolations | Partial (only strings, integers, booleans, and custom types that are CustomStringConvertible can be interpolated) | -| Unicode | Yes | -| Unsafe\[Mutable\]\[Raw\]\[Buffer\]Pointer | Yes | -| VarArgs | No | - -## Non-stdlib Features - -This status table describes which of the following Swift features can be used in Embedded Swift: - -| **Swift Feature** | **Currently Supported In Embedded Swift?** | -|------------------------------------------------------------|-----------------------------------------------------| -| Synchronization module | Partial (only Atomic types, no Mutex) | -| Swift Concurrency | Partial, experimental (basics of actors and tasks work in single-threaded concurrency mode) | -| C interop | Yes | -| C++ interop | Yes | -| ObjC interop | No, intentionally unsupported long-term | -| Library Evolution | No, intentionally unsupported long-term | +Embedded Swift documentation has moved to: [Swift.org](https://docs.swift.org/embedded/documentation/embedded). diff --git a/docs/EmbeddedSwift/Existentials.md b/docs/EmbeddedSwift/Existentials.md new file mode 100644 index 0000000000000..e61d3a037f438 --- /dev/null +++ b/docs/EmbeddedSwift/Existentials.md @@ -0,0 +1,3 @@ +# Embedded Swift -- Existentials + +Embedded Swift documentation has moved to: [Swift.org](https://docs.swift.org/embedded/documentation/embedded). diff --git a/docs/EmbeddedSwift/IntegratingWithSDKs.md b/docs/EmbeddedSwift/IntegratingWithSDKs.md index 6a39248d79f89..a4f4dae4568a5 100644 --- a/docs/EmbeddedSwift/IntegratingWithSDKs.md +++ b/docs/EmbeddedSwift/IntegratingWithSDKs.md @@ -1,112 +1,3 @@ # Embedded Swift -- Integrating with embedded SDKs -**⚠️ Embedded Swift is experimental. This document might be out of date with latest development.** - -**‼️ Use the latest downloadable 'Trunk Development' snapshot from swift.org to use Embedded Swift. Public releases of Swift do not yet support Embedded Swift.** - -For an introduction and motivation into Embedded Swift, please see "[A Vision for Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md)", a Swift Evolution document highlighting the main goals and approaches. - -The following document sketches how to integrate Swift code into some popular embedded platforms' SDKs and build systems. - -## Integrating with Raspberry Pi Pico (W) build system: - -Development for [Raspberry Pi Pico and Pico W](https://www.raspberrypi.com/products/raspberry-pi-pico/) normally uses the [Pico SDK](https://github.com/raspberrypi/pico-sdk) and the vendor provides several [sample projects in the pico-examples repository](https://github.com/raspberrypi/pico-examples). The SDK and sample project setup is described in: - -- https://www.raspberrypi.com/documentation/microcontrollers/c_sdk.html#sdk-setup -- https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf - -Before trying to use Swift with the Pico SDK, make sure your environment works and can build the provided C/C++ sample projects. - -### CMake setup with a bridging header - -The Pico SDK is using CMake as its build system, and so the simplest way to integrate with it is to also use CMake to build a Swift firmware application on top of the SDK and the libraries from it. The following describes an example set up of that on a "blinky" example (code that just blinks the built-in LED). - -Let's create a directory with a Swift source file, a bridging header, and a CMake definition file: - -``` -./SwiftPicoBlinky/Main.swift -./SwiftPicoBlinky/BridgingHeader.h -./SwiftPicoBlinky/CMakeLists.txt -``` - -In `Main.swift`, let's add basic logic to initialize the GPIO port for the Pico's built-in LED, and then turn it on and off in a loop: - -```swift -@main -struct Main { - static func main() { - let led = UInt32(PICO_DEFAULT_LED_PIN) - gpio_init(led) - gpio_set_dir(led, /*out*/true) - while true { - gpio_put(led, true) - sleep_ms(250) - gpio_put(led, false) - sleep_ms(250) - } - } -} -``` - -Notice that we're using functions and variables defined in C in the Pico SDK. For that to be possible, the Swift compiler needs to have access to the C header files that define these functions and variables. The cleanest option would be to define a modulemap, but for simplicity let's just use a bridging header to make declarations visible in Swift without a module. `BridgingHeader.h` should contain: - -```c -#pragma once - -#include "pico/stdlib.h" -``` - -Finally, we need to define the application's build rules in CMake that will be using CMake logic from the Pico SDK. The following content of `CMakeLists.txt` shows how to *manually call swiftc, the Swift compiler* instead of using the recently added CMake native support for Swift, so that we can see the full Swift compilation command. - -```cmake -cmake_minimum_required(VERSION 3.13) -include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake) - -project(swift-blinky) -pico_sdk_init() -execute_process(COMMAND xcrun -f swiftc OUTPUT_VARIABLE SWIFTC OUTPUT_STRIP_TRAILING_WHITESPACE) - -add_executable(swift-blinky) -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o - COMMAND - ${SWIFTC} - -target armv6m-none-none-eabi -Xcc -mfloat-abi=soft -Xcc -fshort-enums - -Xfrontend -function-sections -enable-experimental-feature Embedded -wmo -parse-as-library - $$\( echo '$' | tr '\;' '\\n' | sed -e 's/\\\(.*\\\)/-Xcc -I\\1/g' \) - $$\( echo '${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}' | tr ' ' '\\n' | sed -e 's/\\\(.*\\\)/-Xcc -I\\1/g' \) - -import-bridging-header ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h - ${CMAKE_CURRENT_LIST_DIR}/Main.swift - -c -o ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o - DEPENDS - ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h - ${CMAKE_CURRENT_LIST_DIR}/Main.swift -) -add_custom_target(swift-blinky-swiftcode DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o) - -target_link_libraries(swift-blinky - pico_stdlib hardware_uart hardware_gpio - ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o -) -add_dependencies(swift-blinky swift-blinky-swiftcode) -pico_add_extra_outputs(swift-blinky) -``` - -With these three files, we can now configure and build a Swift firmware for the Pico: - -```bash -$ export TOOLCHAINS=org.swift.59202401301a -$ export PICO_BOARD=pico -$ export PICO_SDK_PATH= -$ export PICO_TOOLCHAIN_PATH= -$ ls -al --rw-r--r-- 1 kuba staff 39B Feb 2 22:08 BridgingHeader.h --rw-r--r-- 1 kuba staff 1.3K Feb 2 22:08 CMakeLists.txt --rw-r--r-- 1 kuba staff 262B Feb 2 22:08 Main.swift -$ mkdir build -$ cd build -$ cmake -S ../ -B . -G Ninja -$ ninja -v -``` - -This should produce several build artifacts in the `build/` subdirectory, include `swift-blinky.uf2`, which can be directly uploaded to the Pico by copying it into the fake Mass Storage Volume that the device presents when plugged over USB in BOOTSEL mode. +Embedded Swift documentation has moved to: [Swift.org](https://docs.swift.org/embedded/documentation/embedded). diff --git a/docs/EmbeddedSwift/NonFinalGenericMethods.md b/docs/EmbeddedSwift/NonFinalGenericMethods.md new file mode 100644 index 0000000000000..2decd4dcca4d7 --- /dev/null +++ b/docs/EmbeddedSwift/NonFinalGenericMethods.md @@ -0,0 +1,3 @@ +# Embedded Swift -- Non-final generic methods + +Embedded Swift documentation has moved to: [Swift.org](https://docs.swift.org/embedded/documentation/embedded). diff --git a/docs/EmbeddedSwift/README.md b/docs/EmbeddedSwift/README.md new file mode 100644 index 0000000000000..b72ed93bd3bf5 --- /dev/null +++ b/docs/EmbeddedSwift/README.md @@ -0,0 +1,3 @@ +# Embedded Swift + +Embedded Swift documentation has moved to: [Swift.org](https://docs.swift.org/embedded/documentation/embedded). diff --git a/docs/EmbeddedSwift/UserManual.md b/docs/EmbeddedSwift/UserManual.md index 3ae79e375f7ea..0ee458817daf4 100644 --- a/docs/EmbeddedSwift/UserManual.md +++ b/docs/EmbeddedSwift/UserManual.md @@ -1,278 +1,3 @@ # Embedded Swift -- User Manual -**⚠️ Embedded Swift is experimental. This document might be out of date with latest development.** - -**‼️ Use the latest downloadable 'Trunk Development' snapshot from swift.org to use Embedded Swift. Public releases of Swift do not yet support Embedded Swift.** - -For an introduction and motivation into Embedded Swift, please see "[A Vision for Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md)", a Swift Evolution document highlighting the main goals and approaches. - -The following document explains how to use Embedded Swift's support in the Swift compiler and toolchain. - -## What Embedded Swift is, and what it isn't - -- Embedded Swift **is** a way to produce small and freestanding binaries (with no, or trivial dependencies). -- Embedded Swift **is not** a complete one-click solution to program all embedded boards and MCUs. -- Embedded Swift **is** a compilation model that's analogous to a traditional C compiler in the sense that the compiler produces an object file (.o) that can be simply linked with your existing code, and it's not going to require you to port any libraries or runtimes. -- Embedded Swift **is not** a HAL, it's not an SDK for development, it's not a set of libraries to program peripherals using high-level APIs. It's instead a compilation mode that's suitable for creating these components. - -## Using Embedded Swift - -A typical setup and build + run cycle for an embedded development board involves: - -- (1) Getting an SDK with the C compilers, headers and libraries for the target -- (2) Building the C source code, and Swift source code into object files. -- (3) Linking all the libraries, C object files, and Swift object files. -- (4) Post-processing the linked firmware into a flashable format (UF2, BIN, HEX, or bespoke formats) -- (5) Uploading the flashable binary to the board over a USB cable using some vendor-provided JTAG/SWD tool, by copying it to a fake USB Mass Storage volume presented by the board or a custom platform bootloader. -- (6) Restarting the board, observing physical effects of the firmware (LEDs light up) or UART output over USB, or presence on network, etc. - -Most of these steps are out of scope for this document, instead refer to the vendor provided documentation. This document only focuses on (2) from the list above, and it's important that you first get familiar with the details of firmware development for your board without Swift in the mix. Even if you want to build a completely pure Swift firmware, you are still going to need the vendor provided tooling for linking, post-processing, uploading, etc. - -## Building code using Embedded Swift - -A basic way to build a set of Swift source files in Embedded Swift mode, is to simply give the compiler (1) a target triple, (2) the `-enable-experimental-feature Embedded` flag, (3) the set of source files that form the input module: - -```bash -$ swiftc -target -enable-experimental-feature Embedded -wmo \ - input1.swift input2.swift ... -c -o output.o -``` - -On macOS, it's common to have Xcode installed, which comes with a toolchain that does not support Embedded Swift yet. Unless you download, install, and activate a swift.org toolchain, you'll see this error: - -```bash -$ swiftc input1.swift -enable-experimental-feature Embedded -wmo -:0: error: unable to load standard library for target 'arm64-apple-macosx15.0' -``` - -To resolve it, download and install a nightly toolchain from swift.org. Then, don't forget to activate it in your terminal by setting the `TOOLCHAINS` environment variable, for example with this command (if you installed into the `/Library` path): - -```bash -$ export TOOLCHAINS=$(plutil -extract CFBundleIdentifier raw /Library/Developer/Toolchains/swift-latest.xctoolchain/Info.plist) -``` - -## Examples - -### Building Swift firmware for an embedded target - -To build Swift firmware (for now ignoring integration with SDKs, libraries and other pre-existing C code), we can use the `-target` argument to specify the CPU architecture. The target triple also decides whether the output object file will be an ELF file, or a Mach-O. For example: - -```bash -# To build an ARMv7 Mach-O object file: -$ swiftc -target armv7-apple-none-macho -enable-experimental-feature Embedded -wmo \ - input1.swift input2.swift ... -c -o output.o - -# To build an ARMv7 ELF object file: -$ swiftc -target armv7-none-none-eabi -enable-experimental-feature Embedded -wmo \ - input1.swift input2.swift ... -c -o output.o -``` - -Additionally, you probably want to specify additional Clang and/or LLVM flags to get the compiler to produce code for the exact ISA and ABI you need for your target. - -For example, a Raspberry Pi Pico / Pico W should target the ARMv6-M architecture via the `armv6m-*` target triple, but the `-mfloat-abi=soft` Clang option should also be used, and if you want to match ABI with libraries built with the GNU toolchain, you might also need `-fshort-enums`. To pass those to Swift, use the `-Xcc` prefix: - -```bash -# To build an ELF object file for ARMv6-M with soft float ABI (floating-point arguments passed in integer registers) and "short enums": -$ swiftc -target armv6m-none-none-eabi -enable-experimental-feature Embedded -wmo \ - -Xcc -mfloat-abi=soft -Xcc -fshort-enums \ - input1.swift input2.swift ... -c -o output.o -``` - -This might not be obvious: `-Xcc` flags are typically only used to alter behavior of the Clang importer, but passing flags to Clang this way also works to specify LLVM target options like selecting a specific CPU architecture (`-march`, `-mcpu`, `-mmcu`), FPU unit availability (`-mfpu`), which registers are used to pass floating-point values (`-mfloat-abi`), and others. - -### Integrating with embedded SDKs and build systems - -For details and concrete examples of how to integrate with existing SDKs, see [Embedded Swift -- Integrating with embedded SDKs](IntegratingWithSDKs.md). - -### Building a macOS Embedded Swift program: - -It's also possible to build in Embedded Swift mode for regular non-embedded operating systems, like macOS. This is very useful for testing purposes, or if you just want to observe and experiment with Embedded Swift. A simple source code like this: - -```swift -print("Hello, embedded world!") -``` - -...can be compiled using the `-enable-experimental-feature Embedded` flag (the implicit `-target` matches the host OS): - -```bash -$ xcrun swiftc hello.swift -enable-experimental-feature Embedded -wmo -$ ./hello -Hello, embedded world! -``` - -Note that the resulting executable is still a *dynamically-linked executable*, so it's not fully standalone in the embedded sense. Namely is still uses `putchar` from Libsystem. But the singular object file that was used to build this executable was produced by the compiler in the same fashion that a real embedded build would. If we ask the compiler and linker to minimize the size of the outputs and to remove any unused code, we can observe that the binary has no other dependencies other than `putchar` and that the machine code section is very small (172 bytes in the `__text` section): - -```bash -$ xcrun swiftc hello.swift -enable-experimental-feature Embedded -wmo -Osize -Xlinker -dead_strip -$ nm -um ./hello - (undefined) external _putchar (from libSystem) -$ size -m ./hello -Segment __TEXT: 16384 - Section __text: 172 -... -``` - -## Strings - -Both StaticString and String types are available in Embedded Swift. As is the case in desktop Swift, certain operations on strings require Unicode data tables for strict Unicode compliance. In Embedded Swift these data tables are provided as a separate static library (libUnicodeDataTables.a) that users need to link in manually – if they need to use these string operations. If the library is required, linking will fail due to missing on one or more of the following symbols: - -``` -_swift_stdlib_getAge -_swift_stdlib_getBinaryProperties -_swift_stdlib_getCaseMapping -_swift_stdlib_getComposition -_swift_stdlib_getDecompositionEntry -_swift_stdlib_getGeneralCategory -_swift_stdlib_getGraphemeBreakProperty -_swift_stdlib_getMapping -_swift_stdlib_getMphIdx -_swift_stdlib_getNameAlias -_swift_stdlib_getNormData -_swift_stdlib_getNumericType -_swift_stdlib_getNumericValue -_swift_stdlib_getScalarBitArrayIdx -_swift_stdlib_getScalarName -_swift_stdlib_getScript -_swift_stdlib_getScriptExtensions -_swift_stdlib_getSpecialMapping -_swift_stdlib_getWordBreakProperty -_swift_stdlib_isLinkingConsonant -_swift_stdlib_nfd_decompositions -``` - -To resolve this, link in the `libswiftUnicodeDataTables.a` that's in Swift toolchain's resource directory (`lib/swift/`) under the target triple that you're using: - -```bash -$ swiftc -target armv6m-none-none-eabi -enable-experimental-feature Embedded -wmo -c -o output.o -$ ld ... -o binary output.o $(dirname `which swiftc`)/../lib/swift/embedded/armv6m-none-none-eabi/libswiftUnicodeDataTables.a -``` - -**Unicode data tables are required for (list not exhaustive):** - -- Comparing String objects for equality -- Sorting Strings -- Using String's hash values, and in particular using String as dictionary keys -- Using String's `.count` property -- Using Unicode-aware string processing APIs (`.split()`, iterating characters, indexing) -- Using Unicode-aware conversion String APIs (`.uppercased()`, `.lowercased()`, etc.) - -**For contrast, unicode data tables are *not required for* (list not exhaustive):** - -- Using StaticString -- Creating, concatenating, string interpolating, and printing String objects -- Using `.utf8`, `.utf16`, and `.unicodeScalars` views of strings, including their .count property, using them as dictionary keys - -Manually linking `libswiftUnicodeDataTables.a` is required for several reasons, including acknowledging that the data tables are desirable: Since they have a non-negligible size, it's useful to be aware that you are using them. - -## Conditionalizing compilation for Embedded Swift - -It's often useful to have source code be compilable under both regular Swift and Embedded Swift. The following syntax is available for that (but note that as the rest of Embedded Swift, it's experimental, subject to change and not considered source stable): - -```swift -func sayHello() { - #if hasFeature(Embedded) - print("I'm Embedded Swift") - #else - print("I'm regular Swift") - #endif -} -``` - -Additionally, you can also use an attribute (also experimental, and not source stable) to make entire functions, types and other declarations unavailable in Embedded Swift. This can be particularly useful to explicitly mark your own code (and also entire types and conformances) that relies on features unavailable in Embedded Swift, e.g. the Any type or Codable -- it is explicitly allowed to use those in unavailable contexts: - -```swift -@_unavailableInEmbedded -func useAny(_: Any) { ... } - -@_unavailableInEmbedded -extension MyStruct: Codable { - ... -} -``` - -## Embedded Swift is a subset of Swift - -Embedded Swift is a subset of the Swift language, and some features are not available in Embedded Swift, however features are available, including: Generics, protocols, enums with associated values, tuples, optionals, classes (instances are allocated on the heap and refcounted just like in regular Swift), inheritance, runtime polymorphism, arrays (heap-allocated copy-on-write just like in regular Swift) and many more. - -Features that are not available: - -- **Not available**: Runtime reflection (`Mirror` APIs). -- **Not available**: Values of protocol types ("existentials"), unless the protocol is restricted to be class-bound (derived from AnyObject). E.g. `let a: Hashable = ...` is not allowed. `Any` is also not allowed. -- **Not available**: Metatypes, e.g. `let t = SomeClass.Type` or `type(of: value)` are not allowed. -- **Not available**: Printing and stringification of arbitrary types (achieved via reflection in desktop Swift). -- **Not available yet (under development)**: Swift Concurrency. - -For a more complete list of supported features in Embedded Swift, see [Embedded Swift -- Status](EmbeddedSwiftStatus.md). - -## Libraries and modules in Embedded Swift - -Traditional library build and use model of Swift is that library code is compiled into a .swiftmodule, containing the interfaces, and a compiled library with binary code, either a .a static library or a .dylib/.so dynamic library. A client's build then uses the .swiftmodule at compile-time, and the static/dynamic library at link-time. - -The library model in Embedded Swift works slightly differently: All Swift source code of a library is promoted into being inlineable and visible to client builds (this is necessary for generic code, and beneficial for optimizations for non-generic code), and ends up serialized into the .swiftmodule, the interface of the library. Therefore, the compiled code of a library is never needed, and doesn't even need to be produced. For example: - -```bash -# Build the library, only as a .swiftmomodule. Notice that we never build the .o or .a for the library. -$ swiftc -target -enable-experimental-feature Embedded -wmo \ - a.swift b.swift -module-name MyLibrary -emit-module -emit-module-path ./MyLibrary.swiftmodule - -# Build the client, "-I ." add the current directory to the module search path list -$ swiftc -target -enable-experimental-feature Embedded -wmo \ - client.swift -I . -c -o client.o -``` - -The Embedded Swift standard library is distributed in the toolchain the same way: It's strictly a .swiftmodule without any compiled code present anywhere. All the compiling into machine code is performed as part of the client's build. This has the major benefit that the client's build can provide additional ABI and ISA defining flags, such as the above-mentioned `-mfloat-abi`, `-fshort-enums`, `-mcpu`, `-march` flags, and these flags in the client's build will apply to all the library code (including standard library code) as well. - -## Allocating and non-allocating Embedded Swift mode - -Embedded Swift does allow instantiating and using reference types (classes) which are refcounted objects allocated on the heap. A common case of needing those is for dynamic containers like arrays and sets (they use dynamically-sized heap-allocated class instances as their storage). There is only a handful of Swift language features that cause allocations: - -- creating class instances, -- escaping a closure that captures local variables, -- creating an indirect enum case with a payload referencing the enum itself -- explicitly calling allocation APIs (e.g. `UnsafeMutablePointer.allocate()`). - -Outside of those cases, Embedded Swift does not perform allocations or cause heap usage. - -Some embedded platforms don't have and/or don't want *any heap allocations whatsoever* and don't provide a heap at all. The `-no-allocations` compiler flag can be used to match that, which will cause the compiler to produce an error at compile time when creating class instances or calling allocation APIs. - -```bash -$ cat test.swift -let p = UnsafeMutablePointer.allocate(capacity: 10) -$ swiftc test.swift -enable-experimental-feature Embedded -wmo -no-allocations -test.swift:1:37: error: cannot use allocating operation in -no-allocations mode -``` - -## External dependencies - -Embedded Swift minimizes external dependencies (i.e. functions that need to be available at link-time), but they still exist. There are generally two categories of dependencies: (1) functions that the Swift standard library or Embedded Swift runtime need to call, and (2) functions/symbols that are implicitly added by LLVM and the compiler pipeline. - -For (1), external dependencies are only used based on actual usage of the program under compilation: - -- instantiating a class, or using UnsafeMutablePointer.allocate() - - dependency: `int posix_memalign(void **, size_t, size_t);` - - dependency: `void free(void *);` -- using print() - - dependency: `int putchar(int);` -- using Hashable, Set, Dictionary, or random-number generating APIs - - dependency: `void arc4random_buf(void *, size_t);` - -For (2), external dependencies are also triggered by specific code needing them, but they are somewhat lower-level patterns where it might not be obvious that such patterns should cause external dependencies: - -- **basic memory copying and zeroing functions** - - usage added for a variety of reasons (e.g. using structs on the stack) - - dependency: `void *memset(void *, int, size_t);` - - dependency: `void *memcpy(void *, const void *, size_t);` -- **stack protectors** (aka stack cookies or stack canaries) - - dependency: `void *__stack_chk_guard;` - - dependency: `void __stack_chk_fail(void);` - - stack protectors can be disabled with `-disable-stack-protector` swiftc flag -- **atomics intrinsics** - - on CPU architectures that don't have direct load-acquire/store-release support in the ISA, LLVM calls helper functions for atomic operations - - needed by refcounting in the Embedded Swift runtime (so any class usage will trigger this dependency) - - also needed when using atomics from the Synchronization module -- **multiplication/division/modulo intrinsics** - - on CPU architectures that don't have direct support for the math operations in the ISA - - dependency (on Mach-O): `__divti3` - - dependency (on Mach-O): `__modti3` - - dependency (with EABI): `__aeabi_ldivmod` - -The user and/or the platform (via basic libraries like libc or compiler builtins) is expected to provide these well-known APIs. +Embedded Swift documentation has moved to: [Swift.org](https://docs.swift.org/embedded/documentation/embedded). diff --git a/docs/Generics/README.md b/docs/Generics/README.md index 8f51b403315c8..c3eeb3190fd11 100644 --- a/docs/Generics/README.md +++ b/docs/Generics/README.md @@ -48,7 +48,7 @@ pdflatex generics ## Reading the PDF -The book makes use of internal hyperlinks so it is is best to use PDF reader with support for PDF bookmarks and back/forward history: +The book makes use of internal hyperlinks so it is best to use PDF reader with support for PDF bookmarks and back/forward history: - Preview.app on macOS fits the bill; you can add Back/Forward buttons to the toolbar with **View** > **Customize Toolbar**. - [Skim.app](https://skim-app.sourceforge.io) is a BSD-licensed open source PDF reader for macOS. diff --git a/docs/Generics/chapters/archetypes.tex b/docs/Generics/chapters/archetypes.tex index ff8a3f50de3a6..b74d266ecb758 100644 --- a/docs/Generics/chapters/archetypes.tex +++ b/docs/Generics/chapters/archetypes.tex @@ -679,7 +679,7 @@ \section{The Archetype Builder}\label{archetype builder} This kind of data structure first appeared in the early history of compilers in the implementation of ``\texttt{EQUIV} statements'' for declaring that two symbols ought to share a storage location. These statements thus define an equivalence relation on symbols, and the performance gains from a careful implementation were noted in~\cite{improvedequivalence}. A survey of later techniques appears in~\cite{unionfindsurvey}. \paragraph{Lazy expansion.} -From a theoretical point of view, the archetype builder's approach amounts to exhausive enumeration of all \index{derived requiremen!enumerationt}derived requirements and \index{valid type parameter!enumeration}valid type parameters of a generic signature, made slightly more efficient by the choice of data structure (the asymmetry in the handling of member types in \AlgRef{archetype builder merge} means we skip parts of the search space that would yield nothing new). +From a theoretical point of view, the archetype builder's approach amounts to exhaustive enumeration of all \index{derived requirement!enumeration}derived requirements and \index{valid type parameter!enumeration}valid type parameters of a generic signature, made slightly more efficient by the choice of data structure (the asymmetry in the handling of member types in \AlgRef{archetype builder merge} means we skip parts of the search space that would yield nothing new). The eager expansion model survived the introduction of protocol \texttt{where} clauses in Swift~4 \cite{se0142}, and thus associated requirements, with only relatively minor changes. The introduction of recursive conformances in Swift~4.1~\cite{se0157} necessitated a larger overhaul. Once the type parameter graph becomes infinite, the eager conformance requirement expansion of \AlgRef{archetype builder expand} no longer makes sense. The \texttt{ArchetypeBuilder} was renamed to the \IndexDefinition{GenericSignatureBuilder@\texttt{GenericSignatureBuilder}}\texttt{GenericSignatureBuilder} as part of a re-design where the recursive expansion was now performed as needed, within the lookup of \AlgRef{archetype builder lookup} itself \cite{implrecursive}. diff --git a/docs/Generics/chapters/building-generic-signatures.tex b/docs/Generics/chapters/building-generic-signatures.tex index 500e43e15f2e0..77aa48ca5db30 100644 --- a/docs/Generics/chapters/building-generic-signatures.tex +++ b/docs/Generics/chapters/building-generic-signatures.tex @@ -768,7 +768,7 @@ \section{Well-Formed Requirements}\label{generic signature validity} \ReflexStep{2}{\rT.Indices}{3} \end{gather*} -We now state the general result. We need this for the proof of \ThmRef{valid theorem}, and also later in \SecRef{conformance paths exist}, when we show that every derived conformance requirement has a derivation of a certrain form. +We now state the general result. We need this for the proof of \ThmRef{valid theorem}, and also later in \SecRef{conformance paths exist}, when we show that every derived conformance requirement has a derivation of a certain form. \begin{lemma}[Formal substitution]\label{subst lemma} Let $G$ be an arbitrary generic signature. Suppose that $G\vdash\tT$ and $G\vdash\TP$ for some type parameter \tT\ and protocol \tP. Then if we take a valid type parameter or derived requirement of~$\GP$ and replace \tSelf\ with \tT\ throughout, we get a valid type parameter or derived requirement of~$G$, just rooted in \tT. That is: @@ -1312,7 +1312,7 @@ \section{Requirement Minimization}\label{minimal requirements} We now state the general definition. \begin{definition}\label{conflicting req def} -Let $G$ be a \index{well-formed generic signature}well-formed generic signature. If $G$ has a pair of \index{derived requirement!conflicts}derived requirements $R_1$~and~$R_2$ where $R_1\otimes\Sigma$ and $R_2\otimes\Sigma$ cannot both be \index{satisfied requirement!conflicts}satisfied by the same substitution map~$\Sigma$, then $R_1$~and~$R_2$ define a pair of \IndexDefinition{conflicting requirement}\emph{confliciting requirements}. A generic signature $G$ is \emph{conflict-free} if it does not have any pairs of conflicting requirements. The pairs of derived requirements that can lead to conflicts are enumerated below: +Let $G$ be a \index{well-formed generic signature}well-formed generic signature. If $G$ has a pair of \index{derived requirement!conflicts}derived requirements $R_1$~and~$R_2$ where $R_1\otimes\Sigma$ and $R_2\otimes\Sigma$ cannot both be \index{satisfied requirement!conflicts}satisfied by the same substitution map~$\Sigma$, then $R_1$~and~$R_2$ define a pair of \IndexDefinition{conflicting requirement}\emph{conflicting requirements}. A generic signature $G$ is \emph{conflict-free} if it does not have any pairs of conflicting requirements. The pairs of derived requirements that can lead to conflicts are enumerated below: \begin{enumerate} \item For two concrete \index{same-type requirement!conflicts}same-type requirements $\SameReq{T}{$\tX_1$}$ and $\SameReq{T}{$\tX_2$}$, we desugar the ``combined'' requirement $\SameReq{$\tX_1$}{$\tX_2$}$, as we already saw. Here and every remaining case below, desugaring will either detect a conflict, or produce a simpler list of requirements that can replace one of the two original requirements. \item For a concrete same-type requirement $\TX$ and superclass requirement $\TC$, we desugar $\ConfReq{X}{C}$, which can be satisfied only if~\tX\ is a class type that is also a subclass of~\tC. diff --git a/docs/Generics/chapters/completion.tex b/docs/Generics/chapters/completion.tex index e08adf790db4b..f0a77ef02e3f4 100644 --- a/docs/Generics/chapters/completion.tex +++ b/docs/Generics/chapters/completion.tex @@ -387,7 +387,7 @@ \chapter{Completion}\label{completion} \end{enumerate} \end{algorithm} -We can now describe the main loop of the Knuth-Bendix completion procedure, which repeatedly finds and resolves critical pairs until no more non-trivial critical pairs remain. This process might not terminate, and we might find ourselves discovering new critical pairs and adding new rules to resolve them, forever. To prevent an infinite loop in the case of failure, we implement a termination check; if we think we've done too much work already, we give up on constructing a covergent rewriting system. We already mentioned the \textbf{left-simplified}, \textbf{right-simplified}, and \textbf{substitution-simplified} flags a few times; they are set by the rule simplification passes, with the first two described in \SecRef{rule reduction} and the third one in \SecRef{subst simplification}. These passes are invoked at appropriate times in the main loop below. +We can now describe the main loop of the Knuth-Bendix completion procedure, which repeatedly finds and resolves critical pairs until no more non-trivial critical pairs remain. This process might not terminate, and we might find ourselves discovering new critical pairs and adding new rules to resolve them, forever. To prevent an infinite loop in the case of failure, we implement a termination check; if we think we've done too much work already, we give up on constructing a convergent rewriting system. We already mentioned the \textbf{left-simplified}, \textbf{right-simplified}, and \textbf{substitution-simplified} flags a few times; they are set by the rule simplification passes, with the first two described in \SecRef{rule reduction} and the third one in \SecRef{subst simplification}. These passes are invoked at appropriate times in the main loop below. \begin{algorithm}[Knuth-Bendix completion procedure]\label{knuthbendix} Takes a list of rewrite rules as input. Records new rewrite rules, as well as rewrite loops, and returns success or failure. On success, the rewrite rules define a convergent rewriting system. Failure indicates we got a non-orientable rewrite rule, or we triggered the termination check. \begin{enumerate} @@ -1233,7 +1233,7 @@ \section{More Critical Pairs}\label{more critical pairs} {\assocsym{S}{C}\cdot(\ProtoConfInv{\assocsym{S}{C}}{S})} \end{center} -If the identity conformance rule had not been part of that initial set, this last critical pair would \emph{define} the identity conformance rule, and we would obtain the same rewrite system in the end. We don't need to explicitly add the identity conformance rule, after all. There is a practical consideration though. By making this rule part of the initial set, and cruicially, marking it \index{permanent rule}\textbf{permanent}, we remove it from consideration in the rewrite system minimization algorithm. This cuts out a lot of unnecessary work. +If the identity conformance rule had not been part of that initial set, this last critical pair would \emph{define} the identity conformance rule, and we would obtain the same rewrite system in the end. We don't need to explicitly add the identity conformance rule, after all. There is a practical consideration though. By making this rule part of the initial set, and crucially, marking it \index{permanent rule}\textbf{permanent}, we remove it from consideration in the rewrite system minimization algorithm. This cuts out a lot of unnecessary work. \smallskip @@ -1523,7 +1523,7 @@ \section{Recursive Conformances}\label{recursive conformances redux} \end{tabular} \end{center} -\ListingRef{rewrite system q} shows the convegent rewrite system for \tQ, which imports rules from protocol~\texttt{N}. Completion discovers the following critical pairs; we will be content to just summarize because they are similar to previous examples: +\ListingRef{rewrite system q} shows the convergent rewrite system for \tQ, which imports rules from protocol~\texttt{N}. Completion discovers the following critical pairs; we will be content to just summarize because they are similar to previous examples: \begin{itemize} \item Rule (6) overlaps with (7) on $\pQ\cdot\pQ\cdot\nA$. We define rule (\CRule{10}) (this general principle was established in \ExRef{proto assoc rule}). \item Rule (8) overlaps with (2) on $\pQ\cdot\protosym{N}\cdot\nA$. We define rule (\CRule{11}) (\ExRef{protocol inheritance completion example}). diff --git a/docs/Generics/chapters/conformance-paths.tex b/docs/Generics/chapters/conformance-paths.tex index c5fa113c31c6a..a6711284e8f9d 100644 --- a/docs/Generics/chapters/conformance-paths.tex +++ b/docs/Generics/chapters/conformance-paths.tex @@ -143,7 +143,7 @@ \chapter{Conformance Paths}\label{conformance paths} \qquad {} = \Proto{Sequence} \otimes \texttt{Substring}\\ \qquad {} = \SubstringSequence \end{gather*} -However, we wish to recover $\SubstringSequence$ from $\Sigma$ alone, without resorting to a \index{global conformance lookup}\emph{global} conformance lookup, as we did in the above calculation. We don't have a lot of options. In fact, pretty much the \emph{only} thing local confomance lookup can do is take one of the \index{root conformance}root conformances stored in the substitution map, and proceed to apply \index{associated conformance projection}associated conformance projection operations. +However, we wish to recover $\SubstringSequence$ from $\Sigma$ alone, without resorting to a \index{global conformance lookup}\emph{global} conformance lookup, as we did in the above calculation. We don't have a lot of options. In fact, pretty much the \emph{only} thing local conformance lookup can do is take one of the \index{root conformance}root conformances stored in the substitution map, and proceed to apply \index{associated conformance projection}associated conformance projection operations. Our generic signature $G$ only has one explicit conformance requirement, so we start with the lone root conformance of $\Sigma$: \[ diff --git a/docs/Generics/chapters/conformances.tex b/docs/Generics/chapters/conformances.tex index d92bcd36e8977..8a015716b03e5 100644 --- a/docs/Generics/chapters/conformances.tex +++ b/docs/Generics/chapters/conformances.tex @@ -64,7 +64,7 @@ \chapter{Conformances}\label{conformances} \draw [arrow] (SquareShape) -- (Square); \end{tikzpicture} \end{center} -We denote this inherited conformance by $\ConfReq{Square}{Shape}$. It behaves identically to the superclass conformance $\ConfReq{Polygon}{Shape}$, except if ask for its \index{conforming type!inherited conformance}conforming type, we get back \texttt{Square} instead of \texttt{Polygon}. In \ExRef{inherited specialized conf}, we will see that more complex behavors can manifest when the superclass declaration has generic parameters. +We denote this inherited conformance by $\ConfReq{Square}{Shape}$. It behaves identically to the superclass conformance $\ConfReq{Polygon}{Shape}$, except if ask for its \index{conforming type!inherited conformance}conforming type, we get back \texttt{Square} instead of \texttt{Polygon}. In \ExRef{inherited specialized conf}, we will see that more complex behaviors can manifest when the superclass declaration has generic parameters. \section{Conformance Lookup}\label{conformance lookup} @@ -320,7 +320,7 @@ \section{Type Witnesses}\label{type witnesses} func play(_: String) {} } \end{Verbatim} -Associated type inference \index{synthesized declaration}synthesizes a type alias member for each inferred associated type, so we can refer to \texttt{Cat.Toy} elsewhere in the program, as if we explictly declared the type alias \texttt{Toy} as a member of \texttt{Cat}. Now, consider \texttt{Dog}, which witnesses \texttt{Pet}'s associated type with a generic parameter named \texttt{Toy}. This is Case~3: +Associated type inference \index{synthesized declaration}synthesizes a type alias member for each inferred associated type, so we can refer to \texttt{Cat.Toy} elsewhere in the program, as if we explicitly declared the type alias \texttt{Toy} as a member of \texttt{Cat}. Now, consider \texttt{Dog}, which witnesses \texttt{Pet}'s associated type with a generic parameter named \texttt{Toy}. This is Case~3: \begin{Verbatim} struct Dog: Pet { // synthesized: typealias Toy = Toy diff --git a/docs/Generics/chapters/existential-types.tex b/docs/Generics/chapters/existential-types.tex index 8e3bc68344752..1b22ce698383e 100644 --- a/docs/Generics/chapters/existential-types.tex +++ b/docs/Generics/chapters/existential-types.tex @@ -106,7 +106,7 @@ Recall from \ChapRef{genericenv} that there are three kinds of generic environments. We've seen primary generic environments, which are associated with generic declarations. We also saw opaque generic environments, which are instantiated from an opaque return declaration and substitution map, in \SecRef{opaquearchetype}. Now, it's time to introduce the third kind, the opened generic environment. An opened generic environment is created from an opened existential signature of the first kind (with no parent generic signature). The archetypes of an opened generic environment are \emph{opened archetypes}. \index{call expression} -When the expression type checker encounters a call expression where an argument of existential type is passed to a parameter of generic parameter type, the existential value is \emph{opened}, projecting the value and assigning it a new opened archetype from a fresh opened generic environment. The call expression is rewritten by wrapping the entire call is wrapped in an \texttt{OpenExistentialExpr}, which stores two sub-expressions. The first sub-expression is the original call argument, which evaluates to the value of existential type. The payload value and opened archetype is scoped to the second sub-expression, which consumes the payload value. The call argument is replaced with a \texttt{OpaqueValueExpr}, which has the opened archetype type. The opened archetype also becomes the replacement type for the generic parameter in the call's substitution map. +When the expression type checker encounters a call expression where an argument of existential type is passed to a parameter of generic parameter type, the existential value is \emph{opened}, projecting the value and assigning it a new opened archetype from a fresh opened generic environment. The call expression is rewritten by wrapping the entire call in an \texttt{OpenExistentialExpr}, which stores two sub-expressions. The first sub-expression is the original call argument, which evaluates to the value of existential type. The payload value and opened archetype is scoped to the second sub-expression, which consumes the payload value. The call argument is replaced with a \texttt{OpaqueValueExpr}, which has the opened archetype type. The opened archetype also becomes the replacement type for the generic parameter in the call's substitution map. For example, if \texttt{animal} is a value of type \texttt{any Animal}, the expression \texttt{animal.eat()} calling a protocol method looks like this before opening: \begin{quote} @@ -463,8 +463,8 @@ func careForAnimals(_ animals: [any Animal]) { for animal in animals { - careForAnimal(animal) // existential opened here in Swift 5.7; - // type check error in Swift 5.6. + petAnimal(animal) // existential opened here in Swift 5.7; + // type check error in Swift 5.6. } } \end{Verbatim} diff --git a/docs/Generics/generics.tex b/docs/Generics/generics.tex index 4522c28b93df3..1c6fe88834fe9 100644 --- a/docs/Generics/generics.tex +++ b/docs/Generics/generics.tex @@ -241,7 +241,7 @@ \\*(Cited on pages #2.)% \fi } -% Request evalator requests +% Request evaluator requests \newcommand{\Request}[1]{\textbf{#1}} % Generic parameters diff --git a/docs/HowToGuides/GettingStarted.md b/docs/HowToGuides/GettingStarted.md index 79cf5f20ab090..1de0dcf0eb249 100644 --- a/docs/HowToGuides/GettingStarted.md +++ b/docs/HowToGuides/GettingStarted.md @@ -142,10 +142,10 @@ toolchain as a one-off, there are a couple of differences: 1. Install Xcode. The minimum required version is specified in the node information on , may change frequently, and is often a beta release. -1. Install [CMake][], [Ninja][] and [Sccache][]: +1. Install [Ninja][] and [Sccache][]: - Via [Homebrew][] (recommended): ```sh - brew install cmake ninja sccache + brew install ninja sccache ``` - Via [Homebrew Bundle][]: ```sh @@ -153,7 +153,6 @@ toolchain as a one-off, there are a couple of differences: ``` [Xcode]: https://developer.apple.com/xcode/resources/ -[CMake]: https://cmake.org [Ninja]: https://ninja-build.org [Homebrew]: https://brew.sh/ [Homebrew Bundle]: https://github.com/Homebrew/homebrew-bundle @@ -164,6 +163,7 @@ toolchain as a one-off, there are a couple of differences: * [Ubuntu 18.04](https://github.com/swiftlang/swift-docker/blob/main/swift-ci/main/ubuntu/18.04/Dockerfile) * [Ubuntu 20.04](https://github.com/swiftlang/swift-docker/blob/main/swift-ci/main/ubuntu/20.04/Dockerfile) * [Ubuntu 22.04](https://github.com/swiftlang/swift-docker/blob/main/swift-ci/main/ubuntu/22.04/Dockerfile) + * [Ubuntu 24.04](https://github.com/swiftlang/swift-docker/blob/main/swift-ci/main/ubuntu/24.04/Dockerfile) * [CentOS 7](https://github.com/swiftlang/swift-docker/blob/main/swift-ci/main/centos/7/Dockerfile) * [Amazon Linux 2](https://github.com/swiftlang/swift-docker/blob/main/swift-ci/main/amazon-linux/2/Dockerfile) @@ -199,7 +199,6 @@ toolchain as a one-off, there are a couple of differences: ### Spot check dependencies -* Run `cmake --version`; this should be at least 3.19.6 (3.24.2 if you want to use Xcode for editing on macOS). * Run `python3 --version`; this should be at least 3.6. * Run `ninja --version`; check that this succeeds. * If you installed and want to use Sccache: Run `sccache --version`; check diff --git a/docs/LibraryEvolution.rst b/docs/LibraryEvolution.rst index 50505ef7bb1d1..ff79d1702c8b5 100644 --- a/docs/LibraryEvolution.rst +++ b/docs/LibraryEvolution.rst @@ -365,8 +365,7 @@ the following changes are permitted: an existing property (unless the struct is marked ``@frozen``; see below). This is effectively the same as modifying the body of a setter. - Removing any non-ABI-public members, including stored properties. -- Adding a conformance to an ABI-public protocol *that was introduced in the - same release* (see below). +- Adding a conformance to an ABI-public protocol (see below about availability). - Adding or removing a conformance to a non-ABI-public protocol. - Adding ``@dynamicCallable`` to the struct. @@ -382,17 +381,9 @@ target for the library is an error. It is not safe to add or remove ``mutating`` or ``nonmutating`` from a member or accessor within a struct. -If a conformance is added to a type in version 1.1 of a library, it's important -that it isn't accessed in version 1.0. This means that it is only safe to add -new conformances to ABI-public protocols when the protocol is introduced, and -not after. If the protocol comes from a separate module, there is no safe way -to conform to it. - -.. admonition:: TODO - - Coming up with a way to do this, either with availability annotations for - protocol conformances or a way to emit a fallback copy of the conformance - for clients on older library versions to use, is highly desired. +When introducing a new conformance of an existing type to an existing protocol, +it is important to annotate the conformance with availability. This is achieved +by declaring the conformance on an extension and annotating the extension. Methods and Initializers diff --git a/docs/ReferenceGuides/LifetimeAnnotation.md b/docs/ReferenceGuides/LifetimeAnnotation.md new file mode 100644 index 0000000000000..5e399d268b8e3 --- /dev/null +++ b/docs/ReferenceGuides/LifetimeAnnotation.md @@ -0,0 +1,184 @@ +# @_lifetime annotation + +## Introduction + +`@_lifetime` annotations are now available under `-enable-experimental-features Lifetimes`. The feature proposal is documented in [PR: Lifetime dependencies #2750](https://github.com/swiftlang/swift-evolution/pull/2750). + +To summarize the basic syntax: functions require a `@_lifetime` annotation when they return a non-Escapable type, either as the function result, or as an `inout` parameter. The annotation syntax pattern is `@_lifetime(target: source)` where `target` is a function output and `source` is an input. If `target:` is omitted, then it is assumed to be the function's result. + +` ::= [borrow|&|copy]` + +`borrow` creates a new borrow scope that guarantees exclusive read access to the caller's `source` argument over all uses of `target`. + +`&` creates a new borrow scope that guarantees exclusive write access to the caller's `source` argument over all uses of `target`. + +`copy` copies the lifetime constraints on the caller's `source` argument to `target`. + +The `@lifetime` annotation is enforced both in the body of the function and at each call site. For both `borrow` and `&` scoped dependencies, the function's implementation guarantees that `target` is valid as long as `source` is alive, and each caller of the function guarantees that `source` will outlive `target`. For `copy` dependencies, the function's implementation guarantees that all constraints on `target` are copied from `source`, and the caller propagates all lifetime constraints on `source` to all uses of `target`. + +## Default lifetimes + +The Swift 6.2 compiler provided default `@_lifetime` behavior whenever it can do so without ambiguity. Often, despite ambiguity, an "obvious" default exists, but we wanted to introduce defaults slowly after developers have enough experience to inform discussion about them. This document tracks the current state of the implementation as it progresses from the original 6.2 implementation. Corresponding tests are in `test/Sema/lifetime_depend_infer.swift`; searching for "DEFAULT:" highlights the rules defined below... + +### Single parameter default rule + +Given a function or method that returns a non-Escapable result: + +- Default to `@_lifetime( a)` for a `~Escapable` result on functions with a single parameter `a`. + +- Default to `@_lifetime( self)` for a `~Escapable` result on methods with no parameters. + +| Type of parameter | default | +| (`a` or `self`) | lifetime dependency | +| ----------------- | ------------------------------ | +| `Escapable` | `@_lifetime(borrow param)`[^1] | +| `inout Escapable` | `@_lifetime(¶m)`[^1] | +| `~Escapable` | none[^2] | + +[^1]: When the parameter is `BitwiseCopyable`, such as an integer or unsafe pointer, the single parameter default rule applies to function parameters but not to the implicit `self` parameter. Depending on a `BitwiseCopyable` value is a convenience for APIs that construct span-like values from an `UnsafePointer` passed as an argument. This creates a dependency on a local copy of the pointer variable with subtle semantics. User-defined `BitwiseCopyable` structs should generally avoid such subtle lifetime dependencies. If needed, the author of the data type should explicitly opt into them. + +[^2]: When the single parameter is also `~Escapable`, the result must depend on it, but the dependency may either be scoped (`borrow` or `&`) or it may be copied (`copy`). `copy` is the obvious choice when the parameter and result are the same type, but it is not always correct. Furthermore, a lifetime dependency can only be copied from a generic type when result as the same generic type. This case is therefore handled by same-type default lifetime (discussed below) rather than as a default `@_lifetime` rule. + +Examples: + +```swift +struct A: Escapable { + let obj: AnyObject // ~BitwiseCopyable +} +struct NE: ~Escapable {...} + +/* DEFAULT: @_lifetime(borrow a) */ +func oneParam_NEResult(a: A) -> NE + +/* DEFAULT: @_lifetime(&a) */ +func oneInoutParam_NEResult(a: inout A) -> NE + +extension A /* Self: Escapable */ { + /* DEFAULT: @_lifetime(borrow self) */ + func noParam_NEResult() -> NE + + /* DEFAULT: @_lifetime(&self) */ + mutating func mutating_noParam_NEResult() -> NE +} +``` + +### Implicit initializer and setter defaults + +An implicit setter of a `~Escapable` stored property defaults to `@_lifetime(self: copy self, copy newValue)`. This is always correct because the setter simply assigns the stored property to the newValue. Assigning a `~Escapable` variable copies the lifetime dependency. + +Similarly, an implicit initializer of a non-Escapable struct defaults to `@_lifetime(self: copy arg)` if all of the initializer arguments are `~Escapable`. This is equivalent to assigning each `~Escapable` stored property. If, however, any initializer arguments are `Escapable`, then no default lifetime is provided unless it is the sole argument, in which case the single parameter rule applies. + +### `inout` parameter default rule + +- Default to `@_lifetime(a: copy a)` for all `inout` parameters where `a` is `~Escapable`. + +- Default to `@_lifetime(self: copy self)` on `mutating` methods where `self` is `~Escapable`. + +#### Examples + +```swift +struct A: Escapable { + let obj: AnyObject // ~BitwiseCopyable +} +struct NE: ~Escapable {...} + +/* DEFAULT: @_lifetime(a: copy a) */ +func inoutNEParam_void(_: inout NE) -> () + +/* DEFAULT: @_lifetime(a: copy a) */ +/* DEFAULT: @_lifetime(b: copy b) */ +func inoutNEParam_inoutNEParam_void(a: inout NE, b: inout NE) -> () + +/* DEFAULT: @_lifetime(ne: copy ne) */ +@_lifetime(&ne) +func inoutNEParam_NEResult(ne: inout NE) -> NE + +extension A /* Self: Escapable */ { + /* DEFAULT: @_lifetime(ne: copy NE) */ + func inoutNEParam_void(a: inout ) -> () + + /* DEFAULT: @_lifetime(ne: copy NE) */ + mutating func mutating_inoutNEParam_void() -> () + + /* DEFAULT: @_lifetime(ne: copy NE) */ + @_lifetime(&self) + func inoutNEParam_NEResult(ne: inout NE) -> NE +} + +extension NE /* Self: ~Escapable */ { + /* DEFAULT: @_lifetime(self: copy self) */ + mutating func mutating_noParam_void() -> () + + /* DEFAULT: @_lifetime(self: copy self) */ + mutating func mutating_oneParam_void(_: NE) -> () + + /* DEFAULT: @_lifetime(self: copy self) */ + /* DEFAULT: @_lifetime(ne: copy ne) */ + mutating func mutating_inoutParam_void(ne: inout NE) -> () + + /* DEFAULT: @_lifetime(self: copy self) */ + @_lifetime(&self) + mutating func mutating_noParam_NEResult() -> NE +} +``` + +## Same-type default lifetime (unimplemented) + +Given a function declaration: + +`func foo(..., a: A, ...) -> R { ... }` + +Where `R: ~Escapable` and `A == R`, default to `@_lifetime(copy a)`. +For methods, the same rule applies to implicit `Self` parameter. + +This handles the obvious cases in which both the parameter and result are `~Escapable`. For example: + +```swift +extension Span { + /* DEFAULT: @_lifetime(copy self) */ + func extracting(droppingLast k: Int) -> Self { ... } +} +``` + +### Generic same-type default lifetime (unimplemented) + +The same-type default lifetime rule described above is convenient for nominal types but essential for generic type parameters. + +Given a generic function declaration: + +`func foo(..., a: A, ...) -> R { ... }` + +The same-type default lifetime rule applies to the types in the function declaration's generic context just as it did for nominal types in the previous example. So, again, if type resolution determines `R: ~Escapable` and `A == R`, then `@_lifetime(copy a)` will be default. + +Unlike nominal types, the programmer is not allowed to explicitly declare a lifetime dependency, `@_lifetime(copy +a)`, unless the argument and result types are equivalent (`A == R`). Copying a lifetime dependency from one value to another requires that both values are non-Escapable. Generic types are conditionally non-Escapable (their lifetime dependencies are type-erased), so type equivalence is the only way to ensure that both values are non-Escapable under the same conditions. + +Here we see how same-type lifetime requirement applies to type substitution and associated types: + +```swift +protocol P { + associatedtype T: ~Escapable +} + +protocol Q { + associatedtype U: ~Escapable +} + +struct S { + /* OK: @_lifetime(copy a) is valid and default */ + func foo(a: A.T) -> B.U where A.T == B.U +} +``` + +Note that lifetime dependencies are resolved at function declaration time, which determines the function's type. The generic context at the point of function invocation is not considered. For example, the following declaration of `foo` is invalid, because it's argument and results types don't match at the point of declaraion, even though the argument and result do have the same type when invoked inside `bar`: + +```swift +struct S { + static func foo(a: T) -> U // ERROR: missing lifetime dependency +} + +/* OK: @_lifetime(copy a) is valid and default */ +func bar(a: T) -> T { + S.foo(a: a) // The same-type rule is satisfied in this context, but 'foo's declaration is invalid. +} +``` diff --git a/docs/ReferenceGuides/UnderscoredAttributes.md b/docs/ReferenceGuides/UnderscoredAttributes.md index 07d66bc5013b9..a9db7c7ea998e 100644 --- a/docs/ReferenceGuides/UnderscoredAttributes.md +++ b/docs/ReferenceGuides/UnderscoredAttributes.md @@ -600,6 +600,43 @@ inherit the actor context (i.e. what actor it should be run on) based on the declaration site of the closure rather than be non-Sendable. This does not do anything if the closure is synchronous. +This works with global actors as expected: + +```swift +@MainActor +func test() { + Task { /* main actor isolated */ } +} +``` + +However, for the inference to work with instance actors (i.e. `isolated` parameters), +the closure must capture the isolated parameter explicitly: + +```swift +func test(actor: isolated (any Actor)) { + Task { /* non isolated */ } // !!! +} + +func test(actor: isolated (any Actor)) { + Task { // @_inheritActorContext + _ = actor // 'actor'-isolated + } +} +``` + +The attribute takes an optional modifier '`always`', which changes this behavior +and *always* captures the enclosing isolated context, rather than forcing developers +to perform the explicit capture themselfes: + +```swift +func test(actor: isolated (any Actor)) { + Task.immediate { // @_inheritActorContext(always) + // 'actor'-isolated! + // (without having to capture 'actor explicitly') + } +} +``` + DISCUSSION: The reason why this does nothing when the closure is synchronous is since it does not have the ability to hop to the appropriate executor before it is run, so we may create concurrency errors. diff --git a/docs/RequestEvaluator.md b/docs/RequestEvaluator.md index 01e09b4309294..e1ce190e9bbfb 100644 --- a/docs/RequestEvaluator.md +++ b/docs/RequestEvaluator.md @@ -66,9 +66,8 @@ To define a request as a dependency source, it must implement an accessor for th ## Open Projects -The request-evaluator is relatively new to the Swift compiler, having been introduced in mid-2018. There are a number of improvements that can be made to the evaluator itself and how it is used in the compiler: +There are a number of improvements that can be made to the evaluator itself and how it is used in the compiler: -* The evaluator uses a `DenseMap` as its cache: we can almost certainly do better with per-request-kind caches that don't depend on so much type erasure. * Explore how best to cache data structures in the evaluator. For example, caching `std::vector` or `std::string` implies that we'll make copies of the underlying data structure each time we access the data. Could we automatically intern the data into an allocation arena owned by the evaluator, and vend `ArrayRef` and `StringRef` to clients instead? * Cycle diagnostics are far too complicated and produce very poor results. Consider replacing the current `diagnoseCycle`/`noteCycleStep` scheme with a single method that produces summary information (e.g., a short summary string + source location information) and provides richer diagnostics from that string. * The `isCached()` check to determine whether a specific instance of a request is worth caching may be at the wrong level, because one generally has to duplicate effort (or worse, code!) to make the decision in `isCached()`. Consider whether the `evaluator()` function could return something special to say "produce this value without caching" vs. the normal "produce this value with caching". diff --git a/docs/SIL/Instructions.md b/docs/SIL/Instructions.md index 9f9691b964f59..e4bbe28a3b072 100644 --- a/docs/SIL/Instructions.md +++ b/docs/SIL/Instructions.md @@ -832,7 +832,7 @@ load-ownership-kind ::= 'take' ``` Loads the value at address `%0` from memory. `T` must be a loadable -type. This does not affect the reference count, if any, of the loaded +type. An unqualified load does not affect the reference count, if any, of the loaded value; the value must be retained explicitly if necessary. It is undefined behavior to load from uninitialized memory or to load from an address that points to deallocated storage. @@ -840,8 +840,8 @@ address that points to deallocated storage. In OSSA the ownership kind specifies how to handle ownership: - **trivial**: the loaded value is trivial and no further action must be taken than to load the raw bits of the value -- **copy**: the loaded value is copied and the original value stays in the - memory location. +- **copy**: the loaded value is copied (e.g., retained) and the original value + stays in the memory location. - **take**: the value is _moved_ from the memory location without copying. After the `load`, the memory location remains uninitialized. @@ -1032,7 +1032,7 @@ extend_lifetime %0 : $X ``` Indicates that a value's linear lifetime extends to this point. -Inserted by OSSALifetimeCompletion(AvailabilityBoundary) in order to +Inserted by OSSACompleteLifetime(AvailabilityBoundary) in order to provide the invariant that a value is either consumed OR has an `extend_lifetime` user on all paths and furthermore that all uses are within the boundary defined by that set of instructions (the @@ -1064,43 +1064,49 @@ a sequence that also correctly destroys the current value. This instruction is only valid in Raw SIL and is rewritten as appropriate by the definitive initialization pass. -### assign_by_wrapper +### assign_or_init ``` -sil-instruction ::= 'assign_by_wrapper' sil-operand 'to' mode? sil-operand ',' 'init' sil-operand ',' 'set' sil-operand +sil-instruction ::= 'assign_or_init' mode? attached-property ',' self-or-local ',' sil-operand ',' 'value' ',' sil-operand ',' 'init' sil-operand ',' 'set' sil-operand -mode ::= '[init]' | '[assign]' | '[assign_wrapped_value]' +mode ::= '[init]' | '[assign]' +attached-property ::= '#' sil-decl-ref +self-or-local ::= 'self' | 'local' -assign_by_wrapper %0 : $S to %1 : $*T, init %2 : $F, set %3 : $G -// $S can be a value or address type -// $T must be the type of a property wrapper. -// $F must be a function type, taking $S as a single argument (or multiple arguments in case of a tuple) and returning $T -// $G must be a function type, taking $S as a single argument (or multiple arguments in case of a tuple) and without a return value +// Nominal Context: +assign_or_init #MyStruct.x, self %A, value %V, init %I, set %S +// Local Context (only emitted with compiler synthesized thunks currently): +assign_or_init #x, local %L, value %V, init %I, set %S ``` -Similar to the [assign](#assign) instruction, but the assignment is done -via a delegate. +Assigns or initializes a computed property with an attached init accessor. +This instruction is emitted during SILGen without an explicit mode. +The definitive initialization (DI) pass resolves the mode and rewrites +the instruction accordingly: -Initially the instruction is created with no mode. Once the mode is -decided (by the definitive initialization pass), the instruction is -lowered as follows: +- `[init]`: In this mode, the init accessor `%I` is called with `%V` +as an argument. +- `[assign]`: In this mode, the setter function `%S` is called with `%V` +as an argument. -If the mode is `initialization`, the function `%2` is called with `%0` -as argument. The result is stored to `%1`. In case of an address type, -`%1` is simply passed as a first out-argument to `%2`. +This instruction is only valid in Raw SIL and is rewritten as appropriate by +the DI pass. -The `assign` mode works similar to `initialization`, except that the -destination is "assigned" rather than "initialized". This means that -the existing value in the destination is destroyed before the new value -is stored. - -If the mode is `assign_wrapped_value`, the function `%3` is called with -`%0` as argument. As `%3` is a setter (e.g. for the property in the -containing nominal type), the destination address `%1` is not used in -this case. - -This instruction is only valid in Raw SIL and is rewritten as -appropriate by the definitive initialization pass. +Operand Roles: +- `attached-property`: The property being written to. For nominal contexts, this +refers to a property with an attached init accessor (e.g. `#MyStruct.x`). For local +contexts, it refers to a local variable name (e.g. `#x`). +- `self-or-local`: + - `self %A`: Refers to the instance of the type that owns the property with the + attached init accessor. + - `local %L`: Indicates the assignment is to a local variable (`%L`) rather than + a property of a nominal type. While init accessors are not currently available to be + used in local contexts in user-authored code, the compiler can synthesize an `assign_or_init` + in local contexts using an init accessor thunk in special cases. +- `value %V`: The input value passed to either the `init` or `set` function, depending on +the selected DI mode. +- `init %I`: A partially applied function implementing the property's init accessor. +- `set %S`: A partially applied function implementing the property's setter. ### mark_uninitialized @@ -2146,6 +2152,22 @@ not replace this reference with a not uniquely reference object. For details see [Copy-on-Write Representation](SIL.md#Copy-on-Write-Representation). +### end_cow_mutation_addr + +``` +sil-instruction ::= 'end_cow_mutation_addr' sil-operand + +end_cow_mutation_addr %0 : $*T +// %0 must be of an address $*T type +``` + +This instruction marks the end of mutation of an address. The address could be +an opaque archetype, a struct, tuple or enum type and the end_cow_mutation_addr +will apply to all members contained within it. +It is currently only generated in cases where we maybe deriving a MutableSpan from +`%0` since it is not possible to schedule an `end_cow_mutation` in the standard +library automatically for Array.mutableSpan etc. + ### destroy_not_escaped_closure ``` @@ -2599,11 +2621,11 @@ except: instead of its normal results. The final (in the case of `@yield_once`) or penultimate (in the case of -`@yield_once_2`) result of a `begin_apply` is a "token", a special -value which can only be used as the operand of an `end_apply` or -`abort_apply` instruction. Before this second instruction is executed, -the coroutine is said to be "suspended", and the token represents a -reference to its suspended activation record. +`@yield_once_2`) result of a `begin_apply` is a "token", a special value which +can only be used as the operand of an `end_apply`, `abort_apply`, or +`end_borrow` instruction. Before this second instruction is executed, the +coroutine is said to be "suspended", and the token represents a reference to its +suspended activation record. If the coroutine's kind `yield_once_2`, its final result is an address of a "token", representing the allocation done by the callee @@ -3451,6 +3473,20 @@ Constructs a statically initialized vector of elements. This instruction can only appear as final instruction in a global variable static initializer list. +### vector_base_addr + +``` +sil-instruction ::= 'vector_base_addr' sil-operand + +%1 = vector_base_addr %0 : $*Builtin.FixedArray +// %0 must have type $*Builtin.FixedArray +// %1 will be of the element type of the Builtin.FixedArray +``` + +Derives the address of the first element of a vector, i.e. a `Builtin.FixedArray`, +from the address of the vector itself. +Addresses of other vector elements can then be derived with `index_addr`. + ### ref_element_addr ``` diff --git a/docs/SIL/Ownership.md b/docs/SIL/Ownership.md index 4a9f768f55832..52b2b5c4603a3 100644 --- a/docs/SIL/Ownership.md +++ b/docs/SIL/Ownership.md @@ -814,6 +814,11 @@ SILValue. Instead, it constrains the lifetime of an addressable variable. Since the constraint is applied to the in-memory representation, no additional lexical SILValue is required. +The `self` function argument in a borrow accessor is not lexical. +`self` is already borrowed for the duration of the borrow accessor and the +dependency between borrowed return value and `self` will be represented in SIL, +making lexical lifetime unnecessary. + ### Deinit Barriers Deinit barriers (see Instruction.isDeinitBarrier(_:)) are instructions diff --git a/docs/SIL/SIL.md b/docs/SIL/SIL.md index 966a1adbd3303..d7cc716ca4f16 100644 --- a/docs/SIL/SIL.md +++ b/docs/SIL/SIL.md @@ -457,9 +457,10 @@ The ownership kind of a value is statically determined: ... ``` -- The ownership of most instruction results is statically defined. For example - `copy_value` always produces an owned value, whereas `begin_borrow` always - produces a guaranteed value. +- The ownership of most instruction results can be statically determined from + the instruction's kind and the offset of the value in the result tuple. For + example `copy_value` has only one result and that result is always an owned + value, whereas `begin_borrow` always produces a guaranteed value. - Forwarding instructions: some instructions work with both, owned and guaranteed ownership, and "forward" the ownership from their operand(s) to diff --git a/docs/SIL/Types.md b/docs/SIL/Types.md index d35e5af2bc143..428432aea24c6 100644 --- a/docs/SIL/Types.md +++ b/docs/SIL/Types.md @@ -431,6 +431,44 @@ in the callee. `@sil_isolated` since a function cannot be isolated to multiple actors at the same time. +- SIL functions may optionally mark a function parameter as + `@sil_implicit_leading_param`. A SIL generator places this on a parameter + that is used to represent a parameter that is implicitly generated by the + generator at a full call site. Since they can only appear at the full call + site, a `@sil_implicit_leading_param` can only appear in between the + indirect result parameters and the direct parameters. For example the + following swift code that uses `nonisolated(nonsending)`: + + ``` + nonisolated(nonsending) + func f(_ x: DirectParam) -> IndirectResult { + ... + } + ``` + + would translate to the following SIL: + + ``` + sil [ossa] @f : $@convention(thin) @async ( + @sil_isolated @sil_implicit_leading_param @guaranteed Optional, + @guaranteed DirectParam + ) -> @out IndirectResult { + bb0(%0 : $*IndirectResult, %1 : @guaranteed $Optional, %2 : @guaranteed $DirectParam): + ... use %0 ... + } + ``` + + Notice how there is an `@sil_isolated` `@sil_implicit_leading_param` + parameter that was inserted by SILGen to implicitly take in the caller's + actor of f. + + NOTE: By design SILOptimizer passes should ignore + `@sil_implicit_leading_param`. Instead, it should only be analyzed as a + normal parameter. So as an example, in f above the implicit parameter should + be treated by the optimizer just as any other isolated parameter. This is + solely SILGen using SIL as a data structure. TODO: Can this be removed by + SILGen so SILOptimizer passes cannot even see it? + ### Async Functions SIL function types may be `@async`. `@async` functions run inside async diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index b20b7d4cb1ca4..a6822a43d75c0 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -128,7 +128,7 @@ cmake -B "S:\b\1" ^ -D CMAKE_CXX_FLAGS="/GS- /Oy /Gw /Gy" ^ -D CMAKE_EXE_LINKER_FLAGS="/INCREMENTAL:NO" ^ -D CMAKE_MT=mt ^ - -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ + -D CMAKE_SHARED_LINKER_FLAGS="/INCREMENTAL:NO" ^ -D LLVM_DEFAULT_TARGET_TRIPLE=x86_64-unknown-windows-msvc ^ -D LLVM_ENABLE_PDB=YES ^ -D LLVM_EXTERNAL_CMARK_SOURCE_DIR=S:\cmark ^ diff --git a/docs/proposals/archive/MemoryAndConcurrencyModel.rst b/docs/proposals/archive/MemoryAndConcurrencyModel.rst index 9cc0d6122e057..bf924300731fc 100644 --- a/docs/proposals/archive/MemoryAndConcurrencyModel.rst +++ b/docs/proposals/archive/MemoryAndConcurrencyModel.rst @@ -11,7 +11,7 @@ Swift Memory and Concurrency Model The goal of this writeup is to provide a safe and efficient way to model, design, and implement concurrent applications in Swift. It is believed that it -will completely eliminate data races and reduce deadlocks in swift apps, and +will completely eliminate data races and reduce deadlocks in Swift apps, and will allow for important performance wins as well. This happens by eliminating shared mutable data, locks, and the need for most atomic memory accesses. The model is quite different from what traditional unix folks are used to @@ -83,7 +83,7 @@ definition. These kinds are: b.list.data = 42 // error, can't change immutable data. As part of mutable data, it is worth pointing out that mutable "global - variables" in swift are not truly global, they are local to the current actor + variables" in Swift are not truly global, they are local to the current actor (somewhat similar to "thread local storage", or perhaps to "an ivar on the actor"). Immutable global variables (like lookup tables) are simple immutable data just like today. Global variables with "static constructors / @@ -225,7 +225,7 @@ Performing synchronous operations --------------------------------- Asynchronous calls are nice and define away the possibility of deadlock, but at -some point you need to get a return value back and async programming is very +some point, you need to get a return value back and async programming is very awkward. To handle this, a 'synch' block is used. For example, the following is valid:: @@ -245,7 +245,7 @@ context. Memory Ownership Model ---------------------- -Within an actor there is a question of how ownership is handled. It's not in the +Within an actor, there is a question of how ownership is handled. It's not in the scope of this document to say what the "one true model" is, but here are a couple of interesting observations: @@ -265,7 +265,7 @@ couple of interesting observations: stop the world to do a collection. 2) actors have natural local quiescent points: when they have finished servicing a message, if their dispatch queue is empty, they go to sleep. If nothing else in the CPU needs the thread, it - would be a natural time to collect. 3) GC would be fully precise in swift, + would be a natural time to collect. 3) GC would be fully precise in Swift, unlike in ObjC, no conservative stack scanning or other hacks are needed. 4) If GC is used for mutable data, it would make sense to still use reference counting for actors themselves and especially for immutable data, meaning diff --git a/include/swift-c/DependencyScan/DependencyScan.h b/include/swift-c/DependencyScan/DependencyScan.h index b558643cbb850..988392265b27d 100644 --- a/include/swift-c/DependencyScan/DependencyScan.h +++ b/include/swift-c/DependencyScan/DependencyScan.h @@ -25,7 +25,7 @@ /// SWIFTSCAN_VERSION_MINOR should increase when there are API additions. /// SWIFTSCAN_VERSION_MAJOR is intended for "major" source/ABI breaking changes. #define SWIFTSCAN_VERSION_MAJOR 2 -#define SWIFTSCAN_VERSION_MINOR 1 +#define SWIFTSCAN_VERSION_MINOR 2 SWIFTSCAN_BEGIN_DECLS @@ -36,7 +36,6 @@ typedef enum { // SwiftInterface and SwiftSource. SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL = 0, SWIFTSCAN_DEPENDENCY_INFO_SWIFT_BINARY = 1, - SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER = 2, SWIFTSCAN_DEPENDENCY_INFO_CLANG = 3 } swiftscan_dependency_info_kind_t; @@ -49,6 +48,9 @@ typedef struct swiftscan_dependency_info_s *swiftscan_dependency_info_t; /// Opaque container to a link library info. typedef struct swiftscan_link_library_info_s *swiftscan_link_library_info_t; +/// Opaque container to an import info. +typedef struct swiftscan_import_info_s *swiftscan_import_info_t; + /// Opaque container to a macro dependency. typedef struct swiftscan_macro_dependency_s *swiftscan_macro_dependency_t; @@ -76,6 +78,18 @@ typedef struct { size_t count; } swiftscan_link_library_set_t; +/// Set of details about source imports +typedef struct { + swiftscan_import_info_t *imports; + size_t count; +} swiftscan_import_info_set_t; + +/// Set of source location infos +typedef struct { + swiftscan_source_location_t *source_locations; + size_t count; +} swiftscan_source_location_set_t; + /// Set of macro dependency typedef struct { swiftscan_macro_dependency_t *macro_dependencies; @@ -89,6 +103,15 @@ typedef enum { SWIFTSCAN_DIAGNOSTIC_SEVERITY_REMARK = 3 } swiftscan_diagnostic_severity_t; +// Must maintain consistency with swift::AccessLevel +typedef enum { + SWIFTSCAN_ACCESS_LEVEL_PRIVATE = 0, + SWIFTSCAN_ACCESS_LEVEL_FILEPRIVATE = 1, + SWIFTSCAN_ACCESS_LEVEL_INTERNAL = 2, + SWIFTSCAN_ACCESS_LEVEL_PACKAGE = 3, + SWIFTSCAN_ACCESS_LEVEL_PUBLIC = 4 +} swiftscan_access_level_t; + typedef struct { swiftscan_diagnostic_info_t *diagnostics; size_t count; @@ -148,10 +171,23 @@ swiftscan_module_info_get_direct_dependencies(swiftscan_dependency_info_t info); SWIFTSCAN_PUBLIC swiftscan_link_library_set_t * swiftscan_module_info_get_link_libraries(swiftscan_dependency_info_t info); +SWIFTSCAN_PUBLIC swiftscan_import_info_set_t * +swiftscan_module_info_get_imports(swiftscan_dependency_info_t info); + SWIFTSCAN_PUBLIC swiftscan_module_details_t swiftscan_module_info_get_details(swiftscan_dependency_info_t info); -//=== Link Library Info Functions ------------------------------------===// +//=== Import Details Functions -------------------------------------------===// +SWIFTSCAN_PUBLIC swiftscan_source_location_set_t * +swiftscan_import_info_get_source_locations(swiftscan_import_info_t info); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_import_info_get_identifier(swiftscan_import_info_t info); + +SWIFTSCAN_PUBLIC swiftscan_access_level_t +swiftscan_import_info_get_access_level(swiftscan_import_info_t info); + +//=== Link Library Info Functions ----------------------------------------===// SWIFTSCAN_PUBLIC swiftscan_string_ref_t swiftscan_link_library_info_get_link_name( swiftscan_link_library_info_t info); @@ -212,6 +248,10 @@ SWIFTSCAN_PUBLIC swiftscan_string_set_t * swiftscan_swift_textual_detail_get_swift_overlay_dependencies( swiftscan_module_details_t details); +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_swift_textual_detail_get_swift_source_import_module_dependencies( +swiftscan_module_details_t details); + SWIFTSCAN_PUBLIC swiftscan_string_ref_t swiftscan_swift_textual_detail_get_cas_fs_root_id( swiftscan_module_details_t details); @@ -269,7 +309,8 @@ swiftscan_swift_binary_detail_get_module_cache_key( SWIFTSCAN_PUBLIC swiftscan_string_ref_t swiftscan_swift_binary_detail_get_user_module_version( swiftscan_module_details_t details); -//=== Swift Placeholder Module Details query APIs -------------------------===// + +//=== Swift Placeholder Module Details query APIs - DEPRECATED -----------===// SWIFTSCAN_PUBLIC swiftscan_string_ref_t swiftscan_swift_placeholder_detail_get_compiled_module_path( diff --git a/include/swift/ABI/Executor.h b/include/swift/ABI/Executor.h index 1257673826434..4f4fcf8927253 100644 --- a/include/swift/ABI/Executor.h +++ b/include/swift/ABI/Executor.h @@ -17,10 +17,10 @@ #ifndef SWIFT_ABI_EXECUTOR_H #define SWIFT_ABI_EXECUTOR_H -#include #include "swift/ABI/Actor.h" #include "swift/ABI/HeapObject.h" #include "swift/Runtime/Casting.h" +#include namespace swift { class AsyncContext; @@ -77,10 +77,10 @@ class SerialExecutorRef { /// Executor that may need to participate in complex "same context" checks, /// by invoking `isSameExclusiveExecutionContext` when comparing execution contexts. ComplexEquality = 0b01, - /// Mark this executor as the one used by `Task.startSynchronously`, + /// Mark this executor as the one used by `Task.immediate`, /// It cannot participate in switching. // TODO: Perhaps make this a generic "cannot switch" rather than start synchronously specific. - StartSynchronously = 0b10, + Immediate = 0b10, }; static_assert(static_cast(ExecutorKind::Ordinary) == 0); @@ -107,12 +107,12 @@ class SerialExecutorRef { static SerialExecutorRef forSynchronousStart() { auto wtable = reinterpret_cast(nullptr) | - static_cast(ExecutorKind::StartSynchronously); + static_cast(ExecutorKind::Immediate); return SerialExecutorRef(nullptr, wtable); } bool isForSynchronousStart() const { return getIdentity() == nullptr && - getExecutorKind() == ExecutorKind::StartSynchronously; + getExecutorKind() == ExecutorKind::Immediate; } /// Given a pointer to a serial executor and its SerialExecutor @@ -413,6 +413,23 @@ class AsyncFunctionPointer { uint32_t ExpectedContextSize; }; +/// Type-safe wrapper around the return value of `isIsolatingCurrentContext`. +enum class IsIsolatingCurrentContextDecision : int8_t { + // The function call could not determine if the current context is isolated + // by this executor or not. Default value for executors which do not implement + // `isIsolatingCurrentContext`. + Unknown = -1, + // The current context is definitely not isolated by this executor. + NotIsolated = 0, + // The current context is definitely isolated by this executor. + Isolated = 1, +}; + +IsIsolatingCurrentContextDecision +getIsIsolatingCurrentContextDecisionFromInt(int8_t value); + +StringRef getIsIsolatingCurrentContextDecisionNameStr(IsIsolatingCurrentContextDecision decision); + } #endif diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 0789767055800..bede849059198 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -2352,12 +2352,7 @@ struct TargetExtendedExistentialTypeShape } bool isCopyable() const { - if (!hasGeneralizationSignature()) { - return true; - } - auto *reqts = getGenSigRequirements(); - for (unsigned i = 0, e = getNumGenSigRequirements(); i < e; ++i) { - auto &reqt = reqts[i]; + for (auto &reqt : getRequirementSignature().getRequirements()) { if (reqt.getKind() != GenericRequirementKind::InvertedProtocols) { continue; } @@ -2792,11 +2787,22 @@ using ResilientWitnessesHeader = TargetResilientWitnessesHeader; /// global actor protocol. template struct TargetGlobalActorReference { +private: + using SignedDescriptorPointer = + const TargetProtocolConformanceDescriptor + *__ptrauth_swift_protocol_conformance_descriptor; + +public: /// The type of the global actor. RelativeDirectPointer type; /// The conformance of the global actor to the GlobalActor protocol. - TargetRelativeProtocolConformanceDescriptorPointer conformance; + RelativeIndirectablePointer< + const TargetProtocolConformanceDescriptor, + /*nullable*/ false, + /*offset*/ int32_t, + /*indirect type*/ SignedDescriptorPointer> + conformance; }; /// Describes the context of a protocol conformance that is relevant when @@ -2984,14 +2990,6 @@ struct TargetProtocolConformanceDescriptor final return Demangle::makeSymbolicMangledNameStringRef(this->template getTrailingObjects>()->type); } - /// True if this is a conformance to 'SerialExecutor' which has a non-default - /// (i.e. not the stdlib's default implementation) witness. This means that - /// the developer has implemented this method explicitly and we should prefer - /// calling it. - bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext() const { - return Flags.hasNonDefaultSerialExecutorIsIsolatingCurrentContext(); - } - /// Retrieve the protocol conformance of the global actor type to the /// GlobalActor protocol. const TargetProtocolConformanceDescriptor * diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 8007e3ab75e82..4e5e9b79d31b8 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -165,18 +165,19 @@ class TargetValueWitnessFlags { // flags for the struct. (The "non-inline" and "has-extra-inhabitants" bits // still require additional fixup.) enum : uint32_t { - AlignmentMask = 0x000000FF, - // unused 0x0000FF00, - IsNonPOD = 0x00010000, - IsNonInline = 0x00020000, - // unused 0x00040000, - HasSpareBits = 0x00080000, - IsNonBitwiseTakable = 0x00100000, - HasEnumWitnesses = 0x00200000, - Incomplete = 0x00400000, - IsNonCopyable = 0x00800000, - IsNonBitwiseBorrowable = 0x01000000, - // unused 0xFE000000, + AlignmentMask = 0x000000FF, + // unused 0x0000FF00, + IsNonPOD = 0x00010000, + IsNonInline = 0x00020000, + // unused 0x00040000, + HasSpareBits = 0x00080000, + IsNonBitwiseTakable = 0x00100000, + HasEnumWitnesses = 0x00200000, + Incomplete = 0x00400000, + IsNonCopyable = 0x00800000, + IsNonBitwiseBorrowable = 0x01000000, + IsAddressableForDependencies = 0x02000000, + // unused 0xFC000000, }; static constexpr const uint32_t MaxNumExtraInhabitants = 0x7FFFFFFF; @@ -268,6 +269,19 @@ class TargetValueWitnessFlags { return TargetValueWitnessFlags((Data & ~IsNonCopyable) | (isCopyable ? 0 : IsNonCopyable)); } + + /// True if values of this type are addressable-for-dependencies, meaning + /// that values of this type should be passed indirectly to functions that + /// produce lifetime-dependent values that could possibly contain pointers + /// to the inline storage of this type. + bool isAddressableForDependencies() const { + return Data & IsAddressableForDependencies; + } + constexpr TargetValueWitnessFlags withAddressableForDependencies(bool afd) const { + return TargetValueWitnessFlags((Data & ~IsAddressableForDependencies) | + (afd ? IsAddressableForDependencies : 0)); + } + /// True if this type's binary representation is that of an enum, and the /// enum value witness table entries are available in this type's value @@ -374,8 +388,6 @@ class MethodDescriptorFlags { Setter, ModifyCoroutine, ReadCoroutine, - Read2Coroutine, - Modify2Coroutine, }; private: @@ -438,23 +450,27 @@ class MethodDescriptorFlags { /// Note that 'init' is not considered an instance member. bool isInstance() const { return Value & IsInstanceMask; } - bool isAsync() const { return Value & IsAsyncMask; } + bool _hasAsyncBitSet() const { return Value & IsAsyncMask; } - bool isCalleeAllocatedCoroutine() const { + bool isAsync() const { return !isCoroutine() && _hasAsyncBitSet(); } + + bool isCoroutine() const { switch (getKind()) { case Kind::Method: case Kind::Init: case Kind::Getter: case Kind::Setter: + return false; case Kind::ModifyCoroutine: case Kind::ReadCoroutine: - return false; - case Kind::Read2Coroutine: - case Kind::Modify2Coroutine: return true; } } + bool isCalleeAllocatedCoroutine() const { + return isCoroutine() && _hasAsyncBitSet(); + } + bool isData() const { return isAsync() || isCalleeAllocatedCoroutine(); } uint16_t getExtraDiscriminator() const { @@ -615,8 +631,6 @@ class ProtocolRequirementFlags { ModifyCoroutine, AssociatedTypeAccessFunction, AssociatedConformanceAccessFunction, - Read2Coroutine, - Modify2Coroutine, }; private: @@ -666,26 +680,30 @@ class ProtocolRequirementFlags { /// Note that 'init' is not considered an instance member. bool isInstance() const { return Value & IsInstanceMask; } - bool isAsync() const { return Value & IsAsyncMask; } + bool _hasAsyncBitSet() const { return Value & IsAsyncMask; } - bool isCalleeAllocatedCoroutine() const { + bool isAsync() const { return !isCoroutine() && _hasAsyncBitSet(); } + + bool isCoroutine() const { switch (getKind()) { case Kind::BaseProtocol: case Kind::Method: case Kind::Init: case Kind::Getter: case Kind::Setter: - case Kind::ReadCoroutine: - case Kind::ModifyCoroutine: case Kind::AssociatedTypeAccessFunction: case Kind::AssociatedConformanceAccessFunction: return false; - case Kind::Read2Coroutine: - case Kind::Modify2Coroutine: + case Kind::ReadCoroutine: + case Kind::ModifyCoroutine: return true; } } + bool isCalleeAllocatedCoroutine() const { + return isCoroutine() && _hasAsyncBitSet(); + } + bool isData() const { return isAsync() || isCalleeAllocatedCoroutine(); } bool isSignedWithAddress() const { @@ -750,14 +768,6 @@ class ConformanceFlags { IsConformanceOfProtocolMask = 0x01u << 18, HasGlobalActorIsolation = 0x01u << 19, - // Used to detect if this is a conformance to SerialExecutor that has - // an user defined implementation of 'isIsolatingCurrentContext'. This - // requirement is special in the sense that if a non-default impl is present - // we will avoid calling the `checkIsolated` method which would lead to a - // crash. In other words, this API "soft replaces" 'checkIsolated' so we - // must at runtime the presence of a non-default implementation. - HasNonDefaultSerialExecutorIsIsolatingCurrentContext = 0x01u << 20, - NumConditionalPackDescriptorsMask = 0xFFu << 24, NumConditionalPackDescriptorsShift = 24 }; @@ -824,15 +834,7 @@ class ConformanceFlags { : 0)); } - ConformanceFlags withHasNonDefaultSerialExecutorIsIsolatingCurrentContext( - bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext) const { - return ConformanceFlags((Value & ~HasNonDefaultSerialExecutorIsIsolatingCurrentContext) - | (hasNonDefaultSerialExecutorIsIsolatingCurrentContext - ? HasNonDefaultSerialExecutorIsIsolatingCurrentContext - : 0)); - } - - /// Retrieve the type reference kind kind. + /// Retrieve the type reference kind. TypeReferenceKind getTypeReferenceKind() const { return TypeReferenceKind( (Value & TypeMetadataKindMask) >> TypeMetadataKindShift); @@ -876,10 +878,6 @@ class ConformanceFlags { return Value & HasGlobalActorIsolation; } - bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext() const { - return Value & HasNonDefaultSerialExecutorIsIsolatingCurrentContext; - } - /// Retrieve the # of conditional requirements. unsigned getNumConditionalRequirements() const { return (Value & NumConditionalRequirementsMask) @@ -1295,7 +1293,7 @@ class TargetExtendedFunctionTypeFlags { // Values for the enumerated isolation kinds IsolatedAny = 0x00000002U, - NonIsolatedCaller = 0x00000004U, + NonIsolatedNonsending = 0x00000004U, // Values if we have a sending result. HasSendingResult = 0x00000010U, @@ -1330,7 +1328,7 @@ class TargetExtendedFunctionTypeFlags { const TargetExtendedFunctionTypeFlags withNonIsolatedCaller() const { return TargetExtendedFunctionTypeFlags((Data & ~IsolationMask) | - NonIsolatedCaller); + NonIsolatedNonsending); } const TargetExtendedFunctionTypeFlags @@ -1354,7 +1352,7 @@ class TargetExtendedFunctionTypeFlags { } bool isNonIsolatedCaller() const { - return (Data & IsolationMask) == NonIsolatedCaller; + return (Data & IsolationMask) == NonIsolatedNonsending; } bool hasSendingResult() const { @@ -1761,7 +1759,7 @@ namespace SpecialPointerAuthDiscriminators { 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 CancellationNotificationFunction = 0x0f08; // = 3848 const uint16_t EscalationNotificationFunction = 0x7861; // = 30817 const uint16_t AsyncThinNullaryFunction = 0x0f08; // = 3848 const uint16_t AsyncFutureFunction = 0x720f; // = 29199 @@ -2762,7 +2760,7 @@ class TaskCreateFlags : public FlagSet { /// /// Supported starting in Swift 6.1. Task_IsTaskFunctionConsumed = 15, - Task_IsStartSynchronouslyTask = 16, + Task_IsImmediateTask = 16, }; explicit constexpr TaskCreateFlags(size_t bits) : FlagSet(bits) {} @@ -2795,9 +2793,9 @@ class TaskCreateFlags : public FlagSet { FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsTaskFunctionConsumed, isTaskFunctionConsumed, setIsTaskFunctionConsumed) - FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsStartSynchronouslyTask, - isSynchronousStartTask, - setIsSYnchronousStartTask) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsImmediateTask, + isImmediateTask, + setIsImmediateTask) }; /// Flags for schedulable jobs. diff --git a/include/swift/ABI/ObjectFile.h b/include/swift/ABI/ObjectFile.h index 2158215c4862f..fddf9c7e064e2 100644 --- a/include/swift/ABI/ObjectFile.h +++ b/include/swift/ABI/ObjectFile.h @@ -83,7 +83,7 @@ class SwiftObjectFileFormatELF : public SwiftObjectFileFormat { } }; -/// Responsible for providing the COFF reflection section identifiers +/// Responsible for providing the COFF reflection section identifiers. class SwiftObjectFileFormatCOFF : public SwiftObjectFileFormat { public: llvm::StringRef getSectionName(ReflectionSectionKind section) override { @@ -101,5 +101,28 @@ class SwiftObjectFileFormatCOFF : public SwiftObjectFileFormat { return sectionName.starts_with(".sw5"); } }; + +/// Responsible for providing the WebAssembly reflection section identifiers. +/// WebAssembly binaries store all reflection metadata in the DATA +/// section. There are symbols for each reflection section kind in the "name" +/// section that point to the corresponding offset inside DATA. +class SwiftObjectFileFormatWasm : public SwiftObjectFileFormat { +public: + llvm::StringRef getSectionName(ReflectionSectionKind section) override { + switch (section) { +#define HANDLE_SWIFT_SECTION(KIND, MACHO, ELF, COFF) \ + case KIND: \ + return ELF; +#include "llvm/BinaryFormat/Swift.def" +#undef HANDLE_SWIFT_SECTION + } + llvm_unreachable("Section not found."); + } + + bool sectionContainsReflectionData(llvm::StringRef sectionName) override { + return sectionName.starts_with("swift5_"); + } +}; + } // namespace swift #endif // SWIFT_ABI_OBJECTFILE_H diff --git a/include/swift/ABI/Task.h b/include/swift/ABI/Task.h index a81ffdfdfa764..e80fd3b889559 100644 --- a/include/swift/ABI/Task.h +++ b/include/swift/ABI/Task.h @@ -29,6 +29,17 @@ #include "bitset" #include "queue" // TODO: remove and replace with our own mpsc +// Does the runtime provide priority escalation support? +#ifndef SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION +#if SWIFT_CONCURRENCY_ENABLE_DISPATCH && \ + __has_include() && __APPLE__ && \ + (defined(__arm64__) || defined(__x86_64__)) +#define SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION 1 +#else +#define SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION 0 +#endif +#endif /* SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION */ + namespace swift { class AsyncTask; class AsyncContext; @@ -143,6 +154,10 @@ class alignas(2 * alignof(void*)) Job : return Flags.getPriority(); } + void setPriority(JobPriority priority) { + Flags.setPriority(priority); + } + uint32_t getJobId() const { return Id; } @@ -226,10 +241,10 @@ struct ResultTypeInfo { #else size_t size = 0; size_t alignMask = 0; - void (*initializeWithCopy)(OpaqueValue *result, OpaqueValue *src) = nullptr; + OpaqueValue * (*initializeWithCopy)(OpaqueValue *result, OpaqueValue *src, void *type) = nullptr; void (*storeEnumTagSinglePayload)(OpaqueValue *v, unsigned whichCase, - unsigned emptyCases) = nullptr; - void (*destroy)(OpaqueValue *) = nullptr; + unsigned emptyCases, void *type) = nullptr; + void (*destroy)(OpaqueValue *, void *) = nullptr; bool isNull() { return initializeWithCopy == nullptr; @@ -241,14 +256,14 @@ struct ResultTypeInfo { return alignMask + 1; } void vw_initializeWithCopy(OpaqueValue *result, OpaqueValue *src) { - initializeWithCopy(result, src); + initializeWithCopy(result, src, nullptr); } void vw_storeEnumTagSinglePayload(OpaqueValue *v, unsigned whichCase, unsigned emptyCases) { - storeEnumTagSinglePayload(v, whichCase, emptyCases); + storeEnumTagSinglePayload(v, whichCase, emptyCases, nullptr); } void vw_destroy(OpaqueValue *v) { - destroy(v); + destroy(v, nullptr); } #endif }; @@ -301,15 +316,16 @@ class AsyncTask : public Job { #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES static constexpr size_t ActiveTaskStatusSize = 4 * sizeof(void *); #else - static constexpr size_t ActiveTaskStatusSize = 4 * sizeof(void *); + static constexpr size_t ActiveTaskStatusSize = 2 * sizeof(void *); #endif // Private storage is currently 6 pointers, 16 bytes of non-pointer data, - // the ActiveTaskStatus, and a RecursiveMutex. + // 8 bytes of padding, the ActiveTaskStatus, and a RecursiveMutex. static constexpr size_t PrivateStorageSize = - 6 * sizeof(void *) + 16 + ActiveTaskStatusSize + sizeof(RecursiveMutex); + 6 * sizeof(void *) + 16 + 8 + ActiveTaskStatusSize + + sizeof(RecursiveMutex); - void *Storage[PrivateStorageSize]; + char Storage[PrivateStorageSize]; /// Initialize this storage during the creation of a task. void initialize(JobPriority basePri); diff --git a/include/swift/ABI/TaskGroup.h b/include/swift/ABI/TaskGroup.h index b1c821d8e1cba..03f7e101cce06 100644 --- a/include/swift/ABI/TaskGroup.h +++ b/include/swift/ABI/TaskGroup.h @@ -44,6 +44,12 @@ class alignas(Alignment_TaskGroup) TaskGroup { /// Checks the cancellation status of the group. bool isCancelled(); + /// Only mark the task group as cancelled, without performing the follow-up + /// work of cancelling all the child tasks. + /// + /// Returns true if the group was already cancelled before this call. + bool statusCancel(); + // Add a child task to the task group. Always called while holding the // status record lock of the task group's owning task. void addChildTask(AsyncTask *task); diff --git a/include/swift/ABI/TaskOptions.h b/include/swift/ABI/TaskOptions.h index f1b8448e3841f..029b031f0e95e 100644 --- a/include/swift/ABI/TaskOptions.h +++ b/include/swift/ABI/TaskOptions.h @@ -199,7 +199,7 @@ class AsyncLetWithBufferTaskOptionRecord : public TaskOptionRecord { AsyncLet *getAsyncLet() const { return asyncLet; } - + void *getResultBuffer() const { return resultBuffer; } @@ -215,16 +215,16 @@ class ResultTypeInfoTaskOptionRecord : public TaskOptionRecord { size_t size; size_t alignMask; - void (*__ptrauth_swift_value_witness_function_pointer( + OpaqueValue *(*__ptrauth_swift_value_witness_function_pointer( SpecialPointerAuthDiscriminators::InitializeWithCopy) - initializeWithCopy)(OpaqueValue *, OpaqueValue *); + initializeWithCopy)(OpaqueValue *, OpaqueValue *, void *); void (*__ptrauth_swift_value_witness_function_pointer( SpecialPointerAuthDiscriminators::StoreEnumTagSinglePayload) - storeEnumTagSinglePayload)(OpaqueValue *, unsigned, unsigned); + storeEnumTagSinglePayload)(OpaqueValue *, unsigned, unsigned, void *); void (*__ptrauth_swift_value_witness_function_pointer( - SpecialPointerAuthDiscriminators::Destroy) destroy)(OpaqueValue *); + SpecialPointerAuthDiscriminators::Destroy) destroy)(OpaqueValue *, void *); static bool classof(const TaskOptionRecord *record) { return record->getKind() == TaskOptionRecordKind::ResultTypeInfo; diff --git a/include/swift/ABI/TaskStatus.h b/include/swift/ABI/TaskStatus.h index 2d5a3daf5e21d..56718978e8245 100644 --- a/include/swift/ABI/TaskStatus.h +++ b/include/swift/ABI/TaskStatus.h @@ -269,7 +269,7 @@ class CancellationNotificationStatusRecord : public TaskStatusRecord { /// subsequently used. class EscalationNotificationStatusRecord : public TaskStatusRecord { public: - using FunctionType = SWIFT_CC(swift) void(JobPriority, JobPriority, SWIFT_CONTEXT void *); + using FunctionType = SWIFT_CC(swift) void(uint8_t, uint8_t, SWIFT_CONTEXT void *); private: FunctionType *__ptrauth_swift_escalation_notification_function Function; @@ -282,7 +282,10 @@ class EscalationNotificationStatusRecord : public TaskStatusRecord { } void run(JobPriority oldPriority, JobPriority newPriority) { - Function(oldPriority, newPriority, Argument); + Function( + static_cast(oldPriority), + static_cast(newPriority), + Argument); } static bool classof(const TaskStatusRecord *record) { diff --git a/include/swift/APIDigester/ModuleAnalyzerNodes.h b/include/swift/APIDigester/ModuleAnalyzerNodes.h index b6887711eb13b..b698bf04b382a 100644 --- a/include/swift/APIDigester/ModuleAnalyzerNodes.h +++ b/include/swift/APIDigester/ModuleAnalyzerNodes.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 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -17,11 +17,6 @@ #ifndef __SWIFT_ABI_DIGESTER_MODULE_NODES_H__ #define __SWIFT_ABI_DIGESTER_MODULE_NODES_H__ -#include "clang/AST/ASTContext.h" -#include "clang/AST/DeclObjC.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Sema/Lookup.h" -#include "clang/Sema/Sema.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" @@ -242,7 +237,9 @@ class SDKContext { void diagnose(YAMLNodeTy node, Diag ID, typename detail::PassArgument::type... args) { auto smRange = node->getSourceRange(); - auto range = SourceRange(SourceLoc(smRange.Start), SourceLoc(smRange.End)); + auto range = + SourceRange(SourceLoc::getFromPointer(smRange.Start.getPointer()), + SourceLoc::getFromPointer(smRange.End.getPointer())); Diags.diagnose(range.Start, ID, std::forward(args)...) .highlight(range); } diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 31cce3d1ff0c2..ec88cb660a4f0 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2025 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 @@ -13,15 +13,21 @@ #ifndef SWIFT_AST_ASTBRIDGING_H #define SWIFT_AST_ASTBRIDGING_H -// Do not add other C++/llvm/swift header files here! -// Function implementations should be placed into ASTBridging.cpp and required header files should be added there. -// -// Pure bridging mode does not permit including any C++/llvm/swift headers. -// See also the comments for `BRIDGING_MODE` in the top-level CMakeLists.txt file. -// +/// `ASTBridging.h` is imported into Swift. Be *very* careful with what you +/// include here and keep these includes minimal! +/// +/// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/AST/AccessorKind.h" +#include "swift/AST/AttrKind.h" +#include "swift/AST/DiagnosticKind.h" +#include "swift/AST/DiagnosticList.h" +#include "swift/AST/GenericTypeParamKind.h" +#include "swift/AST/Identifier.h" +#include "swift/AST/LayoutConstraintKind.h" +#include "swift/AST/PlatformKind.h" #include "swift/Basic/BasicBridging.h" -#ifdef USED_IN_CPP_SOURCE +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE #include "swift/AST/Attr.h" #include "swift/AST/Decl.h" #endif @@ -52,6 +58,7 @@ class IfConfigClauseRangeInfo; class GenericSignature; class GenericSignatureImpl; struct LabeledStmtInfo; +class LangOptions; class LayoutConstraint; class LayoutConstraintInfo; struct LifetimeDescriptor; @@ -60,7 +67,6 @@ class MacroIntroducedDeclName; enum class MacroIntroducedDeclNameKind; enum class ParamSpecifier : uint8_t; class ParsedAutoDiffParameter; -enum class PlatformKind : uint8_t; class ProtocolConformanceRef; class RegexLiteralPatternFeature; class RegexLiteralPatternFeatureKind; @@ -71,16 +77,17 @@ class CanType; class TypeBase; class StmtConditionElement; class SubstitutionMap; +enum class RequirementReprKind : unsigned; } struct BridgedASTType; class BridgedCanType; class BridgedASTContext; +class BridgedLangOptions; struct BridgedSubstitutionMap; struct BridgedGenericSignature; struct BridgedConformance; class BridgedParameterList; -enum BridgedPlatformKind : size_t; // Forward declare the underlying AST node type for each wrapper. namespace swift { @@ -107,43 +114,20 @@ namespace swift { // MARK: Identifier //===----------------------------------------------------------------------===// -class BridgedIdentifier { -public: - SWIFT_UNAVAILABLE("Use '.raw' instead") - const void *_Nullable Raw; - - BridgedIdentifier() : Raw(nullptr) {} - - SWIFT_NAME("init(raw:)") - BridgedIdentifier(const void *_Nullable raw) : Raw(raw) {} - - BRIDGED_INLINE BridgedIdentifier(swift::Identifier ident); - - BRIDGED_INLINE swift::Identifier unbridged() const; -}; - -SWIFT_NAME("getter:BridgedIdentifier.raw(self:)") -inline const void *_Nullable BridgedIdentifier_raw(BridgedIdentifier ident) { - return ident.Raw; -} - -SWIFT_NAME("getter:BridgedIdentifier.isOperator(self:)") -BRIDGED_INLINE bool BridgedIdentifier_isOperator(const BridgedIdentifier); - struct BridgedLocatedIdentifier { SWIFT_NAME("name") - BridgedIdentifier Name; + swift::Identifier Name; SWIFT_NAME("nameLoc") - BridgedSourceLoc NameLoc; + swift::SourceLoc NameLoc; }; struct BridgedConsumedLookupResult { SWIFT_NAME("name") - BridgedIdentifier Name; + swift::Identifier Name; SWIFT_NAME("nameLoc") - BridgedSourceLoc NameLoc; + swift::SourceLoc NameLoc; SWIFT_NAME("flag") SwiftInt Flag; @@ -153,28 +137,6 @@ struct BridgedConsumedLookupResult { SwiftInt flag); }; -class BridgedDeclBaseName { - BridgedIdentifier Ident; - -public: - BRIDGED_INLINE BridgedDeclBaseName(swift::DeclBaseName baseName); - - BRIDGED_INLINE swift::DeclBaseName unbridged() const; -}; - -SWIFT_NAME("BridgedDeclBaseName.createConstructor()") -BridgedDeclBaseName BridgedDeclBaseName_createConstructor(); - -SWIFT_NAME("BridgedDeclBaseName.createDestructor()") -BridgedDeclBaseName BridgedDeclBaseName_createDestructor(); - -SWIFT_NAME("BridgedDeclBaseName.createSubscript()") -BridgedDeclBaseName BridgedDeclBaseName_createSubscript(); - -SWIFT_NAME("BridgedDeclBaseName.createIdentifier(_:)") -BridgedDeclBaseName -BridgedDeclBaseName_createIdentifier(BridgedIdentifier identifier); - class BridgedDeclNameRef { void *_Nullable opaque; @@ -187,14 +149,13 @@ class BridgedDeclNameRef { }; SWIFT_NAME("BridgedDeclNameRef.createParsed(_:baseName:argumentLabels:)") -BridgedDeclNameRef -BridgedDeclNameRef_createParsed(BridgedASTContext cContext, - BridgedDeclBaseName cBaseName, - BridgedArrayRef cLabels); +BridgedDeclNameRef BridgedDeclNameRef_createParsed(BridgedASTContext cContext, + swift::DeclBaseName baseName, + BridgedArrayRef cLabels); SWIFT_NAME("BridgedDeclNameRef.createParsed(_:)") BridgedDeclNameRef -BridgedDeclNameRef_createParsed(BridgedDeclBaseName cBaseName); +BridgedDeclNameRef_createParsed(swift::DeclBaseName baseName); class BridgedDeclNameLoc { const void *_Nullable LocationInfo; @@ -210,14 +171,29 @@ class BridgedDeclNameLoc { SWIFT_NAME("BridgedDeclNameLoc.createParsed(_:baseNameLoc:lParenLoc:" "argumentLabelLocs:rParenLoc:)") +BridgedDeclNameLoc BridgedDeclNameLoc_createParsed(BridgedASTContext cContext, + swift::SourceLoc baseNameLoc, + swift::SourceLoc lParenLoc, + BridgedArrayRef cLabelLocs, + swift::SourceLoc rParenLoc); + +SWIFT_NAME("BridgedDeclNameLoc.createParsed(_:moduleSelectorLoc:baseNameLoc:" + "lParenLoc:argumentLabelLocs:rParenLoc:)") BridgedDeclNameLoc BridgedDeclNameLoc_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cBaseNameLoc, - BridgedSourceLoc cLParenLoc, BridgedArrayRef cLabelLocs, - BridgedSourceLoc cRParenLoc); + BridgedASTContext cContext, swift::SourceLoc cModuleSelectorLoc, + swift::SourceLoc baseNameLoc, swift::SourceLoc lParenLoc, + BridgedArrayRef cLabelLocs, swift::SourceLoc rParenLoc); SWIFT_NAME("BridgedDeclNameLoc.createParsed(_:)") BridgedDeclNameLoc -BridgedDeclNameLoc_createParsed(BridgedSourceLoc cBaseNameLoc); +BridgedDeclNameLoc_createParsed(swift::SourceLoc baseNameLoc); + +SWIFT_NAME("BridgedDeclNameLoc.createParsed(_:moduleSelectorLoc:baseNameLoc:)") +BridgedDeclNameLoc +BridgedDeclNameLoc_createParsed(BridgedASTContext cContext, + swift::SourceLoc moduleSelectorLoc, + swift::SourceLoc baseNameLoc); + //===----------------------------------------------------------------------===// // MARK: ASTContext @@ -232,17 +208,26 @@ class BridgedASTContext { SWIFT_UNAVAILABLE("Use '.raw' instead") BRIDGED_INLINE swift::ASTContext &unbridged() const; + + SWIFT_COMPUTED_PROPERTY + void *_Nonnull getRaw() const { return Ctx; } + + SWIFT_COMPUTED_PROPERTY + unsigned getMajorLanguageVersion() const; + + SWIFT_COMPUTED_PROPERTY + BridgedAvailabilityMacroMap getAvailabilityMacroMap() const; + + SWIFT_COMPUTED_PROPERTY + BridgedDiagnosticEngine getDiags() const; }; -#define IDENTIFIER_WITH_NAME(Name, _) \ -SWIFT_NAME("getter:BridgedASTContext.id_" #Name "(self:)") \ -BRIDGED_INLINE BridgedIdentifier BridgedASTContext_id_##Name(BridgedASTContext bridged); +#define IDENTIFIER_WITH_NAME(Name, _) \ + SWIFT_NAME("getter:BridgedASTContext.id_" #Name "(self:)") \ + BRIDGED_INLINE swift::Identifier BridgedASTContext_id_##Name( \ + BridgedASTContext bridged); #include "swift/AST/KnownIdentifiers.def" -SWIFT_NAME("getter:BridgedASTContext.raw(self:)") -BRIDGED_INLINE -void * _Nonnull BridgedASTContext_raw(BridgedASTContext bridged); - SWIFT_NAME("BridgedASTContext.init(raw:)") BRIDGED_INLINE BridgedASTContext BridgedASTContext_fromRaw(void * _Nonnull ptr); @@ -259,85 +244,19 @@ BridgedASTContext_allocateCopyString(BridgedASTContext cContext, BridgedStringRef cStr); SWIFT_NAME("BridgedASTContext.getIdentifier(self:_:)") -BridgedIdentifier BridgedASTContext_getIdentifier(BridgedASTContext cContext, +swift::Identifier BridgedASTContext_getIdentifier(BridgedASTContext cContext, BridgedStringRef cStr); SWIFT_NAME("BridgedASTContext.getDollarIdentifier(self:_:)") -BridgedIdentifier +swift::Identifier BridgedASTContext_getDollarIdentifier(BridgedASTContext cContext, size_t idx); -SWIFT_NAME("BridgedASTContext.langOptsHasFeature(self:_:)") -bool BridgedASTContext_langOptsHasFeature(BridgedASTContext cContext, - BridgedFeature feature); - -SWIFT_NAME("getter:BridgedASTContext.majorLanguageVersion(self:)") -unsigned BridgedASTContext_majorLanguageVersion(BridgedASTContext cContext); - -SWIFT_NAME("BridgedASTContext.langOptsCustomConditionSet(self:_:)") -bool BridgedASTContext_langOptsCustomConditionSet(BridgedASTContext cContext, - BridgedStringRef cName); - -SWIFT_NAME("BridgedASTContext.langOptsHasFeatureNamed(self:_:)") -bool BridgedASTContext_langOptsHasFeatureNamed(BridgedASTContext cContext, - BridgedStringRef cName); - -SWIFT_NAME("BridgedASTContext.langOptsHasAttributeNamed(self:_:)") -bool BridgedASTContext_langOptsHasAttributeNamed(BridgedASTContext cContext, - BridgedStringRef cName); - -SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetOS(self:_:)") -bool BridgedASTContext_langOptsIsActiveTargetOS(BridgedASTContext cContext, - BridgedStringRef cName); - -SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetArchitecture(self:_:)") -bool BridgedASTContext_langOptsIsActiveTargetArchitecture(BridgedASTContext cContext, - BridgedStringRef cName); - -SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetEnvironment(self:_:)") -bool BridgedASTContext_langOptsIsActiveTargetEnvironment(BridgedASTContext cContext, - BridgedStringRef cName); - -SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetRuntime(self:_:)") -bool BridgedASTContext_langOptsIsActiveTargetRuntime(BridgedASTContext cContext, - BridgedStringRef cName); - -SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetPtrAuth(self:_:)") -bool BridgedASTContext_langOptsIsActiveTargetPtrAuth(BridgedASTContext cContext, - BridgedStringRef cName); - -SWIFT_NAME("getter:BridgedASTContext.langOptsTargetPointerBitWidth(self:)") -unsigned BridgedASTContext_langOptsTargetPointerBitWidth(BridgedASTContext cContext); - -SWIFT_NAME("BridgedASTContext.langOptsGetTargetAtomicBitWidths(self:_:)") -SwiftInt BridgedASTContext_langOptsGetTargetAtomicBitWidths(BridgedASTContext cContext, - SwiftInt* _Nullable * _Nonnull cComponents); - -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedEndianness : size_t { - EndianLittle, - EndianBig, -}; - -SWIFT_NAME("getter:BridgedASTContext.langOptsAttachCommentsToDecls(self:)") -bool BridgedASTContext_langOptsAttachCommentsToDecls( - BridgedASTContext cContext); +SWIFT_NAME("getter:BridgedASTContext.langOpts(self:)") +BridgedLangOptions BridgedASTContext_langOpts(BridgedASTContext cContext); -SWIFT_NAME("getter:BridgedASTContext.langOptsTargetEndianness(self:)") -BridgedEndianness BridgedASTContext_langOptsTargetEndianness(BridgedASTContext cContext); - -SWIFT_NAME("BridgedASTContext.langOptsGetLanguageVersion(self:_:)") -SwiftInt BridgedASTContext_langOptsGetLanguageVersion(BridgedASTContext cContext, - SwiftInt* _Nullable * _Nonnull cComponents); - -SWIFT_NAME("BridgedASTContext.langOptsGetCompilerVersion(self:_:)") -SwiftInt BridgedASTContext_langOptsGetCompilerVersion(BridgedASTContext cContext, - SwiftInt* _Nullable * _Nonnull cComponents); - -SWIFT_NAME("getter:BridgedASTContext.availabilityMacroMap(self:)") -BridgedAvailabilityMacroMap -BridgedASTContext_getAvailabilityMacroMap(BridgedASTContext cContext); - -/* Deallocate an array of Swift int values that was allocated in C++. */ -void deallocateIntBuffer(SwiftInt * _Nullable cComponents); +SWIFT_NAME("BridgedLangOptions.hasAttributeNamed(self:_:)") +bool BridgedLangOptions_hasAttributeNamed(BridgedLangOptions cLangOpts, + BridgedStringRef cName); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedCanImportVersion : size_t { CanImportUnversioned, @@ -348,11 +267,14 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedCanImportVersion : size_t { SWIFT_NAME("BridgedASTContext.canImport(self:importPath:location:versionKind:versionComponents:numVersionComponents:)") bool BridgedASTContext_canImport(BridgedASTContext cContext, BridgedStringRef importPath, - BridgedSourceLoc canImportLoc, + swift::SourceLoc canImportLoc, BridgedCanImportVersion versionKind, - const SwiftInt * _Nullable versionComponents, + const SwiftInt *_Nullable versionComponents, SwiftInt numVersionComponents); +SWIFT_NAME("getter:BridgedASTContext.staticBuildConfigurationPtr(self:)") +void * _Nonnull BridgedASTContext_staticBuildConfiguration(BridgedASTContext cContext); + //===----------------------------------------------------------------------===// // MARK: AST nodes //===----------------------------------------------------------------------===// @@ -361,12 +283,22 @@ void registerBridgedDecl(BridgedStringRef bridgedClassName, SwiftMetatype metaty struct OptionalBridgedDeclObj { OptionalSwiftObject obj; + + OptionalBridgedDeclObj(OptionalSwiftObject obj) : obj(obj) {} + +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + template D *_Nullable getAs() const { + if (obj) + return llvm::cast(static_cast(obj)); + return nullptr; + } +#endif }; struct BridgedDeclObj { SwiftObject obj; -#ifdef USED_IN_CPP_SOURCE +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE template D *_Nonnull getAs() const { return llvm::cast(static_cast(obj)); } @@ -377,22 +309,27 @@ struct BridgedDeclObj { BridgedDeclObj(SwiftObject obj) : obj(obj) {} BridgedOwnedString getDebugDescription() const; - BRIDGED_INLINE BridgedSourceLoc getLoc() const; + BRIDGED_INLINE swift::SourceLoc getLoc() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedDeclObj getModuleContext() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getParent() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedStringRef Type_getName() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedStringRef Value_getUserFacingName() const; - SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedSourceLoc Value_getNameLoc() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE swift::SourceLoc Value_getNameLoc() const; BRIDGED_INLINE bool hasClangNode() const; BRIDGED_INLINE bool Value_isObjC() const; BRIDGED_INLINE bool AbstractStorage_isConst() const; BRIDGED_INLINE bool GenericType_isGenericAtAnyLevel() const; BRIDGED_INLINE bool NominalType_isGlobalActor() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj NominalType_getValueTypeDestructor() const; + BRIDGED_INLINE bool Enum_hasRawType() const; BRIDGED_INLINE bool Struct_hasUnreferenceableStorage() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType Class_getSuperclass() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedDeclObj Class_getDestructor() const; + BRIDGED_INLINE bool ProtocolDecl_requiresClass() const; BRIDGED_INLINE bool AbstractFunction_isOverridden() const; BRIDGED_INLINE bool Destructor_isIsolated() const; + BRIDGED_INLINE bool EnumElementDecl_hasAssociatedValues() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedStringRef AccessorDecl_getKindName() const; }; enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedASTNodeKind : uint8_t { @@ -425,7 +362,7 @@ class BridgedASTNode { return BridgedASTNode(e.unbridged(), BridgedASTNodeKindExpr); } - SWIFT_UNAVAILABLE("use .kind") + SWIFT_COMPUTED_PROPERTY BridgedASTNodeKind getKind() const { return static_cast(opaque & 0x7); } @@ -437,11 +374,6 @@ class BridgedASTNode { BRIDGED_INLINE swift::ASTNode unbridged() const; }; -SWIFT_NAME("getter:BridgedASTNode.kind(self:)") -inline BridgedASTNodeKind BridgedASTNode_getKind(BridgedASTNode node) { - return node.getKind(); -} - // Declare `.asDecl` on each BridgedXXXDecl type, which upcasts a wrapper for // a Decl subclass to a BridgedDecl. #define DECL(Id, Parent) \ @@ -511,29 +443,15 @@ inline BridgedASTNodeKind BridgedASTNode_getKind(BridgedASTNode node) { struct BridgedPatternBindingEntry { BridgedPattern pattern; - BridgedSourceLoc equalLoc; + swift::SourceLoc equalLoc; BridgedNullableExpr init; BridgedNullablePatternBindingInitializer initContext; }; -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedAccessorKind { -#define ACCESSOR(ID) BridgedAccessorKind##ID, -#include "swift/AST/AccessorKinds.def" -}; - -swift::AccessorKind unbridged(BridgedAccessorKind kind); - //===----------------------------------------------------------------------===// // MARK: Diagnostic Engine //===----------------------------------------------------------------------===// -// NOTE: This must be the same underlying value as C++ 'swift::DiagID' defined -// in 'DiagnosticList.cpp'. -enum ENUM_EXTENSIBILITY_ATTR(open) BridgedDiagID : uint32_t { -#define DIAG(KIND, ID, Group, Options, Text, Signature) BridgedDiagID_##ID, -#include "swift/AST/DiagnosticsAll.def" -}; - class BridgedDiagnosticArgument { int64_t storage[3]; @@ -547,7 +465,7 @@ class BridgedDiagnosticArgument { class BridgedFixIt { public: - BridgedCharSourceRange replacementRange; + swift::CharSourceRange replacementRange; BridgedStringRef replacementText; }; @@ -555,16 +473,8 @@ class BridgedDiagnosticFixIt { public: int64_t storage[7]; - BridgedDiagnosticFixIt(BridgedSourceLoc start, uint32_t length, BridgedStringRef text); -}; - -/// Diagnostic severity when reporting diagnostics. -enum ENUM_EXTENSIBILITY_ATTR(open) BridgedDiagnosticSeverity : size_t { - BridgedFatalError, - BridgedError, - BridgedWarning, - BridgedRemark, - BridgedNote, + BridgedDiagnosticFixIt(swift::SourceLoc start, uint32_t length, + BridgedStringRef text); }; class BridgedDiagnostic { @@ -585,10 +495,15 @@ class BridgedDiagnostic { SWIFT_NAME("BridgedDiagnosticEngine.diagnose(self:at:_:_:highlightAt:" "highlightLength:fixIts:)") void BridgedDiagnosticEngine_diagnose( - BridgedDiagnosticEngine, BridgedSourceLoc loc, BridgedDiagID diagID, - BridgedArrayRef arguments, BridgedSourceLoc highlightStart, + BridgedDiagnosticEngine, swift::SourceLoc loc, swift::DiagID diagID, + BridgedArrayRef arguments, swift::SourceLoc highlightStart, uint32_t hightlightLength, BridgedArrayRef fixIts); +SWIFT_NAME("BridgedDiagnosticEngine.getLocationFromExternalSource(self:path:line:column:)") +swift::SourceLoc BridgedDiagnostic_getLocationFromExternalSource( + BridgedDiagnosticEngine bridgedEngine, BridgedStringRef path, SwiftInt line, + SwiftInt column); + SWIFT_NAME("getter:BridgedDiagnosticEngine.hadAnyError(self:)") bool BridgedDiagnosticEngine_hadAnyError(BridgedDiagnosticEngine); @@ -598,22 +513,22 @@ bool BridgedDiagnosticEngine_hadAnyError(BridgedDiagnosticEngine); /// \returns a diagnostic instance that can be extended with additional /// information and then must be finished via \c SwiftDiagnostic_finish. SWIFT_NAME("BridgedDiagnostic.init(at:message:severity:engine:)") -BridgedDiagnostic BridgedDiagnostic_create(BridgedSourceLoc cLoc, +BridgedDiagnostic BridgedDiagnostic_create(swift::SourceLoc loc, BridgedStringRef cText, - BridgedDiagnosticSeverity severity, + swift::DiagnosticKind severity, BridgedDiagnosticEngine cDiags); /// Highlight a source range as part of the diagnostic. SWIFT_NAME("BridgedDiagnostic.highlight(self:start:end:)") void BridgedDiagnostic_highlight(BridgedDiagnostic cDiag, - BridgedSourceLoc cStartLoc, - BridgedSourceLoc cEndLoc); + swift::SourceLoc LStartLoc, + swift::SourceLoc endLoc); /// Add a Fix-It to replace a source range as part of the diagnostic. SWIFT_NAME("BridgedDiagnostic.fixItReplace(self:start:end:replacement:)") void BridgedDiagnostic_fixItReplace(BridgedDiagnostic cDiag, - BridgedSourceLoc cStartLoc, - BridgedSourceLoc cEndLoc, + swift::SourceLoc startLoc, + swift::SourceLoc endLoc, BridgedStringRef cReplaceText); /// Finish the given diagnostic and emit it. @@ -686,20 +601,14 @@ BridgedClosureExpr_asDeclContext(BridgedClosureExpr cClosure); // MARK: Availability //===----------------------------------------------------------------------===// -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedPlatformKind : size_t { - BridgedPlatformKind_None, -#define AVAILABILITY_PLATFORM(X, PrettyName) BridgedPlatformKind_##X, -#include "swift/AST/PlatformKinds.def" -}; - -SWIFT_NAME("BridgedPlatformKind.init(from:)") -BridgedPlatformKind BridgedPlatformKind_fromString(BridgedStringRef cStr); +BRIDGED_OPTIONAL(swift::PlatformKind, PlatformKind) -SWIFT_NAME("BridgedPlatformKind.init(from:)") -BridgedPlatformKind -BridgedPlatformKind_fromIdentifier(BridgedIdentifier cIdent); +SWIFT_NAME("BridgedOptionalPlatformKind.init(from:)") +BridgedOptionalPlatformKind PlatformKind_fromString(BridgedStringRef cStr); -swift::PlatformKind unbridge(BridgedPlatformKind cPlatform); +SWIFT_NAME("BridgedOptionalPlatformKind.init(from:)") +BridgedOptionalPlatformKind +PlatformKind_fromIdentifier(swift::Identifier ident); SWIFT_NAME("BridgedAvailabilityMacroMap.has(self:name:)") bool BridgedAvailabilityMacroMap_hasName(BridgedAvailabilityMacroMap map, @@ -736,21 +645,21 @@ BRIDGED_INLINE bool BridgedAvailabilityDomainOrIdentifier_isDomain( BridgedAvailabilityDomainOrIdentifier cVal); SWIFT_NAME("getter:BridgedAvailabilityDomainOrIdentifier.asIdentifier(self:)") -BRIDGED_INLINE BridgedIdentifier +BRIDGED_INLINE swift::Identifier BridgedAvailabilityDomainOrIdentifier_getAsIdentifier( BridgedAvailabilityDomainOrIdentifier cVal); SWIFT_NAME("BridgedAvailabilitySpec.createWildcard(_:loc:)") BridgedAvailabilitySpec BridgedAvailabilitySpec_createWildcard(BridgedASTContext cContext, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME( "BridgedAvailabilitySpec.createForDomainIdentifier(_:name:nameLoc:version:" "versionRange:)") BridgedAvailabilitySpec BridgedAvailabilitySpec_createForDomainIdentifier( - BridgedASTContext cContext, BridgedIdentifier cName, BridgedSourceLoc cLoc, - BridgedVersionTuple cVersion, BridgedSourceRange cVersionRange); + BridgedASTContext cContext, swift::Identifier name, swift::SourceLoc loc, + BridgedVersionTuple cVersion, swift::SourceRange versionRange); SWIFT_NAME("BridgedAvailabilitySpec.clone(self:_:)") BridgedAvailabilitySpec @@ -759,14 +668,14 @@ BridgedAvailabilitySpec_clone(BridgedAvailabilitySpec spec, SWIFT_NAME("BridgedAvailabilitySpec.setMacroLoc(self:_:)") void BridgedAvailabilitySpec_setMacroLoc(BridgedAvailabilitySpec spec, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("getter:BridgedAvailabilitySpec.domainOrIdentifier(self:)") BridgedAvailabilityDomainOrIdentifier BridgedAvailabilitySpec_getDomainOrIdentifier(BridgedAvailabilitySpec spec); SWIFT_NAME("getter:BridgedAvailabilitySpec.sourceRange(self:)") -BridgedSourceRange +swift::SourceRange BridgedAvailabilitySpec_getSourceRange(BridgedAvailabilitySpec spec); SWIFT_NAME("getter:BridgedAvailabilitySpec.isWildcard(self:)") @@ -777,7 +686,7 @@ BridgedVersionTuple BridgedAvailabilitySpec_getRawVersion(BridgedAvailabilitySpec spec); SWIFT_NAME("getter:BridgedAvailabilitySpec.versionRange(self:)") -BridgedSourceRange +swift::SourceRange BridgedAvailabilitySpec_getVersionRange(BridgedAvailabilitySpec spec); //===----------------------------------------------------------------------===// @@ -796,37 +705,37 @@ swift::DifferentiabilityKind unbridged(BridgedDifferentiabilityKind cKind); class BridgedParsedAutoDiffParameter { private: - BridgedSourceLoc loc; + swift::SourceLoc loc; enum Kind { Named, Ordered, Self, } kind; union Value { - BridgedIdentifier name; + swift::Identifier name; unsigned index; - Value(BridgedIdentifier name) : name(name) {} + Value(swift::Identifier name) : name(name) {} Value(unsigned index) : index(index) {} Value() : name() {} } value; - BridgedParsedAutoDiffParameter(BridgedSourceLoc loc, Kind kind, Value value) + BridgedParsedAutoDiffParameter(swift::SourceLoc loc, Kind kind, Value value) : loc(loc), kind(kind), value(value) {} public: SWIFT_NAME("forNamed(_:loc:)") - static BridgedParsedAutoDiffParameter forNamed(BridgedIdentifier name, - BridgedSourceLoc loc) { + static BridgedParsedAutoDiffParameter forNamed(swift::Identifier name, + swift::SourceLoc loc) { return BridgedParsedAutoDiffParameter(loc, Kind::Named, name); } SWIFT_NAME("forOrdered(_:loc:)") static BridgedParsedAutoDiffParameter forOrdered(size_t index, - BridgedSourceLoc loc) { + swift::SourceLoc loc) { return BridgedParsedAutoDiffParameter(loc, Kind::Ordered, index); } SWIFT_NAME("forSelf(loc:)") - static BridgedParsedAutoDiffParameter forSelf(BridgedSourceLoc loc) { + static BridgedParsedAutoDiffParameter forSelf(swift::SourceLoc loc) { return BridgedParsedAutoDiffParameter(loc, Kind::Self, {}); } @@ -837,14 +746,11 @@ class BridgedParsedAutoDiffParameter { // MARK: DeclAttributes //===----------------------------------------------------------------------===// -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedDeclAttrKind { -#define DECL_ATTR(_, CLASS, ...) BridgedDeclAttrKind##CLASS, -#include "swift/AST/DeclAttr.def" - BridgedDeclAttrKindNone, -}; +BRIDGED_OPTIONAL(swift::DeclAttrKind, DeclAttrKind) -SWIFT_NAME("BridgedDeclAttrKind.init(from:)") -BridgedDeclAttrKind BridgedDeclAttrKind_fromString(BridgedStringRef cStr); +SWIFT_NAME("BridgedOptionalDeclAttrKind.init(from:)") +BridgedOptionalDeclAttrKind +BridgedOptionalDeclAttrKind_fromString(BridgedStringRef cStr); struct BridgedDeclAttributes { BridgedNullableDeclAttribute chain; @@ -857,10 +763,10 @@ struct BridgedDeclAttributes { }; SWIFT_NAME("BridgedDeclAttribute.shouldBeRejectedByParser(_:)") -bool BridgedDeclAttribute_shouldBeRejectedByParser(BridgedDeclAttrKind cKind); +bool BridgedDeclAttribute_shouldBeRejectedByParser(swift::DeclAttrKind kind); SWIFT_NAME("BridgedDeclAttribute.isDeclModifier(_:)") -bool BridgedDeclAttribute_isDeclModifier(BridgedDeclAttrKind cKind); +bool BridgedDeclAttribute_isDeclModifier(swift::DeclAttrKind kind); SWIFT_NAME("BridgedDeclAttributes.add(self:_:)") void BridgedDeclAttributes_add(BridgedDeclAttributes *_Nonnull attrs, @@ -868,13 +774,13 @@ void BridgedDeclAttributes_add(BridgedDeclAttributes *_Nonnull attrs, SWIFT_NAME("BridgedDeclAttribute.createSimple(_:kind:atLoc:nameLoc:)") BridgedDeclAttribute BridgedDeclAttribute_createSimple( - BridgedASTContext cContext, BridgedDeclAttrKind cKind, - BridgedSourceLoc cAtLoc, BridgedSourceLoc cNameLoc); + BridgedASTContext cContext, swift::DeclAttrKind kind, + swift::SourceLoc atLoc, swift::SourceLoc nameLoc); SWIFT_NAME("BridgedABIAttr.createParsed(_:atLoc:range:abiDecl:)") BridgedABIAttr BridgedABIAttr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc atLoc, - BridgedSourceRange range, + swift::SourceLoc atLoc, + swift::SourceRange range, BridgedNullableDecl abiDecl); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedAvailableAttrKind { @@ -888,20 +794,20 @@ SWIFT_NAME("BridgedAvailableAttr.createParsed(_:atLoc:range:domainIdentifier:" "domainLoc:kind:message:renamed:introduced:introducedRange:" "deprecated:deprecatedRange:obsoleted:obsoletedRange:isSPI:)") BridgedAvailableAttr BridgedAvailableAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedIdentifier cDomainIdentifier, - BridgedSourceLoc cDomainLoc, BridgedAvailableAttrKind cKind, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, swift::Identifier domainIdentifier, + swift::SourceLoc domainLoc, BridgedAvailableAttrKind cKind, BridgedStringRef cMessage, BridgedStringRef cRenamed, - BridgedVersionTuple cIntroduced, BridgedSourceRange cIntroducedRange, - BridgedVersionTuple cDeprecated, BridgedSourceRange cDeprecatedRange, - BridgedVersionTuple cObsoleted, BridgedSourceRange cObsoletedRange, + BridgedVersionTuple cIntroduced, swift::SourceRange introducedRange, + BridgedVersionTuple cDeprecated, swift::SourceRange deprecatedRange, + BridgedVersionTuple cObsoleted, swift::SourceRange obsoletedRange, bool isSPI); SWIFT_NAME("BridgedAvailableAttr.createUnavailableInEmbedded(_:atLoc:range:)") BridgedAvailableAttr BridgedAvailableAttr_createUnavailableInEmbedded(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange); + swift::SourceLoc atLoc, + swift::SourceRange range); SWIFT_NAME("BridgedAvailableAttr.setIsGroupMember(self:)") void BridgedAvailableAttr_setIsGroupMember(BridgedAvailableAttr cAttr); @@ -910,126 +816,99 @@ void BridgedAvailableAttr_setIsGroupedWithWildcard(BridgedAvailableAttr cAttr); SWIFT_NAME("BridgedAvailableAttr.setIsGroupTerminator(self:)") void BridgedAvailableAttr_setIsGroupTerminator(BridgedAvailableAttr cAttr); -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedExecutionKind { - BridgedExecutionKindConcurrent, - BridgedExecutionKindCaller, -}; - -SWIFT_NAME("BridgedExecutionAttr.createParsed(_:atLoc:range:behavior:)") -BridgedExecutionAttr BridgedExecutionAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc atLoc, - BridgedSourceRange range, BridgedExecutionKind behavior); - -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedAccessLevel { - BridgedAccessLevelPrivate, - BridgedAccessLevelFilePrivate, - BridgedAccessLevelInternal, - BridgedAccessLevelPackage, - BridgedAccessLevelPublic, - BridgedAccessLevelOpen, - BridgedAccessLevelNone, -}; +BRIDGED_OPTIONAL(swift::AccessLevel, AccessLevel) SWIFT_NAME("BridgedAccessControlAttr.createParsed(_:range:accessLevel:)") BridgedAccessControlAttr BridgedAccessControlAttr_createParsed(BridgedASTContext cContext, - BridgedSourceRange cRange, - BridgedAccessLevel cAccessLevel); + swift::SourceRange range, + swift::AccessLevel accessLevel); SWIFT_NAME("BridgedAlignmentAttr.createParsed(_:atLoc:range:value:)") BridgedAlignmentAttr BridgedAlignmentAttr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, size_t cValue); + swift::SourceLoc atLoc, + swift::SourceRange range, size_t cValue); SWIFT_NAME("BridgedAllowFeatureSuppressionAttr.createParsed(_:atLoc:range:inverted:features:)") BridgedAllowFeatureSuppressionAttr -BridgedAllowFeatureSuppressionAttr_createParsed( - BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, - bool inverted, - BridgedArrayRef cFeatures); +BridgedAllowFeatureSuppressionAttr_createParsed(BridgedASTContext cContext, + swift::SourceLoc atLoc, + swift::SourceRange range, + bool inverted, + BridgedArrayRef cFeatures); SWIFT_NAME( "BridgedBackDeployedAttr.createParsed(_:atLoc:range:platform:version:)") BridgedBackDeployedAttr BridgedBackDeployedAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedPlatformKind cPlatform, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, swift::PlatformKind platform, BridgedVersionTuple cVersion); -SWIFT_NAME("BridgedCDeclAttr.createParsed(_:atLoc:range:name:)") +SWIFT_NAME("BridgedCDeclAttr.createParsed(_:atLoc:range:name:underscored:)") BridgedCDeclAttr BridgedCDeclAttr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, - BridgedStringRef cName); + swift::SourceLoc atLoc, + swift::SourceRange range, + BridgedStringRef cName, + bool underscored); SWIFT_NAME( "BridgedCustomAttr.createParsed(_:atLoc:type:initContext:argumentList:)") BridgedCustomAttr BridgedCustomAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, BridgedTypeRepr cType, + BridgedASTContext cContext, swift::SourceLoc atLoc, BridgedTypeRepr cType, BridgedNullableCustomAttributeInitializer cInitContext, BridgedNullableArgumentList cArgumentList); SWIFT_NAME("BridgedDerivativeAttr.createParsed(_:atLoc:range:baseType:" "originalName:originalNameLoc:accessorKind:params:)") BridgedDerivativeAttr BridgedDerivativeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedNullableTypeRepr cBaseType, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedNullableTypeRepr cBaseType, BridgedDeclNameRef cOriginalName, BridgedDeclNameLoc cOriginalNameLoc, - BridgedAccessorKind cAccessorKind, BridgedArrayRef cParams); + swift::AccessorKind AccessorKind, BridgedArrayRef cParams); SWIFT_NAME("BridgedDerivativeAttr.createParsed(_:atLoc:range:baseType:" "originalName:originalNameLoc:params:)") BridgedDerivativeAttr BridgedDerivativeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedNullableTypeRepr cBaseType, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedNullableTypeRepr cBaseType, BridgedDeclNameRef cOriginalName, BridgedDeclNameLoc cOriginalNameLoc, BridgedArrayRef cParams); SWIFT_NAME("BridgedDifferentiableAttr.createParsed(_:atLoc:range:kind:params:" "genericWhereClause:)") BridgedDifferentiableAttr BridgedDifferentiableAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedDifferentiabilityKind cKind, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedDifferentiabilityKind cKind, BridgedArrayRef cParams, BridgedNullableTrailingWhereClause cGenericWhereClause); SWIFT_NAME("BridgedDocumentationAttr.createParsed(_:atLoc:range:metadata:" "accessLevel:)") BridgedDocumentationAttr BridgedDocumentationAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedStringRef cMetadata, - BridgedAccessLevel cAccessLevel); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedStringRef cMetadata, + BridgedOptionalAccessLevel accessLevel); SWIFT_NAME( "BridgedDynamicReplacementAttr.createParsed(_:atLoc:attrNameLoc:lParenLoc:" "replacedFunction:rParenLoc:)") BridgedDynamicReplacementAttr BridgedDynamicReplacementAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cAttrNameLoc, BridgedSourceLoc cLParenLoc, - BridgedDeclNameRef cReplacedFunction, BridgedSourceLoc cRParenLoc); - -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedEffectsKind { - BridgedEffectsKindReadNone, - BridgedEffectsKindReadOnly, - BridgedEffectsKindReleaseNone, - BridgedEffectsKindReadWrite, - BridgedEffectsKindUnspecified, - BridgedEffectsKindCustom, -}; + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceLoc attrNameLoc, swift::SourceLoc lParenLoc, + BridgedDeclNameRef cReplacedFunction, swift::SourceLoc rParenLoc); SWIFT_NAME("BridgedEffectsAttr.createParsed(_:atLoc:range:effectKind:)") BridgedEffectsAttr BridgedEffectsAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedEffectsKind cEffectKind); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, swift::EffectsKind effectKind); SWIFT_NAME("BridgedEffectsAttr.createParsed(_:atLoc:range:customString:" "customStringLoc:)") BridgedEffectsAttr BridgedEffectsAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedStringRef cCustomString, - BridgedSourceLoc cCustomStringLoc); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedStringRef cCustomString, + swift::SourceLoc customStringLoc); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedExclusivityAttrMode { BridgedExclusivityAttrModeChecked, @@ -1038,64 +917,50 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedExclusivityAttrMode { SWIFT_NAME("BridgedExclusivityAttr.createParsed(_:atLoc:range:mode:)") BridgedExclusivityAttr BridgedExclusivityAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedExclusivityAttrMode cMode); - -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedExposureKind { - BridgedExposureKindCxx, - BridgedExposureKindWasm, -}; + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedExclusivityAttrMode cMode); SWIFT_NAME("BridgedExposeAttr.createParsed(_:atLoc:range:name:kind:)") BridgedExposeAttr BridgedExposeAttr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, + swift::SourceLoc atLoc, + swift::SourceRange range, BridgedStringRef cName, - BridgedExposureKind cKind); - -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedExternKind { - BridgedExternKindC, - BridgedExternKindWasm, -}; + swift::ExposureKind kind); SWIFT_NAME("BridgedExternAttr.createParsed(_:atLoc:range:lParenLoc:rParenLoc:" "kind:moduleName:name:)") BridgedExternAttr BridgedExternAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedSourceLoc cLParenLoc, - BridgedSourceLoc cRParenLoc, BridgedExternKind cKind, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, swift::SourceLoc lParenLoc, + swift::SourceLoc rParenLoc, swift::ExternKind kind, BridgedStringRef cModuleName, BridgedStringRef cName); SWIFT_NAME("BridgedImplementsAttr.createParsed(_:atLoc:range:protocolType:" "memberName:memberNameLoc:)") BridgedImplementsAttr BridgedImplementsAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedTypeRepr cProtocolType, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedTypeRepr cProtocolType, BridgedDeclNameRef cMemberName, BridgedDeclNameLoc cMemberNameLoc); -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedInlineKind { - BridgedInlineKindNever, - BridgedInlineKindAlways, -}; - SWIFT_NAME("BridgedInlineAttr.createParsed(_:atLoc:range:kind:)") BridgedInlineAttr BridgedInlineAttr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, - BridgedInlineKind cKind); + swift::SourceLoc atLoc, + swift::SourceRange range, + swift::InlineKind kind); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedParsedLifetimeDependenceKind { BridgedParsedLifetimeDependenceKindDefault, - BridgedParsedLifetimeDependenceKindScope, + BridgedParsedLifetimeDependenceKindBorrow, BridgedParsedLifetimeDependenceKindInherit, + BridgedParsedLifetimeDependenceKindInout }; class BridgedLifetimeDescriptor { union Value { - BridgedIdentifier name; + swift::Identifier name; unsigned index; - Value(BridgedIdentifier name) : name(name) {} + Value(swift::Identifier name) : name(name) {} Value(unsigned index) : index(index) {} Value() : name() {} } value; @@ -1107,33 +972,33 @@ class BridgedLifetimeDescriptor { } kind; BridgedParsedLifetimeDependenceKind dependenceKind; - BridgedSourceLoc loc; + swift::SourceLoc loc; BridgedLifetimeDescriptor(Value value, DescriptorKind kind, BridgedParsedLifetimeDependenceKind dependenceKind, - BridgedSourceLoc loc) + swift::SourceLoc loc) : value(value), kind(kind), dependenceKind(dependenceKind), loc(loc) {} public: SWIFT_NAME("forNamed(_:dependenceKind:loc:)") static BridgedLifetimeDescriptor - forNamed(BridgedIdentifier name, + forNamed(swift::Identifier name, BridgedParsedLifetimeDependenceKind dependenceKind, - BridgedSourceLoc loc) { + swift::SourceLoc loc) { return BridgedLifetimeDescriptor(name, DescriptorKind::Named, dependenceKind, loc); } SWIFT_NAME("forOrdered(_:dependenceKind:loc:)") static BridgedLifetimeDescriptor forOrdered(size_t index, BridgedParsedLifetimeDependenceKind dependenceKind, - BridgedSourceLoc loc) { + swift::SourceLoc loc) { return BridgedLifetimeDescriptor(index, DescriptorKind::Ordered, dependenceKind, loc); } SWIFT_NAME("forSelf(dependenceKind:loc:)") static BridgedLifetimeDescriptor forSelf(BridgedParsedLifetimeDependenceKind dependenceKind, - BridgedSourceLoc loc) { + swift::SourceLoc loc) { return BridgedLifetimeDescriptor({}, DescriptorKind::Self, dependenceKind, loc); } @@ -1144,18 +1009,19 @@ class BridgedLifetimeDescriptor { SWIFT_NAME("BridgedLifetimeEntry.createParsed(_:range:sources:)") BridgedLifetimeEntry BridgedLifetimeEntry_createParsed(BridgedASTContext cContext, - BridgedSourceRange cRange, + swift::SourceRange range, BridgedArrayRef cSources); SWIFT_NAME("BridgedLifetimeEntry.createParsed(_:range:sources:target:)") BridgedLifetimeEntry BridgedLifetimeEntry_createParsed( - BridgedASTContext cContext, BridgedSourceRange cRange, + BridgedASTContext cContext, swift::SourceRange range, BridgedArrayRef cSources, BridgedLifetimeDescriptor cTarget); -SWIFT_NAME("BridgedLifetimeAttr.createParsed(_:atLoc:range:entry:)") +SWIFT_NAME( + "BridgedLifetimeAttr.createParsed(_:atLoc:range:entry:isUnderscored:)") BridgedLifetimeAttr BridgedLifetimeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedLifetimeEntry cEntry); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedLifetimeEntry cEntry, bool isUnderscored); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedMacroSyntax { BridgedMacroSyntaxFreestanding, @@ -1197,32 +1063,32 @@ BRIDGED_INLINE bool BridgedMacroRole_isAttached(BridgedMacroRole role); SWIFT_NAME("BridgedMacroRoleAttr.createParsed(_:atLoc:range:syntax:lParenLoc:" "role:names:conformances:rParenLoc:)") BridgedMacroRoleAttr BridgedMacroRoleAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedMacroSyntax cSyntax, - BridgedSourceLoc cLParenLoc, BridgedMacroRole cRole, BridgedArrayRef cNames, - BridgedArrayRef cConformances, BridgedSourceLoc cRParenLoc); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedMacroSyntax cSyntax, + swift::SourceLoc lParenLoc, BridgedMacroRole cRole, BridgedArrayRef cNames, + BridgedArrayRef cConformances, swift::SourceLoc rParenLoc); SWIFT_NAME("BridgedOriginallyDefinedInAttr.createParsed(_:atLoc:range:" "moduleName:platform:version:)") BridgedOriginallyDefinedInAttr BridgedOriginallyDefinedInAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedStringRef cModuleName, - BridgedPlatformKind cPlatform, BridgedVersionTuple cVersion); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedStringRef cModuleName, + swift::PlatformKind platform, BridgedVersionTuple cVersion); SWIFT_NAME("BridgedStorageRestrictionsAttr.createParsed(_:atLoc:range:" "initializes:accesses:)") BridgedStorageRestrictionsAttr BridgedStorageRestrictionsAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedArrayRef cInitializes, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedArrayRef cInitializes, BridgedArrayRef cAccesses); SWIFT_NAME( "BridgedSwiftNativeObjCRuntimeBaseAttr.createParsed(_:atLoc:range:name:)") BridgedSwiftNativeObjCRuntimeBaseAttr BridgedSwiftNativeObjCRuntimeBaseAttr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, - BridgedIdentifier cName); + swift::SourceLoc atLoc, + swift::SourceRange range, + swift::Identifier name); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedNonSendableKind { BridgedNonSendableKindSpecific, @@ -1231,46 +1097,50 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedNonSendableKind { SWIFT_NAME("BridgedNonSendableAttr.createParsed(_:atLoc:range:kind:)") BridgedNonSendableAttr BridgedNonSendableAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedNonSendableKind cKind); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedNonSendableKind cKind); + +SWIFT_NAME("BridgedNonisolatedAttr.createParsed(_:atLoc:range:modifier:)") +BridgedNonisolatedAttr BridgedNonisolatedAttr_createParsed( + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, swift::NonIsolatedModifier modifier); -SWIFT_NAME("BridgedNonisolatedAttr.createParsed(_:atLoc:range:isUnsafe:)") -BridgedNonisolatedAttr -BridgedNonisolatedAttr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, bool isUnsafe); +SWIFT_NAME("BridgedInheritActorContextAttr.createParsed(_:atLoc:range:modifier:)") +BridgedInheritActorContextAttr BridgedInheritActorContextAttr_createParsed( + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, swift::InheritActorContextModifier modifier); SWIFT_NAME("BridgedObjCAttr.createParsedUnnamed(_:atLoc:attrNameLoc:)") BridgedObjCAttr BridgedObjCAttr_createParsedUnnamed(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceLoc cAttrNameLoc); + swift::SourceLoc atLoc, + swift::SourceLoc attrNameLoc); SWIFT_NAME("BridgedObjCAttr.createParsedNullary(_:atLoc:attrNameLoc:lParenLoc:" "nameLoc:name:rParenLoc:)") BridgedObjCAttr BridgedObjCAttr_createParsedNullary( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cAttrNameLoc, BridgedSourceLoc cLParenLoc, - BridgedSourceLoc cNameLoc, BridgedIdentifier cName, - BridgedSourceLoc cRParenLoc); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceLoc attrNameLoc, swift::SourceLoc lParenLoc, + swift::SourceLoc nameLoc, swift::Identifier name, + swift::SourceLoc rParenLoc); SWIFT_NAME("BridgedObjCAttr.createParsedSelector(_:atLoc:attrNameLoc:lParenLoc:" "nameLocs:names:rParenLoc:)") BridgedObjCAttr BridgedObjCAttr_createParsedSelector( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cAttrNameLoc, BridgedSourceLoc cLParenLoc, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceLoc attrNameLoc, swift::SourceLoc lParenLoc, BridgedArrayRef cNameLocs, BridgedArrayRef cNames, - BridgedSourceLoc cRParenLoc); + swift::SourceLoc rParenLoc); SWIFT_NAME("BridgedObjCImplementationAttr.createParsed(_:atLoc:range:name:isEarlyAdopter:)") BridgedObjCImplementationAttr BridgedObjCImplementationAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedIdentifier cName, bool isEarlyAdopter); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, swift::Identifier name, bool isEarlyAdopter); SWIFT_NAME("BridgedObjCRuntimeNameAttr.createParsed(_:atLoc:range:name:)") BridgedObjCRuntimeNameAttr BridgedObjCRuntimeNameAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedIdentifier cName); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, swift::Identifier name); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedOptimizationMode { BridgedOptimizationModeForSpeed, @@ -1280,44 +1150,44 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedOptimizationMode { SWIFT_NAME("BridgedOptimizeAttr.createParsed(_:atLoc:range:mode:)") BridgedOptimizeAttr BridgedOptimizeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedOptimizationMode cMode); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedOptimizationMode cMode); SWIFT_NAME("BridgedPrivateImportAttr.createParsed(_:atLoc:attrNameLoc:" "lParenLoc:fileName:rParenLoc:)") BridgedPrivateImportAttr BridgedPrivateImportAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cAttrNameLoc, BridgedSourceLoc cLParenLoc, - BridgedStringRef cFileName, BridgedSourceLoc cRParenLoc); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceLoc attrNameLoc, swift::SourceLoc lParenLoc, + BridgedStringRef cFileName, swift::SourceLoc rParenLoc); SWIFT_NAME( "BridgedProjectedValuePropertyAttr.createParsed(_:atLoc:range:name:)") BridgedProjectedValuePropertyAttr BridgedProjectedValuePropertyAttr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, - BridgedIdentifier cName); + swift::SourceLoc atLoc, + swift::SourceRange range, + swift::Identifier name); SWIFT_NAME("BridgedRawDocCommentAttr.createParsed(_:range:)") BridgedRawDocCommentAttr BridgedRawDocCommentAttr_createParsed(BridgedASTContext cContext, - BridgedCharSourceRange cRange); + swift::CharSourceRange range); SWIFT_NAME("BridgedRawLayoutAttr.createParsed(_:atLoc:range:size:alignment:)") BridgedRawLayoutAttr BridgedStorageRestrictionsAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, size_t size, size_t alignment); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, size_t size, size_t alignment); SWIFT_NAME("BridgedRawLayoutAttr.createParsed(_:atLoc:range:like:moveAsLike:)") BridgedRawLayoutAttr BridgedStorageRestrictionsAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedTypeRepr cLikeType, bool moveAsLike); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedTypeRepr cLikeType, bool moveAsLike); SWIFT_NAME("BridgedRawLayoutAttr.createParsed(_:atLoc:range:likeArrayOf:count:" "moveAsLike:)") BridgedRawLayoutAttr BridgedStorageRestrictionsAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedTypeRepr cLikeType, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedTypeRepr cLikeType, BridgedTypeRepr cCountType, bool moveAsLike); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedReferenceOwnership { @@ -1331,25 +1201,25 @@ swift::ReferenceOwnership unbridged(BridgedReferenceOwnership kind); SWIFT_NAME("BridgedReferenceOwnershipAttr.createParsed(_:atLoc:range:kind:)") BridgedReferenceOwnershipAttr BridgedReferenceOwnershipAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedReferenceOwnership cKind); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedReferenceOwnership cKind); SWIFT_NAME("BridgedSectionAttr.createParsed(_:atLoc:range:name:)") BridgedSectionAttr BridgedSectionAttr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, + swift::SourceLoc atLoc, + swift::SourceRange range, BridgedStringRef cName); SWIFT_NAME("BridgedSemanticsAttr.createParsed(_:atLoc:range:value:)") BridgedSemanticsAttr BridgedSemanticsAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedStringRef cValue); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedStringRef cValue); SWIFT_NAME("BridgedSetterAccessAttr.createParsed(_:range:accessLevel:)") BridgedSetterAccessAttr BridgedSetterAccessAttr_createParsed(BridgedASTContext cContext, - BridgedSourceRange cRange, - BridgedAccessLevel cAccessLevel); + swift::SourceRange range, + swift::AccessLevel accessLevel); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedSpecializationKind : uint8_t { BridgedSpecializationKindFull, @@ -1359,8 +1229,17 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedSpecializationKind : uint8_t { SWIFT_NAME("BridgedSpecializeAttr.createParsed(_:atLoc:range:whereClause:" "exported:kind:taretFunction:spiGroups:availableAttrs:)") BridgedSpecializeAttr BridgedSpecializeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedNullableTrailingWhereClause cWhereClause, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedNullableTrailingWhereClause cWhereClause, + bool exported, BridgedSpecializationKind cKind, + BridgedDeclNameRef cTargetFunction, BridgedArrayRef cSPIGroups, + BridgedArrayRef cAvailableAttrs); + +SWIFT_NAME("BridgedSpecializedAttr.createParsed(_:atLoc:range:whereClause:" + "exported:kind:taretFunction:spiGroups:availableAttrs:)") +BridgedSpecializedAttr BridgedSpecializedAttr_createParsed( + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedNullableTrailingWhereClause cWhereClause, bool exported, BridgedSpecializationKind cKind, BridgedDeclNameRef cTargetFunction, BridgedArrayRef cSPIGroups, BridgedArrayRef cAvailableAttrs); @@ -1368,33 +1247,33 @@ BridgedSpecializeAttr BridgedSpecializeAttr_createParsed( SWIFT_NAME( "BridgedSPIAccessControlAttr.createParsed(_:atLoc:range:spiGroupName:)") BridgedSPIAccessControlAttr BridgedSPIAccessControlAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedIdentifier cSPIGroupName); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, swift::Identifier SPIGroupName); SWIFT_NAME("BridgedSILGenNameAttr.createParsed(_:atLoc:range:name:isRaw:)") BridgedSILGenNameAttr BridgedSILGenNameAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedStringRef cName, bool isRaw); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedStringRef cName, bool isRaw); SWIFT_NAME( "BridgedTransposeAttr.createParsed(_:atLoc:range:baseType:originalName:" "originalNameLoc:params:)") BridgedTransposeAttr BridgedTransposeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedNullableTypeRepr cBaseType, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedNullableTypeRepr cBaseType, BridgedDeclNameRef cOriginalName, BridgedDeclNameLoc cOriginalNameLoc, BridgedArrayRef cParams); SWIFT_NAME("BridgedTypeEraserAttr.createParsed(_:atLoc:range:typeExpr:)") BridgedTypeEraserAttr BridgedTypeEraserAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedTypeExpr cTypeExpr); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedTypeExpr cTypeExpr); SWIFT_NAME( "BridgedUnavailableFromAsyncAttr.createParsed(_:atLoc:range:message:)") BridgedUnavailableFromAsyncAttr BridgedUnavailableFromAsyncAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, BridgedStringRef cMessage); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceRange range, BridgedStringRef cMessage); //===----------------------------------------------------------------------===// // MARK: Decls @@ -1416,9 +1295,9 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedStaticSpelling { }; struct BridgedAccessorRecord { - BridgedSourceLoc lBraceLoc; + swift::SourceLoc lBraceLoc; BridgedArrayRef accessors; - BridgedSourceLoc rBraceLoc; + swift::SourceLoc rBraceLoc; }; SWIFT_NAME("BridgedAccessorDecl.createParsed(_:declContext:kind:storage:" @@ -1426,10 +1305,10 @@ SWIFT_NAME("BridgedAccessorDecl.createParsed(_:declContext:kind:storage:" "throwsSpecifierLoc:thrownType:)") BridgedAccessorDecl BridgedAccessorDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedAccessorKind cKind, BridgedAbstractStorageDecl cStorage, - BridgedSourceLoc cDeclLoc, BridgedSourceLoc cAccessorKeywordLoc, - BridgedNullableParameterList cParamList, BridgedSourceLoc cAsyncLoc, - BridgedSourceLoc cThrowsLoc, BridgedNullableTypeRepr cThrownType); + swift::AccessorKind Kind, BridgedAbstractStorageDecl cStorage, + swift::SourceLoc declLoc, swift::SourceLoc accessorKeywordLoc, + BridgedNullableParameterList cParamList, swift::SourceLoc asyncLoc, + swift::SourceLoc throwsLoc, BridgedNullableTypeRepr cThrownType); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedVarDeclIntroducer { BridgedVarDeclIntroducerLet = 0, @@ -1442,8 +1321,8 @@ SWIFT_NAME("BridgedPatternBindingDecl.createParsed(_:declContext:attributes:" "staticLoc:staticSpelling:introducerLoc:introducer:entries:)") BridgedPatternBindingDecl BridgedPatternBindingDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedDeclAttributes cAttrs, BridgedSourceLoc cStaticLoc, - BridgedStaticSpelling cStaticSpelling, BridgedSourceLoc cIntroducerLoc, + BridgedDeclAttributes cAttrs, swift::SourceLoc staticLoc, + BridgedStaticSpelling cStaticSpelling, swift::SourceLoc introducerLoc, BridgedVarDeclIntroducer cIntorducer, BridgedArrayRef cBindingEntries); SWIFT_NAME("BridgedParamDecl.createParsed(_:declContext:specifierLoc:argName:" @@ -1451,9 +1330,9 @@ SWIFT_NAME("BridgedParamDecl.createParsed(_:declContext:specifierLoc:argName:" "defaultValueInitContext:)") BridgedParamDecl BridgedParamDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cSpecifierLoc, BridgedIdentifier cArgName, - BridgedSourceLoc cArgNameLoc, BridgedIdentifier cParamName, - BridgedSourceLoc cParamNameLoc, BridgedNullableExpr defaultValue, + swift::SourceLoc specifierLoc, swift::Identifier argName, + swift::SourceLoc argNameLoc, swift::Identifier paramName, + swift::SourceLoc paramNameLoc, BridgedNullableExpr defaultValue, BridgedNullableDefaultArgumentInitializer cDefaultArgumentInitContext); SWIFT_NAME("BridgedParamDecl.setTypeRepr(self:_:)") @@ -1498,11 +1377,11 @@ SWIFT_NAME("BridgedFuncDecl.createParsed(_:declContext:staticLoc:" "throwsSpecifierLoc:thrownType:returnType:genericWhereClause:)") BridgedFuncDecl BridgedFuncDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cStaticLoc, BridgedStaticSpelling cStaticSpelling, - BridgedSourceLoc cFuncKeywordLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedNullableGenericParamList genericParamList, - BridgedParameterList parameterList, BridgedSourceLoc cAsyncLoc, - BridgedSourceLoc cThrowsLoc, BridgedNullableTypeRepr thrownType, + swift::SourceLoc staticLoc, BridgedStaticSpelling cStaticSpelling, + swift::SourceLoc funcKeywordLoc, swift::Identifier name, + swift::SourceLoc nameLoc, BridgedNullableGenericParamList genericParamList, + BridgedParameterList parameterList, swift::SourceLoc asyncLoc, + swift::SourceLoc throwsLoc, BridgedNullableTypeRepr thrownType, BridgedNullableTypeRepr returnType, BridgedNullableTrailingWhereClause opaqueGenericWhereClause); @@ -1512,10 +1391,10 @@ SWIFT_NAME( "asyncSpecifierLoc:throwsSpecifierLoc:thrownType:genericWhereClause:)") BridgedConstructorDecl BridgedConstructorDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cInitKeywordLoc, BridgedSourceLoc cFailabilityMarkLoc, + swift::SourceLoc initKeywordLoc, swift::SourceLoc failabilityMarkLoc, bool isIUO, BridgedNullableGenericParamList genericParams, - BridgedParameterList parameterList, BridgedSourceLoc cAsyncLoc, - BridgedSourceLoc cThrowsLoc, BridgedNullableTypeRepr thrownType, + BridgedParameterList parameterList, swift::SourceLoc asyncLoc, + swift::SourceLoc throwsLoc, BridgedNullableTypeRepr thrownType, BridgedNullableTrailingWhereClause genericWhereClause); SWIFT_NAME( @@ -1523,16 +1402,16 @@ SWIFT_NAME( BridgedDestructorDecl BridgedDestructorDecl_createParsed(BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cDeinitKeywordLoc); + swift::SourceLoc deinitKeywordLoc); SWIFT_NAME( "BridgedTypeAliasDecl.createParsed(_:declContext:typealiasKeywordLoc:name:" "nameLoc:genericParamList:equalLoc:underlyingType:genericWhereClause:)") BridgedTypeAliasDecl BridgedTypeAliasDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cAliasKeywordLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedNullableGenericParamList genericParamList, - BridgedSourceLoc cEqualLoc, BridgedTypeRepr underlyingType, + swift::SourceLoc aliasKeywordLoc, swift::Identifier name, + swift::SourceLoc nameLoc, BridgedNullableGenericParamList genericParamList, + swift::SourceLoc equalLoc, BridgedTypeRepr underlyingType, BridgedNullableTrailingWhereClause genericWhereClause); SWIFT_NAME("BridgedExtensionDecl.setParsedMembers(self:_:fingerprint:)") @@ -1545,25 +1424,25 @@ SWIFT_NAME( "genericParamList:inheritedTypes:genericWhereClause:braceRange:)") BridgedNominalTypeDecl BridgedEnumDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cEnumKeywordLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedNullableGenericParamList genericParamList, + swift::SourceLoc enumKeywordLoc, swift::Identifier name, + swift::SourceLoc nameLoc, BridgedNullableGenericParamList genericParamList, BridgedArrayRef cInheritedTypes, BridgedNullableTrailingWhereClause genericWhereClause, - BridgedSourceRange cBraceRange); + swift::SourceRange braceRange); SWIFT_NAME( "BridgedEnumCaseDecl.createParsed(declContext:caseKeywordLoc:elements:)") BridgedEnumCaseDecl BridgedEnumCaseDecl_createParsed(BridgedDeclContext cDeclContext, - BridgedSourceLoc cCaseKeywordLoc, + swift::SourceLoc caseKeywordLoc, BridgedArrayRef cElements); SWIFT_NAME("BridgedEnumElementDecl.createParsed(_:declContext:name:nameLoc:" "parameterList:equalsLoc:rawValue:)") BridgedEnumElementDecl BridgedEnumElementDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedIdentifier cName, BridgedSourceLoc cNameLoc, - BridgedNullableParameterList parameterList, BridgedSourceLoc cEqualsLoc, + swift::Identifier name, swift::SourceLoc nameLoc, + BridgedNullableParameterList parameterList, swift::SourceLoc equalsLoc, BridgedNullableExpr opaqueRawValue); SWIFT_NAME("BridgedStructDecl.createParsed(_:declContext:structKeywordLoc:name:" @@ -1571,22 +1450,22 @@ SWIFT_NAME("BridgedStructDecl.createParsed(_:declContext:structKeywordLoc:name:" "braceRange:)") BridgedNominalTypeDecl BridgedStructDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cStructKeywordLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedNullableGenericParamList genericParamList, + swift::SourceLoc structKeywordLoc, swift::Identifier name, + swift::SourceLoc nameLoc, BridgedNullableGenericParamList genericParamList, BridgedArrayRef cInheritedTypes, BridgedNullableTrailingWhereClause genericWhereClause, - BridgedSourceRange cBraceRange); + swift::SourceRange braceRange); SWIFT_NAME( "BridgedClassDecl.createParsed(_:declContext:classKeywordLoc:name:nameLoc:" "genericParamList:inheritedTypes:genericWhereClause:braceRange:isActor:)") BridgedNominalTypeDecl BridgedClassDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cClassKeywordLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedNullableGenericParamList genericParamList, + swift::SourceLoc classKeywordLoc, swift::Identifier name, + swift::SourceLoc nameLoc, BridgedNullableGenericParamList genericParamList, BridgedArrayRef cInheritedTypes, BridgedNullableTrailingWhereClause genericWhereClause, - BridgedSourceRange cBraceRange, bool isActor); + swift::SourceRange braceRange, bool isActor); SWIFT_NAME( "BridgedProtocolDecl.createParsed(_:declContext:protocolKeywordLoc:name:" @@ -1594,19 +1473,19 @@ SWIFT_NAME( "genericWhereClause:braceRange:)") BridgedNominalTypeDecl BridgedProtocolDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cProtocolKeywordLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedArrayRef cPrimaryAssociatedTypeNames, + swift::SourceLoc protocolKeywordLoc, swift::Identifier name, + swift::SourceLoc nameLoc, BridgedArrayRef cPrimaryAssociatedTypeNames, BridgedArrayRef cInheritedTypes, BridgedNullableTrailingWhereClause genericWhereClause, - BridgedSourceRange cBraceRange); + swift::SourceRange braceRange); SWIFT_NAME("BridgedAssociatedTypeDecl.createParsed(_:declContext:" "associatedtypeKeywordLoc:name:nameLoc:inheritedTypes:defaultType:" "genericWhereClause:)") BridgedAssociatedTypeDecl BridgedAssociatedTypeDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cAssociatedtypeKeywordLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedArrayRef cInheritedTypes, + swift::SourceLoc associatedtypeKeywordLoc, swift::Identifier name, + swift::SourceLoc nameLoc, BridgedArrayRef cInheritedTypes, BridgedNullableTypeRepr opaqueDefaultType, BridgedNullableTrailingWhereClause genericWhereClause); @@ -1616,29 +1495,29 @@ SWIFT_NAME( "genericWhereClause:)") BridgedMacroDecl BridgedMacroDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cMacroLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedNullableGenericParamList cGenericParams, - BridgedParameterList cParams, BridgedSourceLoc cArrowLoc, + swift::SourceLoc macroLoc, swift::Identifier name, swift::SourceLoc nameLoc, + BridgedNullableGenericParamList cGenericParams, + BridgedParameterList cParams, swift::SourceLoc arrowLoc, BridgedNullableTypeRepr cResultType, BridgedNullableExpr cDefinition, BridgedNullableTrailingWhereClause genericWhereClause); SWIFT_NAME("BridgedMacroExpansionDecl.createParsed(_:poundLoc:macroNameRef:" "macroNameLoc:leftAngleLoc:genericArgs:rightAngleLoc:args:)") BridgedMacroExpansionDecl BridgedMacroExpansionDecl_createParsed( - BridgedDeclContext cDeclContext, BridgedSourceLoc cPoundLoc, + BridgedDeclContext cDeclContext, swift::SourceLoc poundLoc, BridgedDeclNameRef cMacroNameRef, BridgedDeclNameLoc cMacroNameLoc, - BridgedSourceLoc cLeftAngleLoc, BridgedArrayRef cGenericArgs, - BridgedSourceLoc cRightAngleLoc, BridgedNullableArgumentList cArgList); + swift::SourceLoc leftAngleLoc, BridgedArrayRef cGenericArgs, + swift::SourceLoc rightAngleLoc, BridgedNullableArgumentList cArgList); SWIFT_NAME( "BridgedExtensionDecl.createParsed(_:declContext:extensionKeywordLoc:" "extendedType:inheritedTypes:genericWhereClause:braceRange:)") BridgedExtensionDecl BridgedExtensionDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cExtensionKeywordLoc, BridgedTypeRepr opaqueExtendedType, + swift::SourceLoc extensionKeywordLoc, BridgedTypeRepr opaqueExtendedType, BridgedArrayRef cInheritedTypes, BridgedNullableTrailingWhereClause genericWhereClause, - BridgedSourceRange cBraceRange); + swift::SourceRange braceRange); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedOperatorFixity { BridgedOperatorFixityInfix, @@ -1649,23 +1528,16 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedOperatorFixity { SWIFT_NAME("BridgedMissingDecl.create(_:declContext:loc:)") BridgedMissingDecl BridgedMissingDecl_create(BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("BridgedOperatorDecl.createParsed(_:declContext:fixity:" "operatorKeywordLoc:name:nameLoc:colonLoc:precedenceGroupName:" "precedenceGroupLoc:)") BridgedOperatorDecl BridgedOperatorDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedOperatorFixity cFixity, BridgedSourceLoc cOperatorKeywordLoc, - BridgedIdentifier cName, BridgedSourceLoc cNameLoc, - BridgedSourceLoc cColonLoc, BridgedIdentifier cPrecedenceGroupName, - BridgedSourceLoc cPrecedenceGroupLoc); - -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedAssociativity { - BridgedAssociativityNone, - BridgedAssociativityLeft, - BridgedAssociativityRight, -}; + BridgedOperatorFixity cFixity, swift::SourceLoc operatorKeywordLoc, + swift::Identifier name, swift::SourceLoc nameLoc, swift::SourceLoc colonLoc, + swift::Identifier precedenceGroupName, swift::SourceLoc precedenceGroupLoc); SWIFT_NAME("BridgedPrecedenceGroupDecl.createParsed(declContext:" "precedencegroupKeywordLoc:name:nameLoc:leftBraceLoc:" @@ -1674,16 +1546,14 @@ SWIFT_NAME("BridgedPrecedenceGroupDecl.createParsed(declContext:" "higherThanKeywordLoc:higherThanNames:lowerThanKeywordLoc:" "lowerThanNames:rightBraceLoc:)") BridgedPrecedenceGroupDecl BridgedPrecedenceGroupDecl_createParsed( - BridgedDeclContext cDeclContext, - BridgedSourceLoc cPrecedencegroupKeywordLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedSourceLoc cLeftBraceLoc, - BridgedSourceLoc cAssociativityKeywordLoc, - BridgedSourceLoc cAssociativityValueLoc, - BridgedAssociativity cAssociativity, BridgedSourceLoc cAssignmentKeywordLoc, - BridgedSourceLoc cAssignmentValueLoc, bool isAssignment, - BridgedSourceLoc cHigherThanKeywordLoc, BridgedArrayRef cHigherThanNames, - BridgedSourceLoc cLowerThanKeywordLoc, BridgedArrayRef cLowerThanNames, - BridgedSourceLoc cRightBraceLoc); + BridgedDeclContext cDeclContext, swift::SourceLoc precedencegroupKeywordLoc, + swift::Identifier name, swift::SourceLoc nameLoc, + swift::SourceLoc leftBraceLoc, swift::SourceLoc associativityKeywordLoc, + swift::SourceLoc associativityValueLoc, swift::Associativity associativity, + swift::SourceLoc assignmentKeywordLoc, swift::SourceLoc assignmentValueLoc, + bool isAssignment, swift::SourceLoc higherThanKeywordLoc, + BridgedArrayRef cHigherThanNames, swift::SourceLoc lowerThanKeywordLoc, + BridgedArrayRef cLowerThanNames, swift::SourceLoc rightBraceLoc); enum ENUM_EXTENSIBILITY_ATTR(open) BridgedImportKind { BridgedImportKindModule, @@ -1700,18 +1570,31 @@ SWIFT_NAME("BridgedImportDecl.createParsed(_:declContext:importKeywordLoc:" "importKind:importKindLoc:path:)") BridgedImportDecl BridgedImportDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cImportKeywordLoc, BridgedImportKind cImportKind, - BridgedSourceLoc cImportKindLoc, BridgedArrayRef cImportPathElements); + swift::SourceLoc importKeywordLoc, BridgedImportKind cImportKind, + swift::SourceLoc importKindLoc, BridgedArrayRef cImportPathElements); + +enum ENUM_EXTENSIBILITY_ATTR(open) BridgedUsingSpecifier { + BridgedUsingSpecifierMainActor, + BridgedUsingSpecifierNonisolated, +}; + +SWIFT_NAME("BridgedUsingDecl.createParsed(_:declContext:usingKeywordLoc:" + "specifierLoc:specifier:)") +BridgedUsingDecl BridgedUsingDecl_createParsed(BridgedASTContext cContext, + BridgedDeclContext cDeclContext, + swift::SourceLoc usingKeywordLoc, + swift::SourceLoc specifierLoc, + BridgedUsingSpecifier specifier); SWIFT_NAME("BridgedSubscriptDecl.createParsed(_:declContext:staticLoc:" "staticSpelling:subscriptKeywordLoc:genericParamList:parameterList:" "arrowLoc:returnType:genericWhereClause:)") BridgedSubscriptDecl BridgedSubscriptDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cStaticLoc, BridgedStaticSpelling cStaticSpelling, - BridgedSourceLoc cSubscriptKeywordLoc, + swift::SourceLoc staticLoc, BridgedStaticSpelling cStaticSpelling, + swift::SourceLoc subscriptKeywordLoc, BridgedNullableGenericParamList cGenericParamList, - BridgedParameterList cParamList, BridgedSourceLoc cArrowLoc, + BridgedParameterList cParamList, swift::SourceLoc arrowLoc, BridgedTypeRepr returnType, BridgedNullableTrailingWhereClause genericWhereClause); @@ -1776,7 +1659,8 @@ void BridgedNominalTypeDecl_setParsedMembers(BridgedNominalTypeDecl decl, BridgedFingerprint fingerprint); SWIFT_NAME("BridgedNominalTypeDecl.getSourceLocation(self:)") -BRIDGED_INLINE BridgedSourceLoc BridgedNominalTypeDecl_getSourceLocation(BridgedNominalTypeDecl decl); +BRIDGED_INLINE swift::SourceLoc +BridgedNominalTypeDecl_getSourceLocation(BridgedNominalTypeDecl decl); //===----------------------------------------------------------------------===// // MARK: SubscriptDecl @@ -1805,8 +1689,8 @@ BridgedVarDecl_asAbstractStorageDecl(BridgedVarDecl decl); //===----------------------------------------------------------------------===// struct BridgedCallArgument { - BridgedSourceLoc labelLoc; - BridgedIdentifier label; + swift::SourceLoc labelLoc; + swift::Identifier label; BridgedExpr argExpr; BRIDGED_INLINE swift::Argument unbridged() const; @@ -1820,49 +1704,49 @@ BridgedArgumentList_createImplicitUnlabeled(BridgedASTContext cContext, SWIFT_NAME("BridgedArgumentList.createParsed(_:lParenLoc:args:rParenLoc:" "firstTrailingClosureIndex:)") BridgedArgumentList BridgedArgumentList_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLParenLoc, - BridgedArrayRef cArgs, BridgedSourceLoc cRParenLoc, + BridgedASTContext cContext, swift::SourceLoc lParenLoc, + BridgedArrayRef cArgs, swift::SourceLoc rParenLoc, size_t cFirstTrailingClosureIndex); SWIFT_NAME("BridgedArrayExpr.createParsed(_:lSquareLoc:elements:commaLocs:" "rSquareLoc:)") BridgedArrayExpr BridgedArrayExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLLoc, + swift::SourceLoc lLoc, BridgedArrayRef elements, BridgedArrayRef commas, - BridgedSourceLoc cRLoc); + swift::SourceLoc rLoc); SWIFT_NAME( "BridgedArrowExpr.createParsed(_:asyncLoc:throwsLoc:thrownType:arrowLoc:)") BridgedArrowExpr BridgedArrowExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAsyncLoc, - BridgedSourceLoc cThrowsLoc, + swift::SourceLoc asyncLoc, + swift::SourceLoc throwsLoc, BridgedNullableExpr cThrownType, - BridgedSourceLoc cArrowLoc); + swift::SourceLoc arrowLoc); SWIFT_NAME("BridgedAssignExpr.createParsed(_:equalsLoc:)") BridgedAssignExpr BridgedAssignExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cEqualsLoc); + swift::SourceLoc equalsLoc); SWIFT_NAME("BridgedAwaitExpr.createParsed(_:awaitLoc:subExpr:)") BridgedAwaitExpr BridgedAwaitExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAwaitLoc, + swift::SourceLoc awaitLoc, BridgedExpr cSubExpr); SWIFT_NAME("BridgedBindOptionalExpr.createParsed(_:subExpr:questionLoc:)") BridgedBindOptionalExpr BridgedBindOptionalExpr_createParsed(BridgedASTContext cContext, BridgedExpr cSubExpr, - BridgedSourceLoc cQuestionLoc); + swift::SourceLoc questionLoc); SWIFT_NAME("BridgedBooleanLiteralExpr.createParsed(_:value:loc:)") BridgedBooleanLiteralExpr BridgedBooleanLiteralExpr_createParsed(BridgedASTContext cContext, bool value, - BridgedSourceLoc cTokenLoc); + swift::SourceLoc tokenLoc); SWIFT_NAME("BridgedBorrowExpr.createParsed(_:borrowLoc:subExpr:)") BridgedBorrowExpr BridgedBorrowExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cBorrowLoc, + swift::SourceLoc borrowLoc, BridgedExpr cSubExpr); SWIFT_NAME("BridgedCallExpr.createParsed(_:fn:args:)") @@ -1875,23 +1759,21 @@ class BridgedCaptureListEntry { public: BRIDGED_INLINE BridgedCaptureListEntry(swift::CaptureListEntry CLE); - BRIDGED_INLINE swift::CaptureListEntry unbridged() const; + + BRIDGED_INLINE + SWIFT_COMPUTED_PROPERTY + BridgedVarDecl getVarDecl() const; }; SWIFT_NAME("BridgedCaptureListEntry.createParsed(_:declContext:ownership:" "ownershipRange:name:nameLoc:equalLoc:initializer:)") BridgedCaptureListEntry BridegedCaptureListEntry_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedReferenceOwnership cOwnershipKind, - BridgedSourceRange cOwnershipRange, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedSourceLoc cEqualLoc, + BridgedReferenceOwnership cOwnershipKind, swift::SourceRange ownershipRange, + swift::Identifier name, swift::SourceLoc nameLoc, swift::SourceLoc equalLoc, BridgedExpr cInitializer); -SWIFT_NAME("getter:BridgedCaptureListEntry.varDecl(self:)") -BRIDGED_INLINE BridgedVarDecl -BridegedCaptureListEntry_getVar(BridgedCaptureListEntry entry); - SWIFT_NAME("BridgedCaptureListExpr.createParsed(_:captureList:closure:)") BridgedCaptureListExpr BridgedCaptureListExpr_createParsed(BridgedASTContext cContext, BridgedArrayRef cCaptureList, @@ -1902,12 +1784,12 @@ SWIFT_NAME("BridgedClosureExpr.createParsed(_:declContext:attributes:" "thrownType:arrowLoc:explicitResultType:inLoc:)") BridgedClosureExpr BridgedClosureExpr_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedDeclAttributes cAttributes, BridgedSourceRange cBracketRange, + BridgedDeclAttributes cAttributes, swift::SourceRange bracketRange, BridgedNullableVarDecl cCapturedSelfDecl, - BridgedNullableParameterList cParameterList, BridgedSourceLoc cAsyncLoc, - BridgedSourceLoc cThrowsLoc, BridgedNullableTypeRepr cThrownType, - BridgedSourceLoc cArrowLoc, BridgedNullableTypeRepr cExplicitResultType, - BridgedSourceLoc cInLoc); + BridgedNullableParameterList cParameterList, swift::SourceLoc asyncLoc, + swift::SourceLoc throwsLoc, BridgedNullableTypeRepr cThrownType, + swift::SourceLoc arrowLoc, BridgedNullableTypeRepr cExplicitResultType, + swift::SourceLoc inLoc); SWIFT_NAME("BridgedClosureExpr.getParameterList(self:)") BridgedParameterList @@ -1929,25 +1811,25 @@ void BridgedClosureExpr_setBody(BridgedClosureExpr cClosure, SWIFT_NAME("BridgedCoerceExpr.createParsed(_:asLoc:type:)") BridgedCoerceExpr BridgedCoerceExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAsLoc, + swift::SourceLoc asLoc, BridgedTypeRepr cType); SWIFT_NAME( "BridgedConditionalCheckedCastExpr.createParsed(_:asLoc:questionLoc:type:)") BridgedConditionalCheckedCastExpr BridgedConditionalCheckedCastExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAsLoc, - BridgedSourceLoc cQuestionLoc, + swift::SourceLoc asLoc, + swift::SourceLoc questionLoc, BridgedTypeRepr cType); SWIFT_NAME("BridgedConsumeExpr.createParsed(_:consumeLoc:subExpr:)") BridgedConsumeExpr BridgedConsumeExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cConsumeLoc, + swift::SourceLoc consumeLoc, BridgedExpr cSubExpr); SWIFT_NAME("BridgedCopyExpr.createParsed(_:copyLoc:subExpr:)") BridgedCopyExpr BridgedCopyExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cCopyLoc, + swift::SourceLoc copyLoc, BridgedExpr cSubExpr); SWIFT_NAME("BridgedDeclRefExpr.create(_:decl:loc:isImplicit:)") @@ -1959,127 +1841,127 @@ BridgedDeclRefExpr BridgedDeclRefExpr_create(BridgedASTContext cContext, SWIFT_NAME("BridgedDictionaryExpr.createParsed(_:lBracketLoc:elements:" "colonLocs:rBracketLoc:)") BridgedDictionaryExpr BridgedDictionaryExpr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLBracketLoc, + BridgedASTContext cContext, swift::SourceLoc lBracketLoc, BridgedArrayRef cElements, BridgedArrayRef cCommaLocs, - BridgedSourceLoc cRBracketLoc); + swift::SourceLoc rBracketLoc); SWIFT_NAME("BridgedDiscardAssignmentExpr.createParsed(_:loc:)") BridgedDiscardAssignmentExpr BridgedDiscardAssignmentExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("BridgedDotSelfExpr.createParsed(_:subExpr:dotLoc:selfLoc:)") BridgedDotSelfExpr BridgedDotSelfExpr_createParsed(BridgedASTContext cContext, BridgedExpr cSubExpr, - BridgedSourceLoc cDotLoc, - BridgedSourceLoc cSelfLoc); + swift::SourceLoc dotLoc, + swift::SourceLoc selfLoc); SWIFT_NAME("BridgedEditorPlaceholderExpr.createParsed(_:placeholder:loc:" "placeholderType:expansionType:)") BridgedEditorPlaceholderExpr BridgedEditorPlaceholderExpr_createParsed( - BridgedASTContext cContext, BridgedIdentifier cPlaceholderId, - BridgedSourceLoc cLoc, BridgedNullableTypeRepr cPlaceholderTyR, + BridgedASTContext cContext, swift::Identifier placeholderId, + swift::SourceLoc loc, BridgedNullableTypeRepr cPlaceholderTyR, BridgedNullableTypeRepr cExpansionTyR); SWIFT_NAME("BridgedErrorExpr.create(_:loc:)") BridgedErrorExpr BridgedErrorExpr_create(BridgedASTContext cContext, - BridgedSourceRange cRange); + swift::SourceRange range); SWIFT_NAME("BridgedFloatLiteralExpr.createParsed(_:value:loc:)") BridgedFloatLiteralExpr BridgedFloatLiteralExpr_createParsed(BridgedASTContext cContext, BridgedStringRef cStr, - BridgedSourceLoc cTokenLoc); + swift::SourceLoc tokenLoc); SWIFT_NAME("BridgedFloatLiteralExpr.setNegative(self:loc:)") BRIDGED_INLINE void BridgedFloatLiteralExpr_setNegative(BridgedFloatLiteralExpr cExpr, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("BridgedForceTryExpr.createParsed(_:tryLoc:subExpr:exclaimLoc:)") BridgedForceTryExpr BridgedForceTryExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cTryLoc, BridgedExpr cSubExpr, - BridgedSourceLoc cExclaimLoc); + swift::SourceLoc tryLoc, BridgedExpr cSubExpr, + swift::SourceLoc exclaimLoc); SWIFT_NAME("BridgedForceValueExpr.createParsed(_:subExpr:exclaimLoc:)") BridgedForceValueExpr BridgedForceValueExpr_createParsed(BridgedASTContext cContext, BridgedExpr cSubExpr, - BridgedSourceLoc cExclaimLoc); + swift::SourceLoc exclaimLoc); SWIFT_NAME( "BridgedForcedCheckedCastExpr.createParsed(_:asLoc:exclaimLoc:type:)") BridgedForcedCheckedCastExpr BridgedForcedCheckedCastExpr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAsLoc, - BridgedSourceLoc cExclaimLoc, BridgedTypeRepr cType); + BridgedASTContext cContext, swift::SourceLoc asLoc, + swift::SourceLoc exclaimLoc, BridgedTypeRepr cType); SWIFT_NAME("BridgedUnresolvedSpecializeExpr.createParsed(_:subExpr:lAngleLoc:" "arguments:rAngleLoc:)") BridgedUnresolvedSpecializeExpr BridgedUnresolvedSpecializeExpr_createParsed( BridgedASTContext cContext, BridgedExpr cSubExpr, - BridgedSourceLoc cLAngleLoc, BridgedArrayRef cArguments, - BridgedSourceLoc cRAngleLoc); + swift::SourceLoc lAngleLoc, BridgedArrayRef cArguments, + swift::SourceLoc rAngleLoc); SWIFT_NAME("BridgedUnsafeExpr.createParsed(_:unsafeLoc:subExpr:)") BridgedUnsafeExpr BridgedUnsafeExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cUnsafeLoc, + swift::SourceLoc unsafeLoc, BridgedExpr cSubExpr); SWIFT_NAME("BridgedInOutExpr.createParsed(_:loc:subExpr:)") BridgedInOutExpr BridgedInOutExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLoc, + swift::SourceLoc loc, BridgedExpr cSubExpr); SWIFT_NAME("BridgedIntegerLiteralExpr.createParsed(_:value:loc:)") BridgedIntegerLiteralExpr BridgedIntegerLiteralExpr_createParsed(BridgedASTContext cContext, BridgedStringRef cStr, - BridgedSourceLoc cTokenLoc); + swift::SourceLoc tokenLoc); SWIFT_NAME("BridgedIntegerLiteralExpr.setNegative(self:loc:)") BRIDGED_INLINE void BridgedIntegerLiteralExpr_setNegative(BridgedIntegerLiteralExpr cExpr, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("BridgedInterpolatedStringLiteralExpr.createParsed(_:loc:" "literalCapacity:interpolationCount:appendingExpr:)") BridgedInterpolatedStringLiteralExpr BridgedInterpolatedStringLiteralExpr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLoc, size_t literalCapacity, + BridgedASTContext cContext, swift::SourceLoc loc, size_t literalCapacity, size_t interpolationCount, BridgedTapExpr cAppendingExpr); SWIFT_NAME("BridgedIsExpr.createParsed(_:isLoc:type:)") BridgedIsExpr BridgedIsExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cIsLoc, + swift::SourceLoc isLoc, BridgedTypeRepr cType); SWIFT_NAME("BridgedKeyPathDotExpr.createParsed(_:loc:)") BridgedKeyPathDotExpr BridgedKeyPathDotExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("BridgedKeyPathExpr.createParsed(_:backslashLoc:parsedRoot:" "parsedPath:hasLeadingDot:)") BridgedKeyPathExpr BridgedKeyPathExpr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cBackslashLoc, + BridgedASTContext cContext, swift::SourceLoc backslashLoc, BridgedNullableExpr cParsedRoot, BridgedNullableExpr cParsedPath, bool hasLeadingDot); SWIFT_NAME("BridgedKeyPathExpr.createParsedPoundKeyPath(_:poundLoc:lParenLoc:" "names:nameLocs:rParenLoc:)") BridgedKeyPathExpr BridgedKeyPathExpr_createParsedPoundKeyPath( - BridgedASTContext cContext, BridgedSourceLoc cPoundLoc, - BridgedSourceLoc cLParenLoc, BridgedArrayRef cNames, - BridgedArrayRef cNameLocs, BridgedSourceLoc cRParenLoc); + BridgedASTContext cContext, swift::SourceLoc poundLoc, + swift::SourceLoc lParenLoc, BridgedArrayRef cNames, + BridgedArrayRef cNameLocs, swift::SourceLoc rParenLoc); SWIFT_NAME("BridgedMacroExpansionExpr.createParsed(_:poundLoc:macroNameRef:" "macroNameLoc:leftAngleLoc:genericArgs:rightAngleLoc:args:)") BridgedMacroExpansionExpr BridgedMacroExpansionExpr_createParsed( - BridgedDeclContext cDeclContext, BridgedSourceLoc cPoundLoc, + BridgedDeclContext cDeclContext, swift::SourceLoc poundLoc, BridgedDeclNameRef cMacroNameRef, BridgedDeclNameLoc cMacroNameLoc, - BridgedSourceLoc cLeftAngleLoc, BridgedArrayRef cGenericArgs, - BridgedSourceLoc cRightAngleLoc, BridgedNullableArgumentList cArgList); + swift::SourceLoc leftAngleLoc, BridgedArrayRef cGenericArgs, + swift::SourceLoc rightAngleLoc, BridgedNullableArgumentList cArgList); enum ENUM_EXTENSIBILITY_ATTR(open) BridgedMagicIdentifierLiteralKind : uint8_t { #define MAGIC_IDENTIFIER(NAME, STRING) \ @@ -2096,12 +1978,12 @@ SWIFT_NAME("BridgedMagicIdentifierLiteralExpr.createParsed(_:kind:loc:)") BridgedMagicIdentifierLiteralExpr BridgedMagicIdentifierLiteralExpr_createParsed( BridgedASTContext cContext, BridgedMagicIdentifierLiteralKind cKind, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("BridgedNilLiteralExpr.createParsed(_:nilKeywordLoc:)") BridgedNilLiteralExpr BridgedNilLiteralExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cNilKeywordLoc); + swift::SourceLoc nilKeywordLoc); enum ENUM_EXTENSIBILITY_ATTR(open) BridgedObjCSelectorKind { BridgedObjCSelectorKindMethod, @@ -2113,9 +1995,9 @@ SWIFT_NAME("BridgedObjCSelectorExpr.createParsed(_:kind:keywordLoc:lParenLoc:" "modifierLoc:subExpr:rParenLoc:)") BridgedObjCSelectorExpr BridgedObjCSelectorExpr_createParsed( BridgedASTContext cContext, BridgedObjCSelectorKind cKind, - BridgedSourceLoc cKeywordLoc, BridgedSourceLoc cLParenLoc, - BridgedSourceLoc cModifierLoc, BridgedExpr cSubExpr, - BridgedSourceLoc cRParenLoc); + swift::SourceLoc keywordLoc, swift::SourceLoc lParenLoc, + swift::SourceLoc modifierLoc, BridgedExpr cSubExpr, + swift::SourceLoc rParenLoc); enum ENUM_EXTENSIBILITY_ATTR(open) BridgedObjectLiteralKind : size_t { #define POUND_OBJECT_LITERAL(Name, Desc, Proto) BridgedObjectLiteralKind_##Name, @@ -2129,31 +2011,31 @@ BridgedObjectLiteralKind_fromString(BridgedStringRef cStr); SWIFT_NAME("BridgedObjectLiteralExpr.createParsed(_:poundLoc:kind:args:)") BridgedObjectLiteralExpr BridgedObjectLiteralExpr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cPoundLoc, + BridgedASTContext cContext, swift::SourceLoc poundLoc, BridgedObjectLiteralKind cKind, BridgedArgumentList args); SWIFT_NAME("BridgedOptionalTryExpr.createParsed(_:tryLoc:subExpr:questionLoc:)") BridgedOptionalTryExpr BridgedOptionalTryExpr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cTryLoc, BridgedExpr cSubExpr, - BridgedSourceLoc cQuestionLoc); + BridgedASTContext cContext, swift::SourceLoc tryLoc, BridgedExpr cSubExpr, + swift::SourceLoc questionLoc); SWIFT_NAME("BridgedPackElementExpr.createParsed(_:eachLoc:packRefExpr:)") BridgedPackElementExpr BridgedPackElementExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cEachLoc, + swift::SourceLoc eachLoc, BridgedExpr cPackRefExpr); SWIFT_NAME("BridgedPackExpansionExpr.createParsed(_:repeatLoc:patternExpr:)") BridgedPackExpansionExpr BridgedPackExpansionExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cRepeatLoc, + swift::SourceLoc repeatLoc, BridgedExpr cPatternExpr); SWIFT_NAME("BridgedParenExpr.createParsed(_:leftParenLoc:expr:rightParenLoc:)") BridgedParenExpr BridgedParenExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLParen, + swift::SourceLoc lParen, BridgedExpr cExpr, - BridgedSourceLoc cRParen); + swift::SourceLoc rParen); SWIFT_NAME("BridgedPostfixUnaryExpr.createParsed(_:operator:operand:)") BridgedPostfixUnaryExpr @@ -2183,13 +2065,13 @@ class BridgedRegexLiteralPatternFeatureKind final { }; class BridgedRegexLiteralPatternFeature final { - BridgedCharSourceRange Range; + swift::CharSourceRange Range; BridgedRegexLiteralPatternFeatureKind Kind; public: SWIFT_NAME("init(kind:at:)") BridgedRegexLiteralPatternFeature(BridgedRegexLiteralPatternFeatureKind kind, - BridgedCharSourceRange range) + swift::CharSourceRange range) : Range(range), Kind(kind) {} using UnbridgedTy = swift::RegexLiteralPatternFeature; @@ -2230,7 +2112,7 @@ class BridgedRegexLiteralPatternFeatures final { SWIFT_NAME("BridgedRegexLiteralExpr.createParsed(_:loc:regexText:)") BridgedRegexLiteralExpr BridgedRegexLiteralExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLoc, + swift::SourceLoc loc, BridgedStringRef cRegexText); SWIFT_NAME("BridgedSequenceExpr.createParsed(_:exprs:)") @@ -2247,12 +2129,11 @@ SWIFT_NAME("BridgedStringLiteralExpr.createParsed(_:value:loc:)") BridgedStringLiteralExpr BridgedStringLiteralExpr_createParsed(BridgedASTContext cContext, BridgedStringRef cStr, - BridgedSourceLoc cTokenLoc); + swift::SourceLoc tokenLoc); SWIFT_NAME("BridgedSuperRefExpr.createParsed(_:superLoc:)") -BridgedSuperRefExpr -BridgedSuperRefExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cSuperLoc); +BridgedSuperRefExpr BridgedSuperRefExpr_createParsed(BridgedASTContext cContext, + swift::SourceLoc superLoc); SWIFT_NAME("BridgedSubscriptExpr.createParsed(_:baseExpr:args:)") BridgedSubscriptExpr @@ -2265,20 +2146,21 @@ BridgedTapExpr BridgedTapExpr_create(BridgedASTContext cContext, BridgedBraceStmt cBody); SWIFT_NAME("BridgedTernaryExpr.createParsed(_:questionLoc:thenExpr:colonLoc:)") -BridgedTernaryExpr BridgedTernaryExpr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cQuestionLoc, - BridgedExpr cThenExpr, BridgedSourceLoc cColonLoc); +BridgedTernaryExpr BridgedTernaryExpr_createParsed(BridgedASTContext cContext, + swift::SourceLoc questionLoc, + BridgedExpr cThenExpr, + swift::SourceLoc colonLoc); SWIFT_NAME("BridgedTryExpr.createParsed(_:tryLoc:subExpr:)") BridgedTryExpr BridgedTryExpr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cTryLoc, + swift::SourceLoc tryLoc, BridgedExpr cSubExpr); SWIFT_NAME("BridgedTupleExpr.createParsed(_:leftParenLoc:exprs:labels:" "labelLocs:rightParenLoc:)") BridgedTupleExpr BridgedTupleExpr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLParen, BridgedArrayRef subs, - BridgedArrayRef names, BridgedArrayRef cNameLocs, BridgedSourceLoc cRParen); + BridgedASTContext cContext, swift::SourceLoc lParen, BridgedArrayRef subs, + BridgedArrayRef names, BridgedArrayRef cNameLocs, swift::SourceLoc rParen); SWIFT_NAME("BridgedTupleExpr.createParsedDictionaryElement(_:key:value:)") BridgedTupleExpr BridgedTupleExpr_createParsedDictionaryElement( @@ -2302,12 +2184,12 @@ BridgedUnresolvedDeclRefExpr BridgedUnresolvedDeclRefExpr_createParsed( SWIFT_NAME("BridgedUnresolvedDotExpr.createParsed(_:base:dotLoc:name:nameLoc:)") BridgedUnresolvedDotExpr BridgedUnresolvedDotExpr_createParsed( - BridgedASTContext cContext, BridgedExpr base, BridgedSourceLoc cDotLoc, + BridgedASTContext cContext, BridgedExpr base, swift::SourceLoc dotLoc, BridgedDeclNameRef cName, BridgedDeclNameLoc cNameLoc); SWIFT_NAME("BridgedUnresolvedMemberExpr.createParsed(_:dotLoc:name:nameLoc:)") BridgedUnresolvedMemberExpr BridgedUnresolvedMemberExpr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cDotLoc, + BridgedASTContext cContext, swift::SourceLoc dotLoc, BridgedDeclNameRef cName, BridgedDeclNameLoc cNameLoc); SWIFT_NAME("BridgedUnresolvedPatternExpr.createParsed(_:pattern:)") @@ -2327,9 +2209,9 @@ void BridgedExpr_dump(BridgedExpr expr); struct BridgedLabeledStmtInfo { SWIFT_NAME("name") - BridgedIdentifier Name; + swift::Identifier Name; SWIFT_NAME("loc") - BridgedSourceLoc Loc; + swift::SourceLoc Loc; BRIDGED_INLINE swift::LabeledStmtInfo unbridged() const; }; @@ -2350,7 +2232,7 @@ BridgedStmtConditionElement_createBoolean(BridgedExpr expr); SWIFT_NAME("BridgedStmtConditionElement.createPatternBinding(_:introducerLoc:" "pattern:initializer:)") BridgedStmtConditionElement BridgedStmtConditionElement_createPatternBinding( - BridgedASTContext cContext, BridgedSourceLoc cIntroducerLoc, + BridgedASTContext cContext, swift::SourceLoc introducerLoc, BridgedPattern cPattern, BridgedExpr cInitializer); SWIFT_NAME("BridgedStmtConditionElement.createPoundAvailable(info:)") @@ -2360,16 +2242,16 @@ BridgedStmtConditionElement BridgedStmtConditionElement_createPoundAvailable( SWIFT_NAME("BridgedPoundAvailableInfo.createParsed(_:poundLoc:lParenLoc:specs:" "rParenLoc:isUnavailable:)") BridgedPoundAvailableInfo BridgedPoundAvailableInfo_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cPoundLoc, - BridgedSourceLoc cLParenLoc, BridgedArrayRef cSpecs, - BridgedSourceLoc cRParenLoc, bool isUnavailability); + BridgedASTContext cContext, swift::SourceLoc poundLoc, + swift::SourceLoc lParenLoc, BridgedArrayRef cSpecs, + swift::SourceLoc rParenLoc, bool isUnavailability); SWIFT_NAME("BridgedStmtConditionElement.createHasSymbol(_:poundLoc:lParenLoc:" "symbol:rParenLoc:)") BridgedStmtConditionElement BridgedStmtConditionElement_createHasSymbol( - BridgedASTContext cContext, BridgedSourceLoc cPoundLoc, - BridgedSourceLoc cLParenLoc, BridgedNullableExpr cSymbolExpr, - BridgedSourceLoc cRParenLoc); + BridgedASTContext cContext, swift::SourceLoc poundLoc, + swift::SourceLoc lParenLoc, BridgedNullableExpr cSymbolExpr, + swift::SourceLoc rParenLoc); struct BridgedCaseLabelItemInfo { SWIFT_NAME("isDefault") @@ -2377,63 +2259,63 @@ struct BridgedCaseLabelItemInfo { SWIFT_NAME("pattern") BridgedPattern ThePattern; SWIFT_NAME("whereLoc") - BridgedSourceLoc WhereLoc; + swift::SourceLoc WhereLoc; SWIFT_NAME("guardExpr") BridgedNullableExpr GuardExpr; }; SWIFT_NAME("BridgedBraceStmt.createParsed(_:lBraceLoc:elements:rBraceLoc:)") BridgedBraceStmt BridgedBraceStmt_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLBLoc, + swift::SourceLoc lBLoc, BridgedArrayRef elements, - BridgedSourceLoc cRBLoc); + swift::SourceLoc rBLoc); SWIFT_NAME("BridgedBraceStmt.createImplicit(_:lBraceLoc:element:rBraceLoc:)") BridgedBraceStmt BridgedBraceStmt_createImplicit(BridgedASTContext cContext, - BridgedSourceLoc cLBLoc, + swift::SourceLoc lBLoc, BridgedASTNode element, - BridgedSourceLoc cRBLoc); + swift::SourceLoc rBLoc); SWIFT_NAME("BridgedBreakStmt.createParsed(_:loc:targetName:targetLoc:)") BridgedBreakStmt BridgedBreakStmt_createParsed(BridgedDeclContext cDeclContext, - BridgedSourceLoc cLoc, - BridgedIdentifier cTargetName, - BridgedSourceLoc cTargetLoc); + swift::SourceLoc loc, + swift::Identifier targetName, + swift::SourceLoc targetLoc); SWIFT_NAME("BridgedCaseStmt.createParsedSwitchCase(_:introducerLoc:" "caseLabelItems:unknownAttrLoc:terminatorLoc:body:)") BridgedCaseStmt BridgedCaseStmt_createParsedSwitchCase( - BridgedASTContext cContext, BridgedSourceLoc cIntroducerLoc, - BridgedArrayRef cCaseLabelItems, BridgedSourceLoc cUnknownAttrLoc, - BridgedSourceLoc cTerminatorLoc, BridgedBraceStmt cBody); + BridgedASTContext cContext, swift::SourceLoc introducerLoc, + BridgedArrayRef cCaseLabelItems, swift::SourceLoc unknownAttrLoc, + swift::SourceLoc terminatorLoc, BridgedBraceStmt cBody); SWIFT_NAME( "BridgedCaseStmt.createParsedDoCatch(_:catchLoc:caseLabelItems:body:)") BridgedCaseStmt BridgedCaseStmt_createParsedDoCatch( - BridgedASTContext cContext, BridgedSourceLoc cCatchLoc, + BridgedASTContext cContext, swift::SourceLoc catchLoc, BridgedArrayRef cCaseLabelItems, BridgedBraceStmt cBody); SWIFT_NAME("BridgedContinueStmt.createParsed(_:loc:targetName:targetLoc:)") BridgedContinueStmt BridgedContinueStmt_createParsed( - BridgedDeclContext cDeclContext, BridgedSourceLoc cLoc, - BridgedIdentifier cTargetName, BridgedSourceLoc cTargetLoc); + BridgedDeclContext cDeclContext, swift::SourceLoc loc, + swift::Identifier targetName, swift::SourceLoc targetLoc); SWIFT_NAME("BridgedDeferStmt.createParsed(_:deferLoc:)") BridgedDeferStmt BridgedDeferStmt_createParsed(BridgedDeclContext cDeclContext, - BridgedSourceLoc cDeferLoc); + swift::SourceLoc deferLoc); SWIFT_NAME("getter:BridgedDeferStmt.tempDecl(self:)") BridgedFuncDecl BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged); SWIFT_NAME("BridgedDiscardStmt.createParsed(_:discardLoc:subExpr:)") BridgedDiscardStmt BridgedDiscardStmt_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cDiscardLoc, + swift::SourceLoc discardLoc, BridgedExpr cSubExpr); SWIFT_NAME("BridgedDoStmt.createParsed(_:labelInfo:doLoc:body:)") BridgedDoStmt BridgedDoStmt_createParsed(BridgedASTContext cContext, BridgedLabeledStmtInfo cLabelInfo, - BridgedSourceLoc cDoLoc, + swift::SourceLoc doLoc, BridgedBraceStmt cBody); SWIFT_NAME( @@ -2441,28 +2323,27 @@ SWIFT_NAME( "body:catches:)") BridgedDoCatchStmt BridgedDoCatchStmt_createParsed( BridgedDeclContext cDeclContext, BridgedLabeledStmtInfo cLabelInfo, - BridgedSourceLoc cDoLoc, BridgedSourceLoc cThrowsLoc, + swift::SourceLoc doLoc, swift::SourceLoc throwsLoc, BridgedNullableTypeRepr cThrownType, BridgedStmt cBody, BridgedArrayRef cCatches); SWIFT_NAME("BridgedFallthroughStmt.createParsed(loc:declContext:)") BridgedFallthroughStmt -BridgedFallthroughStmt_createParsed(BridgedSourceLoc cLoc, +BridgedFallthroughStmt_createParsed(swift::SourceLoc loc, BridgedDeclContext cDC); SWIFT_NAME("BridgedForEachStmt.createParsed(_:labelInfo:forLoc:tryLoc:awaitLoc:" "unsafeLoc:pattern:inLoc:sequence:whereLoc:whereExpr:body:)") BridgedForEachStmt BridgedForEachStmt_createParsed( BridgedASTContext cContext, BridgedLabeledStmtInfo cLabelInfo, - BridgedSourceLoc cForLoc, BridgedSourceLoc cTryLoc, - BridgedSourceLoc cAwaitLoc, BridgedSourceLoc cUnsafeLoc, - BridgedPattern cPat, BridgedSourceLoc cInLoc, - BridgedExpr cSequence, BridgedSourceLoc cWhereLoc, + swift::SourceLoc forLoc, swift::SourceLoc tryLoc, swift::SourceLoc awaitLoc, + swift::SourceLoc unsafeLoc, BridgedPattern cPat, swift::SourceLoc inLoc, + BridgedExpr cSequence, swift::SourceLoc whereLoc, BridgedNullableExpr cWhereExpr, BridgedBraceStmt cBody); SWIFT_NAME("BridgedGuardStmt.createParsed(_:guardLoc:conds:body:)") BridgedGuardStmt BridgedGuardStmt_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cGuardLoc, + swift::SourceLoc guardLoc, BridgedArrayRef cConds, BridgedBraceStmt cBody); @@ -2470,56 +2351,56 @@ SWIFT_NAME("BridgedIfStmt.createParsed(_:labelInfo:ifLoc:conditions:then:" "elseLoc:else:)") BridgedIfStmt BridgedIfStmt_createParsed( BridgedASTContext cContext, BridgedLabeledStmtInfo cLabelInfo, - BridgedSourceLoc cIfLoc, BridgedArrayRef cConds, BridgedBraceStmt cThen, - BridgedSourceLoc cElseLoc, BridgedNullableStmt cElse); + swift::SourceLoc ifLoc, BridgedArrayRef cConds, BridgedBraceStmt cThen, + swift::SourceLoc elseLoc, BridgedNullableStmt cElse); SWIFT_NAME("BridgedPoundAssertStmt.createParsed(_:range:condition:message:)") BridgedPoundAssertStmt BridgedPoundAssertStmt_createParsed( - BridgedASTContext cContext, BridgedSourceRange cRange, + BridgedASTContext cContext, swift::SourceRange range, BridgedExpr cConditionExpr, BridgedStringRef cMessage); SWIFT_NAME("BridgedRepeatWhileStmt.createParsed(_:labelInfo:repeatLoc:cond:" "whileLoc:body:)") BridgedRepeatWhileStmt BridgedRepeatWhileStmt_createParsed( BridgedASTContext cContext, BridgedLabeledStmtInfo cLabelInfo, - BridgedSourceLoc cRepeatLoc, BridgedExpr cCond, BridgedSourceLoc cWhileLoc, + swift::SourceLoc repeatLoc, BridgedExpr cCond, swift::SourceLoc whileLoc, BridgedStmt cBody); SWIFT_NAME("BridgedReturnStmt.createParsed(_:loc:expr:)") BridgedReturnStmt BridgedReturnStmt_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLoc, + swift::SourceLoc loc, BridgedNullableExpr expr); SWIFT_NAME("BridgedSwitchStmt.createParsed(_:labelInfo:switchLoc:subjectExpr:" "lBraceLoc:cases:rBraceLoc:)") BridgedSwitchStmt BridgedSwitchStmt_createParsed( BridgedASTContext cContext, BridgedLabeledStmtInfo cLabelInfo, - BridgedSourceLoc cSwitchLoc, BridgedExpr cSubjectExpr, - BridgedSourceLoc cLBraceLoc, BridgedArrayRef cCases, - BridgedSourceLoc cRBraceLoc); + swift::SourceLoc switchLoc, BridgedExpr cSubjectExpr, + swift::SourceLoc lBraceLoc, BridgedArrayRef cCases, + swift::SourceLoc rBraceLoc); SWIFT_NAME("BridgedThenStmt.createParsed(_:thenLoc:result:)") BridgedThenStmt BridgedThenStmt_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cThenLoc, + swift::SourceLoc thenLoc, BridgedExpr cResult); SWIFT_NAME("BridgedThrowStmt.createParsed(_:throwLoc:subExpr:)") BridgedThrowStmt BridgedThrowStmt_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cThrowLoc, + swift::SourceLoc throwLoc, BridgedExpr cSubExpr); SWIFT_NAME("BridgedWhileStmt.createParsed(_:labelInfo:whileLoc:cond:body:)") BridgedWhileStmt BridgedWhileStmt_createParsed( BridgedASTContext cContext, BridgedLabeledStmtInfo cLabelInfo, - BridgedSourceLoc cWhileLoc, BridgedArrayRef cCond, BridgedStmt cBody); + swift::SourceLoc whileLoc, BridgedArrayRef cCond, BridgedStmt cBody); SWIFT_NAME( "BridgedYieldStmt.createParsed(_:yieldLoc:lParenLoc:yields:rParenLoc:)") BridgedYieldStmt BridgedYieldStmt_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cYieldLoc, - BridgedSourceLoc cLParenLoc, + swift::SourceLoc yieldLoc, + swift::SourceLoc lParenLoc, BridgedArrayRef cYields, - BridgedSourceLoc cRParenLoc); + swift::SourceLoc rParenLoc); SWIFT_NAME("BridgedStmt.dump(self:)") void BridgedStmt_dump(BridgedStmt statement); @@ -2562,20 +2443,16 @@ class BridgedTypeOrCustomAttr { SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCustomAttr castToCustomAttr() const; }; -// Bridged type attribute kinds, which mirror TypeAttrKind exactly. -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedTypeAttrKind { -#define TYPE_ATTR(_, CLASS) BridgedTypeAttrKind##CLASS, -#include "swift/AST/TypeAttr.def" - BridgedTypeAttrKindNone, -}; +BRIDGED_OPTIONAL(swift::TypeAttrKind, TypeAttrKind) -SWIFT_NAME("BridgedTypeAttrKind.init(from:)") -BridgedTypeAttrKind BridgedTypeAttrKind_fromString(BridgedStringRef cStr); +SWIFT_NAME("BridgedOptionalTypeAttrKind.init(from:)") +BridgedOptionalTypeAttrKind +BridgedOptionalTypeAttrKind_fromString(BridgedStringRef cStr); SWIFT_NAME("BridgedTypeAttribute.createSimple(_:kind:atLoc:nameLoc:)") BridgedTypeAttribute BridgedTypeAttribute_createSimple( - BridgedASTContext cContext, BridgedTypeAttrKind cKind, - BridgedSourceLoc cAtLoc, BridgedSourceLoc cNameLoc); + BridgedASTContext cContext, swift::TypeAttrKind kind, + swift::SourceLoc atLoc, swift::SourceLoc nameLoc); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedIsolatedTypeAttrIsolationKind { BridgedIsolatedTypeAttrIsolationKind_DynamicIsolation, @@ -2584,49 +2461,35 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedIsolatedTypeAttrIsolationKind { SWIFT_NAME("BridgedConventionTypeAttr.createParsed(_:atLoc:nameLoc:parensRange:" "name:nameLoc:witnessMethodProtocol:clangType:clangTypeLoc:)") BridgedConventionTypeAttr BridgedConventionTypeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cKwLoc, BridgedSourceRange cParens, BridgedStringRef cName, - BridgedSourceLoc cNameLoc, BridgedDeclNameRef cWitnessMethodProtocol, - BridgedStringRef cClangType, BridgedSourceLoc cClangTypeLoc); - -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedExecutionTypeAttrExecutionKind { - BridgedExecutionTypeAttrExecutionKind_Concurrent, - BridgedExecutionTypeAttrExecutionKind_Caller -}; + BridgedASTContext cContext, swift::SourceLoc atLoc, swift::SourceLoc kwLoc, + swift::SourceRange parens, BridgedStringRef cName, swift::SourceLoc nameLoc, + BridgedDeclNameRef cWitnessMethodProtocol, BridgedStringRef cClangType, + swift::SourceLoc clangTypeLoc); SWIFT_NAME("BridgedDifferentiableTypeAttr.createParsed(_:atLoc:nameLoc:" "parensRange:kind:kindLoc:)") BridgedDifferentiableTypeAttr BridgedDifferentiableTypeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cNameLoc, BridgedSourceRange cParensRange, - BridgedDifferentiabilityKind cKind, BridgedSourceLoc cKindLoc); - -SWIFT_NAME("BridgedExecutionTypeAttr.createParsed(_:atLoc:nameLoc:parensRange:" - "behavior:behaviorLoc:)") -BridgedExecutionTypeAttr BridgedExecutionTypeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cNameLoc, BridgedSourceRange cParensRange, - BridgedExecutionTypeAttrExecutionKind behavior, - BridgedSourceLoc cBehaviorLoc); + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceLoc nameLoc, swift::SourceRange parensRange, + BridgedDifferentiabilityKind cKind, swift::SourceLoc kindLoc); SWIFT_NAME("BridgedIsolatedTypeAttr.createParsed(_:atLoc:nameLoc:parensRange:" "isolationKind:isolationKindLoc:)") BridgedIsolatedTypeAttr BridgedIsolatedTypeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cNameLoc, BridgedSourceRange cParensRange, + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceLoc nameLoc, swift::SourceRange parensRange, BridgedIsolatedTypeAttrIsolationKind cIsolation, - BridgedSourceLoc cIsolationLoc); + swift::SourceLoc isolationLoc); SWIFT_NAME("BridgedOpaqueReturnTypeOfTypeAttr.createParsed(_:atLoc:nameLoc:" "parensRange:" "mangled:mangledLoc:index:indexLoc:)") BridgedOpaqueReturnTypeOfTypeAttr BridgedOpaqueReturnTypeOfTypeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cKwLoc, BridgedSourceRange cParens, - BridgedStringRef cMangled, BridgedSourceLoc cMangledDoc, size_t index, - BridgedSourceLoc cIndexLoc); + BridgedASTContext cContext, swift::SourceLoc atLoc, swift::SourceLoc kwLoc, + swift::SourceRange parens, BridgedStringRef cMangled, + swift::SourceLoc mangledDoc, size_t index, swift::SourceLoc indexLoc); //===----------------------------------------------------------------------===// // MARK: TypeReprs @@ -2634,13 +2497,13 @@ BridgedOpaqueReturnTypeOfTypeAttr_createParsed( SWIFT_NAME("BridgedUnqualifiedIdentTypeRepr.createParsed(_:loc:name:)") BridgedUnqualifiedIdentTypeRepr BridgedUnqualifiedIdentTypeRepr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLoc, BridgedIdentifier id); + BridgedASTContext cContext, swift::SourceLoc loc, swift::Identifier id); SWIFT_NAME( "BridgedArrayTypeRepr.createParsed(_:base:leftSquareLoc:rightSquareLoc:)") BridgedArrayTypeRepr BridgedArrayTypeRepr_createParsed( BridgedASTContext cContext, BridgedTypeRepr base, - BridgedSourceLoc cLSquareLoc, BridgedSourceLoc cRSquareLoc); + swift::SourceLoc lSquareLoc, swift::SourceLoc rSquareLoc); SWIFT_NAME("BridgedAttributedTypeRepr.createParsed(_:base:attributes:)") BridgedAttributedTypeRepr @@ -2651,82 +2514,82 @@ BridgedAttributedTypeRepr_createParsed(BridgedASTContext cContext, SWIFT_NAME("BridgedCompositionTypeRepr.createEmpty(_:anyKeywordLoc:)") BridgedCompositionTypeRepr BridgedCompositionTypeRepr_createEmpty(BridgedASTContext cContext, - BridgedSourceLoc cAnyLoc); + swift::SourceLoc anyLoc); SWIFT_NAME("BridgedCompositionTypeRepr.createParsed(_:types:ampersandLoc:)") BridgedCompositionTypeRepr BridgedCompositionTypeRepr_createParsed(BridgedASTContext cContext, BridgedArrayRef types, - BridgedSourceLoc cFirstAmpLoc); + swift::SourceLoc firstAmpLoc); SWIFT_NAME("BridgedCompileTimeLiteralTypeRepr.createParsed(_:base:specifierLoc:)") BridgedCompileTimeLiteralTypeRepr BridgedCompileTimeLiteralTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr base, - BridgedSourceLoc cSpecifierLoc); + swift::SourceLoc specifierLoc); SWIFT_NAME("BridgedDeclRefTypeRepr.createParsed(_:base:name:nameLoc:" "genericArguments:angleRange:)") BridgedDeclRefTypeRepr BridgedDeclRefTypeRepr_createParsed( - BridgedASTContext cContext, BridgedTypeRepr cBase, BridgedIdentifier cName, - BridgedSourceLoc cLoc, BridgedArrayRef cGenericArguments, - BridgedSourceRange cAngleRange); + BridgedASTContext cContext, BridgedTypeRepr cBase, swift::Identifier name, + swift::SourceLoc loc, BridgedArrayRef cGenericArguments, + swift::SourceRange angleRange); SWIFT_NAME("BridgedDictionaryTypeRepr.createParsed(_:leftSquareLoc:keyType:" "colonLoc:valueType:rightSquareLoc:)") BridgedDictionaryTypeRepr BridgedDictionaryTypeRepr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLSquareLoc, - BridgedTypeRepr keyType, BridgedSourceLoc cColonloc, - BridgedTypeRepr valueType, BridgedSourceLoc cRSquareLoc); + BridgedASTContext cContext, swift::SourceLoc lSquareLoc, + BridgedTypeRepr keyType, swift::SourceLoc colonloc, + BridgedTypeRepr valueType, swift::SourceLoc rSquareLoc); SWIFT_NAME("BridgedErrorTypeRepr.create(_:range:)") BridgedErrorTypeRepr BridgedErrorTypeRepr_create(BridgedASTContext cContext, - BridgedSourceRange cRange); + swift::SourceRange range); SWIFT_NAME("BridgedFunctionTypeRepr.createParsed(_:argsType:asyncLoc:throwsLoc:" "thrownType:arrowLoc:resultType:)") BridgedFunctionTypeRepr BridgedFunctionTypeRepr_createParsed( BridgedASTContext cContext, BridgedTypeRepr argsTy, - BridgedSourceLoc cAsyncLoc, BridgedSourceLoc cThrowsLoc, - BridgedNullableTypeRepr thrownType, BridgedSourceLoc cArrowLoc, + swift::SourceLoc asyncLoc, swift::SourceLoc throwsLoc, + BridgedNullableTypeRepr thrownType, swift::SourceLoc arrowLoc, BridgedTypeRepr resultType); SWIFT_NAME("BridgedUnqualifiedIdentTypeRepr.createParsed(_:name:nameLoc:" "genericArgs:leftAngleLoc:rightAngleLoc:)") BridgedUnqualifiedIdentTypeRepr BridgedUnqualifiedIdentTypeRepr_createParsed( - BridgedASTContext cContext, BridgedIdentifier name, - BridgedSourceLoc cNameLoc, BridgedArrayRef genericArgs, - BridgedSourceLoc cLAngleLoc, BridgedSourceLoc cRAngleLoc); + BridgedASTContext cContext, swift::Identifier name, + swift::SourceLoc nameLoc, BridgedArrayRef genericArgs, + swift::SourceLoc lAngleLoc, swift::SourceLoc rAngleLoc); SWIFT_NAME("BridgedOptionalTypeRepr.createParsed(_:base:questionLoc:)") BridgedOptionalTypeRepr BridgedOptionalTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr base, - BridgedSourceLoc cQuestionLoc); + swift::SourceLoc questionLoc); SWIFT_NAME("BridgedImplicitlyUnwrappedOptionalTypeRepr.createParsed(_:base:" "exclaimLoc:)") BridgedImplicitlyUnwrappedOptionalTypeRepr BridgedImplicitlyUnwrappedOptionalTypeRepr_createParsed( BridgedASTContext cContext, BridgedTypeRepr base, - BridgedSourceLoc cExclamationLoc); + swift::SourceLoc exclamationLoc); SWIFT_NAME("BridgedInlineArrayTypeRepr.createParsed(_:count:element:brackets:)") BridgedInlineArrayTypeRepr BridgedInlineArrayTypeRepr_createParsed( BridgedASTContext cContext, BridgedTypeRepr cCountType, - BridgedTypeRepr cElementType, BridgedSourceRange cBracketsRange); + BridgedTypeRepr cElementType, swift::SourceRange bracketsRange); SWIFT_NAME("BridgedInverseTypeRepr.createParsed(_:tildeLoc:constraint:)") BridgedInverseTypeRepr BridgedInverseTypeRepr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cTildeLoc, + swift::SourceLoc tildeLoc, BridgedTypeRepr cConstraint); SWIFT_NAME("BridgedIsolatedTypeRepr.createParsed(_:base:specifierLoc:)") BridgedIsolatedTypeRepr BridgedIsolatedTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr base, - BridgedSourceLoc cSpecifierLoc); + swift::SourceLoc specifierLoc); SWIFT_NAME("BridgedLifetimeDependentTypeRepr.createParsed(_:base:entry:)") BridgedLifetimeDependentTypeRepr @@ -2738,49 +2601,53 @@ SWIFT_NAME("BridgedMetatypeTypeRepr.createParsed(_:base:typeKeywordLoc:)") BridgedMetatypeTypeRepr BridgedMetatypeTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr baseType, - BridgedSourceLoc cTypeLoc); + swift::SourceLoc typeLoc); SWIFT_NAME( "BridgedOwnershipTypeRepr.createParsed(_:base:specifier:specifierLoc:)") BridgedOwnershipTypeRepr BridgedOwnershipTypeRepr_createParsed( BridgedASTContext cContext, BridgedTypeRepr base, - BridgedParamSpecifier cSpecifier, BridgedSourceLoc cSpecifierLoc); + BridgedParamSpecifier cSpecifier, swift::SourceLoc specifierLoc); SWIFT_NAME("BridgedPlaceholderTypeRepr.createParsed(_:loc:)") BridgedPlaceholderTypeRepr BridgedPlaceholderTypeRepr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("BridgedProtocolTypeRepr.createParsed(_:base:protocolKeywordLoc:)") BridgedProtocolTypeRepr BridgedProtocolTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr baseType, - BridgedSourceLoc cProtoLoc); + swift::SourceLoc protoLoc); SWIFT_NAME("BridgedPackElementTypeRepr.createParsed(_:base:eachKeywordLoc:)") -BridgedPackElementTypeRepr -BridgedPackElementTypeRepr_createParsed(BridgedASTContext cContext, - BridgedTypeRepr base, - BridgedSourceLoc cEachLoc); +BridgedPackElementTypeRepr BridgedPackElementTypeRepr_createParsed( + BridgedASTContext cContext, BridgedTypeRepr base, swift::SourceLoc eachLoc); SWIFT_NAME( "BridgedPackExpansionTypeRepr.createParsed(_:base:repeatKeywordLoc:)") BridgedPackExpansionTypeRepr BridgedPackExpansionTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr base, - BridgedSourceLoc cRepeatLoc); + swift::SourceLoc repeatLoc); SWIFT_NAME("BridgedSendingTypeRepr.createParsed(_:base:specifierLoc:)") BridgedSendingTypeRepr BridgedSendingTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr base, - BridgedSourceLoc cSpecifierLoc); + swift::SourceLoc specifierLoc); + +SWIFT_NAME("BridgedCallerIsolatedTypeRepr.createParsed(_:base:specifierLoc:)") +BridgedCallerIsolatedTypeRepr +BridgedCallerIsolatedTypeRepr_createParsed(BridgedASTContext cContext, + BridgedTypeRepr base, + swift::SourceLoc specifierLoc); SWIFT_NAME( "BridgedTupleTypeRepr.createParsed(_:elements:leftParenLoc:rightParenLoc:)") BridgedTupleTypeRepr BridgedTupleTypeRepr_createParsed( BridgedASTContext cContext, BridgedArrayRef elements, - BridgedSourceLoc cLParenLoc, BridgedSourceLoc cRParenLoc); + swift::SourceLoc lParenLoc, swift::SourceLoc rParenLoc); SWIFT_NAME( "BridgedNamedOpaqueReturnTypeRepr.createParsed(_:base:genericParamList:)") @@ -2791,26 +2658,26 @@ BridgedNamedOpaqueReturnTypeRepr BridgedNamedOpaqueReturnTypeRepr_createParsed( SWIFT_NAME("BridgedOpaqueReturnTypeRepr.createParsed(_:someKeywordLoc:base:)") BridgedOpaqueReturnTypeRepr BridgedOpaqueReturnTypeRepr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cOpaqueLoc, + swift::SourceLoc opaqueLoc, BridgedTypeRepr baseTy); SWIFT_NAME("BridgedExistentialTypeRepr.createParsed(_:anyKeywordLoc:base:)") BridgedExistentialTypeRepr BridgedExistentialTypeRepr_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cAnyLoc, + swift::SourceLoc anyLoc, BridgedTypeRepr baseTy); SWIFT_NAME("BridgedVarargTypeRepr.createParsed(_:base:ellipsisLoc:)") BridgedVarargTypeRepr BridgedVarargTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr base, - BridgedSourceLoc cEllipsisLoc); + swift::SourceLoc ellipsisLoc); SWIFT_NAME( "BridgedIntegerTypeRepr.createParsed(_:string:loc:minusLoc:)") BridgedIntegerTypeRepr BridgedIntegerTypeRepr_createParsed( - BridgedASTContext cContext, BridgedStringRef cString, BridgedSourceLoc cLoc, - BridgedSourceLoc cMinusLoc); + BridgedASTContext cContext, BridgedStringRef cString, swift::SourceLoc loc, + swift::SourceLoc minusLoc); SWIFT_NAME("BridgedTypeRepr.dump(self:)") void BridgedTypeRepr_dump(BridgedTypeRepr type); @@ -2824,7 +2691,7 @@ BridgedNullableVarDecl BridgedPattern_getSingleVar(BridgedPattern cPattern); SWIFT_NAME("BridgedAnyPattern.createParsed(_:loc:)") BridgedAnyPattern BridgedAnyPattern_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("BridgedAnyPattern.createImplicit(_:)") BridgedAnyPattern BridgedAnyPattern_createImplicit(BridgedASTContext cContext); @@ -2832,13 +2699,13 @@ BridgedAnyPattern BridgedAnyPattern_createImplicit(BridgedASTContext cContext); SWIFT_NAME("BridgedBindingPattern.createParsed(_:keywordLoc:isLet:subPattern:)") BridgedBindingPattern BridgedBindingPattern_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cKeywordLoc, bool isLet, + swift::SourceLoc keywordLoc, bool isLet, BridgedPattern cSubPattern); SWIFT_NAME("BridgedBindingPattern.createImplicitCatch(_:loc:)") BridgedBindingPattern BridgedBindingPattern_createImplicitCatch(BridgedDeclContext cDeclContext, - BridgedSourceLoc cLoc); + swift::SourceLoc loc); SWIFT_NAME("BridgedExprPattern.createParsed(_:expr:)") BridgedExprPattern @@ -2847,31 +2714,31 @@ BridgedExprPattern_createParsed(BridgedDeclContext cDeclContext, SWIFT_NAME("BridgedIsPattern.createParsed(_:isLoc:typeExpr:)") BridgedIsPattern BridgedIsPattern_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cIsLoc, + swift::SourceLoc isLoc, BridgedTypeExpr cTypeExpr); SWIFT_NAME("BridgedNamedPattern.createParsed(_:declContext:name:loc:)") BridgedNamedPattern BridgedNamedPattern_createParsed(BridgedASTContext astContext, BridgedDeclContext declContext, - BridgedIdentifier name, BridgedSourceLoc cLoc); + swift::Identifier name, swift::SourceLoc loc); SWIFT_NAME( "BridgedParenPattern.createParsed(_:lParenLoc:subPattern:rParenLoc:)") BridgedParenPattern BridgedParenPattern_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLParenLoc, - BridgedPattern cSubPattern, BridgedSourceLoc cRParenLoc); + BridgedASTContext cContext, swift::SourceLoc lParenLoc, + BridgedPattern cSubPattern, swift::SourceLoc rParenLoc); struct BridgedTuplePatternElt { - BridgedIdentifier Label; - BridgedSourceLoc LabelLoc; + swift::Identifier Label; + swift::SourceLoc LabelLoc; BridgedPattern ThePattern; }; SWIFT_NAME("BridgedTuplePattern.createParsed(_:lParenLoc:elements:rParenLoc:)") BridgedTuplePattern BridgedTuplePattern_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLParenLoc, - BridgedArrayRef cElements, BridgedSourceLoc cRParenLoc); + BridgedASTContext cContext, swift::SourceLoc lParenLoc, + BridgedArrayRef cElements, swift::SourceLoc rParenLoc); SWIFT_NAME("BridgedTypedPattern.createParsed(_:pattern:type:)") BridgedTypedPattern BridgedTypedPattern_createParsed(BridgedASTContext cContext, @@ -2886,25 +2753,12 @@ SWIFT_NAME("BridgedPattern.setImplicit(self:)") void BridgedPattern_setImplicit(BridgedPattern cPattern); SWIFT_NAME("getter:BridgedPattern.boundName(self:)") -BridgedIdentifier BridgedPattern_getBoundName(BridgedPattern cPattern); +swift::Identifier BridgedPattern_getBoundName(BridgedPattern cPattern); //===----------------------------------------------------------------------===// // MARK: Generics //===----------------------------------------------------------------------===// -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedLayoutConstraintKind { - BridgedLayoutConstraintKindUnknownLayout, - BridgedLayoutConstraintKindTrivialOfExactSize, - BridgedLayoutConstraintKindTrivialOfAtMostSize, - BridgedLayoutConstraintKindTrivial, - BridgedLayoutConstraintKindClass, - BridgedLayoutConstraintKindNativeClass, - BridgedLayoutConstraintKindRefCountedObject, - BridgedLayoutConstraintKindNativeRefCountedObject, - BridgedLayoutConstraintKindBridgeObject, - BridgedLayoutConstraintKindTrivialStride, -}; - class BridgedLayoutConstraint { swift::LayoutConstraintInfo *_Nullable raw; @@ -2915,55 +2769,49 @@ class BridgedLayoutConstraint { SWIFT_UNAVAILABLE("Use the factory methods") BRIDGED_INLINE BridgedLayoutConstraint(swift::LayoutConstraint constraint); + BRIDGED_INLINE + SWIFT_COMPUTED_PROPERTY + bool getIsNull() const; + + SWIFT_COMPUTED_PROPERTY + swift::LayoutConstraintKind getKind() const; + + BRIDGED_INLINE + SWIFT_COMPUTED_PROPERTY + bool getIsKnownLayout() const; + + BRIDGED_INLINE + SWIFT_COMPUTED_PROPERTY + bool getIsTrivial() const; + + SWIFT_UNAVAILABLE("Unavailable in Swift") BRIDGED_INLINE swift::LayoutConstraint unbridged() const; }; SWIFT_NAME("BridgedLayoutConstraint.getLayoutConstraint(_:id:)") BridgedLayoutConstraint BridgedLayoutConstraint_getLayoutConstraint(BridgedASTContext cContext, - BridgedIdentifier cID); + swift::Identifier ID); SWIFT_NAME("BridgedLayoutConstraint.getLayoutConstraint(_:kind:)") BridgedLayoutConstraint BridgedLayoutConstraint_getLayoutConstraint(BridgedASTContext cContext, - BridgedLayoutConstraintKind cKind); + swift::LayoutConstraintKind kind); SWIFT_NAME( "BridgedLayoutConstraint.getLayoutConstraint(_:kind:size:alignment:)") BridgedLayoutConstraint BridgedLayoutConstraint_getLayoutConstraint(BridgedASTContext cContext, - BridgedLayoutConstraintKind cKind, + swift::LayoutConstraintKind kind, size_t size, size_t alignment); -SWIFT_NAME("getter:BridgedLayoutConstraint.isNull(self:)") -BRIDGED_INLINE bool -BridgedLayoutConstraint_isNull(BridgedLayoutConstraint cConstraint); - -SWIFT_NAME("getter:BridgedLayoutConstraint.kind(self:)") -BridgedLayoutConstraintKind -BridgedLayoutConstraint_getKind(BridgedLayoutConstraint cConstraint); - -SWIFT_NAME("getter:BridgedLayoutConstraint.isKnownLayout(self:)") -BRIDGED_INLINE bool -BridgedLayoutConstraint_isKnownLayout(BridgedLayoutConstraint cConstraint); - -SWIFT_NAME("getter:BridgedLayoutConstraint.isTrivial(self:)") -BRIDGED_INLINE bool -BridgedLayoutConstraint_isTrivial(BridgedLayoutConstraint cConstraint); - -enum ENUM_EXTENSIBILITY_ATTR(open) BridgedRequirementReprKind : size_t { - BridgedRequirementReprKindTypeConstraint, - BridgedRequirementReprKindSameType, - BridgedRequirementReprKindLayoutConstraint, -}; - struct BridgedRequirementRepr { - BridgedSourceLoc SeparatorLoc; - BridgedRequirementReprKind Kind; + swift::SourceLoc SeparatorLoc; + swift::RequirementReprKind Kind; BridgedTypeRepr FirstType; BridgedNullableTypeRepr SecondType; BridgedLayoutConstraint LayoutConstraint; - BridgedSourceLoc LayoutConstraintLoc; + swift::SourceLoc LayoutConstraintLoc; bool IsExpansionPattern; swift::RequirementRepr unbridged() const; @@ -2972,58 +2820,49 @@ struct BridgedRequirementRepr { SWIFT_NAME("BridgedRequirementRepr.createTypeConstraint(subject:colonLoc:" "constraint:isExpansionPattern:)") BridgedRequirementRepr BridgedRequirementRepr_createTypeConstraint( - BridgedTypeRepr cSubject, BridgedSourceLoc cColonLoc, + BridgedTypeRepr cSubject, swift::SourceLoc colonLoc, BridgedTypeRepr cConstraint, bool isExpansionPattern); SWIFT_NAME("BridgedRequirementRepr.createSameType(firstType:equalLoc:" "secondType:isExpansionPattern:)") BridgedRequirementRepr BridgedRequirementRepr_createSameType( - BridgedTypeRepr cFirstType, BridgedSourceLoc cEqualLoc, + BridgedTypeRepr cFirstType, swift::SourceLoc equalLoc, BridgedTypeRepr cSecondType, bool isExpansionPattern); SWIFT_NAME("BridgedRequirementRepr.createLayoutConstraint(subject:colonLoc:" "layout:layoutLoc:isExpansionPattern:)") BridgedRequirementRepr BridgedRequirementRepr_createLayoutConstraint( - BridgedTypeRepr cSubject, BridgedSourceLoc cColonLoc, - BridgedLayoutConstraint cLayout, BridgedSourceLoc cLayoutLoc, + BridgedTypeRepr cSubject, swift::SourceLoc colonLoc, + BridgedLayoutConstraint cLayout, swift::SourceLoc layoutLoc, bool isExpansionPattern); -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedGenericTypeParamKind : size_t { - /// A regular generic type parameter: 'T' - BridgedGenericTypeParamKindType = 0, - /// A generic parameter pack: 'each T' - BridgedGenericTypeParamKindPack, - /// A generic value parameter: 'let T' - BridgedGenericTypeParamKindValue, -}; - SWIFT_NAME("BridgedGenericParamList.createParsed(_:leftAngleLoc:parameters:" "genericWhereClause:rightAngleLoc:)") BridgedGenericParamList BridgedGenericParamList_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLeftAngleLoc, + BridgedASTContext cContext, swift::SourceLoc leftAngleLoc, BridgedArrayRef cParameters, BridgedNullableTrailingWhereClause genericWhereClause, - BridgedSourceLoc cRightAngleLoc); + swift::SourceLoc rightAngleLoc); SWIFT_NAME( "BridgedGenericTypeParamDecl.createParsed(_:declContext:specifierLoc:" "name:nameLoc:inheritedType:index:paramKind:)") BridgedGenericTypeParamDecl BridgedGenericTypeParamDecl_createParsed( BridgedASTContext cContext, BridgedDeclContext cDeclContext, - BridgedSourceLoc cSpecifierLoc, BridgedIdentifier cName, - BridgedSourceLoc cNameLoc, BridgedNullableTypeRepr opaqueInheritedType, - size_t index, BridgedGenericTypeParamKind paramKind); + swift::SourceLoc specifierLoc, swift::Identifier name, + swift::SourceLoc nameLoc, BridgedNullableTypeRepr opaqueInheritedType, + size_t index, swift::GenericTypeParamKind paramKind); SWIFT_NAME( "BridgedTrailingWhereClause.createParsed(_:whereKeywordLoc:requirements:)") BridgedTrailingWhereClause BridgedTrailingWhereClause_createParsed(BridgedASTContext cContext, - BridgedSourceLoc cWhereKeywordLoc, + swift::SourceLoc whereKeywordLoc, BridgedArrayRef cRequirements); SWIFT_NAME("BridgedParameterList.createParsed(_:leftParenLoc:parameters:" "rightParenLoc:)") BridgedParameterList BridgedParameterList_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cLeftParenLoc, - BridgedArrayRef cParameters, BridgedSourceLoc cRightParenLoc); + BridgedASTContext cContext, swift::SourceLoc leftParenLoc, + BridgedArrayRef cParameters, swift::SourceLoc rightParenLoc); SWIFT_NAME("getter:BridgedParameterList.size(self:)") size_t BridgedParameterList_size(BridgedParameterList cParameterList); @@ -3037,14 +2876,14 @@ BridgedParamDecl BridgedParameterList_get(BridgedParameterList cParameterList, //===----------------------------------------------------------------------===// struct BridgedTupleTypeElement { - BridgedIdentifier Name; - BridgedSourceLoc NameLoc; - BridgedIdentifier SecondName; - BridgedSourceLoc SecondNameLoc; - BridgedSourceLoc UnderscoreLoc; - BridgedSourceLoc ColonLoc; + swift::Identifier Name; + swift::SourceLoc NameLoc; + swift::Identifier SecondName; + swift::SourceLoc SecondNameLoc; + swift::SourceLoc UnderscoreLoc; + swift::SourceLoc ColonLoc; BridgedTypeRepr Type; - BridgedSourceLoc TrailingCommaLoc; + swift::SourceLoc TrailingCommaLoc; }; enum ENUM_EXTENSIBILITY_ATTR(open) BridgedMacroDefinitionKind : size_t { @@ -3057,8 +2896,6 @@ enum ENUM_EXTENSIBILITY_ATTR(open) BridgedMacroDefinitionKind : size_t { BridgedBuiltinExternalMacro, /// The builtin definition for the "isolation" macro. BridgedBuiltinIsolationMacro, - /// The builtin definition for the "SwiftSetting" macro. - BridgedBuiltinSwiftSettingsMacro, }; struct BridgedASTType { @@ -3079,16 +2916,21 @@ struct BridgedASTType { BRIDGED_INLINE swift::Type unbridged() const; BridgedOwnedString getDebugDescription() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType getCanonicalType() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedDiagnosticArgument asDiagnosticArgument() const; BRIDGED_INLINE bool hasArchetype() const; BRIDGED_INLINE bool isLegalFormalType() const; BRIDGED_INLINE bool isGenericAtAnyLevel() const; BRIDGED_INLINE bool hasTypeParameter() const; BRIDGED_INLINE bool hasLocalArchetype() const; + BRIDGED_INLINE bool hasDynamicSelf() const; + BRIDGED_INLINE bool isArchetype() const; + BRIDGED_INLINE bool archetypeRequiresClass() const; BRIDGED_INLINE bool isExistentialArchetype() const; BRIDGED_INLINE bool isExistentialArchetypeWithError() const; BRIDGED_INLINE bool isExistential() const; BRIDGED_INLINE bool isDynamicSelf() const; BRIDGED_INLINE bool isClassExistential() const; + BRIDGED_INLINE bool isGenericTypeParam() const; BRIDGED_INLINE bool isEscapable() const; BRIDGED_INLINE bool isNoEscape() const; BRIDGED_INLINE bool isInteger() const; @@ -3105,19 +2947,26 @@ struct BridgedASTType { BRIDGED_INLINE bool isBuiltinInteger() const; BRIDGED_INLINE bool isBuiltinFloat() const; BRIDGED_INLINE bool isBuiltinVector() const; + BRIDGED_INLINE bool isBuiltinFixedArray() const; + BRIDGED_INLINE bool isBox() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getBuiltinVectorElementType() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType getBuiltinFixedArrayElementType() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType getBuiltinFixedArraySizeType() const; BRIDGED_INLINE bool isBuiltinFixedWidthInteger(SwiftInt width) const; BRIDGED_INLINE bool isOptional() const; + BRIDGED_INLINE bool isBuiltinType() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getNominalOrBoundGenericNominal() const; BRIDGED_INLINE TraitResult canBeClass() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getAnyNominal() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getInstanceTypeOfMetatype() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getStaticTypeOfDynamicSelf() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getInterfaceTypeOfArchetype() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getSuperClassType() const; BRIDGED_INLINE MetatypeRepresentation getRepresentationOfMetatype() const; + BRIDGED_INLINE BridgedOptionalInt getValueOfIntegerType() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedSubstitutionMap getContextSubstitutionMap() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGenericSignature getInvocationGenericSignatureOfFunctionType() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType subst(BridgedSubstitutionMap substMap) const; - SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType subst(BridgedASTType fromType, BridgedASTType toType) const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformance checkConformance(BridgedDeclObj proto) const; }; @@ -3125,6 +2974,7 @@ class BridgedCanType { swift::TypeBase * _Nullable type; public: + BRIDGED_INLINE BridgedCanType(); BRIDGED_INLINE BridgedCanType(swift::CanType ty); BRIDGED_INLINE swift::CanType unbridged() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getRawType() const; @@ -3191,6 +3041,7 @@ struct BridgedGenericSignature { BRIDGED_INLINE swift::GenericSignature unbridged() const; BridgedOwnedString getDebugDescription() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTTypeArray getGenericParams() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType mapTypeIntoContext(BridgedASTType type) const; }; struct BridgedFingerprint { @@ -3222,9 +3073,9 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedIfConfigClauseKind : size_t { /// Bridged version of IfConfigClauseRangeInfo. struct BridgedIfConfigClauseRangeInfo { - BridgedSourceLoc directiveLoc; - BridgedSourceLoc bodyLoc; - BridgedSourceLoc endLoc; + swift::SourceLoc directiveLoc; + swift::SourceLoc bodyLoc; + swift::SourceLoc endLoc; BridgedIfConfigClauseKind kind; BRIDGED_INLINE swift::IfConfigClauseRangeInfo unbridged() const; diff --git a/include/swift/AST/ASTBridgingImpl.h b/include/swift/AST/ASTBridgingImpl.h index ac8d117cc9aef..b71a98e3b140d 100644 --- a/include/swift/AST/ASTBridgingImpl.h +++ b/include/swift/AST/ASTBridgingImpl.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2023 - 2025 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/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericSignature.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/IfConfigClauseRangeInfo.h" #include "swift/AST/MacroDeclaration.h" #include "swift/AST/ProtocolConformance.h" @@ -32,39 +33,12 @@ SWIFT_BEGIN_NULLABILITY_ANNOTATIONS //===----------------------------------------------------------------------===// -// MARK: BridgedIdentifier -//===----------------------------------------------------------------------===// - -BridgedIdentifier::BridgedIdentifier(swift::Identifier ident) - : Raw(ident.getAsOpaquePointer()) {} - -swift::Identifier BridgedIdentifier::unbridged() const { - return swift::Identifier::getFromOpaquePointer(Raw); -} - -bool BridgedIdentifier_isOperator(const BridgedIdentifier ident) { - return ident.unbridged().isOperator(); -} - -//===----------------------------------------------------------------------===// -// MARK: BridgedDeclBaseName -//===----------------------------------------------------------------------===// - -BridgedDeclBaseName::BridgedDeclBaseName(swift::DeclBaseName baseName) - : Ident(baseName.Ident) {} - -swift::DeclBaseName BridgedDeclBaseName::unbridged() const { - return swift::DeclBaseName(Ident.unbridged()); -} - -//===----------------------------------------------------------------------===// -// MARK: BridgedDeclBaseName +// MARK: BridgedConsumedLookupResult //===----------------------------------------------------------------------===// BridgedConsumedLookupResult::BridgedConsumedLookupResult( swift::Identifier name, swift::SourceLoc sourceLoc, SwiftInt flag) - : Name(BridgedIdentifier(name)), NameLoc(BridgedSourceLoc(sourceLoc)), - Flag(flag) {} + : Name(name), NameLoc(swift::SourceLoc(sourceLoc)), Flag(flag) {} //===----------------------------------------------------------------------===// // MARK: BridgedDeclNameRef @@ -92,6 +66,17 @@ swift::DeclNameLoc BridgedDeclNameLoc::unbridged() const { return swift::DeclNameLoc(LocationInfo, NumArgumentLabels); } +//===----------------------------------------------------------------------===// +// MARK: BridgedLangOptions +//===----------------------------------------------------------------------===// + +BridgedLangOptions::BridgedLangOptions(const swift::LangOptions &langOpts) + : LangOpts(&langOpts) { } + +const swift::LangOptions &BridgedLangOptions::unbridged() const { + return *LangOpts; +} + //===----------------------------------------------------------------------===// // MARK: BridgedASTContext //===----------------------------------------------------------------------===// @@ -100,10 +85,6 @@ BridgedASTContext::BridgedASTContext(swift::ASTContext &ctx) : Ctx(&ctx) {} swift::ASTContext &BridgedASTContext::unbridged() const { return *Ctx; } -void * _Nonnull BridgedASTContext_raw(BridgedASTContext bridged) { - return &bridged.unbridged(); -} - BridgedASTContext BridgedASTContext_fromRaw(void * _Nonnull ptr) { return *static_cast(ptr); } @@ -118,10 +99,10 @@ BridgedStringRef BridgedASTContext_allocateCopyString(BridgedASTContext bridged, return bridged.unbridged().AllocateCopy(cStr.unbridged()); } -#define IDENTIFIER_WITH_NAME(Name, _) \ -BridgedIdentifier BridgedASTContext_id_##Name(BridgedASTContext bridged) { \ -return bridged.unbridged().Id_##Name; \ -} +#define IDENTIFIER_WITH_NAME(Name, _) \ + swift::Identifier BridgedASTContext_id_##Name(BridgedASTContext bridged) { \ + return bridged.unbridged().Id_##Name; \ + } #include "swift/AST/KnownIdentifiers.def" //===----------------------------------------------------------------------===// @@ -171,15 +152,18 @@ bool BridgedSourceFile_isScriptMode(BridgedSourceFile sf) { // MARK: BridgedDeclObj //===----------------------------------------------------------------------===// -BridgedSourceLoc BridgedDeclObj::getLoc() const { - swift::SourceLoc sourceLoc = unbridged()->getLoc(); - return BridgedSourceLoc(sourceLoc.getOpaquePointerValue()); +swift::SourceLoc BridgedDeclObj::getLoc() const { + return unbridged()->getLoc(); } BridgedDeclObj BridgedDeclObj::getModuleContext() const { return {unbridged()->getModuleContext()}; } +OptionalBridgedDeclObj BridgedDeclObj::getParent() const { + return {unbridged()->getDeclContext()->getAsDecl()}; +} + BridgedStringRef BridgedDeclObj::Type_getName() const { return getAs()->getName().str(); } @@ -188,8 +172,8 @@ BridgedStringRef BridgedDeclObj::Value_getUserFacingName() const { return getAs()->getBaseName().userFacingName(); } -BridgedSourceLoc BridgedDeclObj::Value_getNameLoc() const { - return BridgedSourceLoc(getAs()->getNameLoc().getOpaquePointerValue()); +swift::SourceLoc BridgedDeclObj::Value_getNameLoc() const { + return getAs()->getNameLoc(); } bool BridgedDeclObj::hasClangNode() const { @@ -216,6 +200,10 @@ OptionalBridgedDeclObj BridgedDeclObj::NominalType_getValueTypeDestructor() cons return {getAs()->getValueTypeDestructor()}; } +bool BridgedDeclObj::Enum_hasRawType() const { + return getAs()->hasRawType(); +} + bool BridgedDeclObj::Struct_hasUnreferenceableStorage() const { return getAs()->hasUnreferenceableStorage(); } @@ -228,6 +216,10 @@ BridgedDeclObj BridgedDeclObj::Class_getDestructor() const { return {getAs()->getDestructor()}; } +bool BridgedDeclObj::ProtocolDecl_requiresClass() const { + return getAs()->requiresClass(); +} + bool BridgedDeclObj::AbstractFunction_isOverridden() const { return getAs()->isOverridden(); } @@ -238,6 +230,10 @@ bool BridgedDeclObj::Destructor_isIsolated() const { return ai.isActorIsolated(); } +bool BridgedDeclObj::EnumElementDecl_hasAssociatedValues() const { + return getAs()->hasAssociatedValues(); +} + //===----------------------------------------------------------------------===// // MARK: BridgedASTNode //===----------------------------------------------------------------------===// @@ -314,7 +310,7 @@ bool BridgedAvailabilityDomainOrIdentifier_isDomain( return cVal.unbridged().isDomain(); } -BridgedIdentifier BridgedAvailabilityDomainOrIdentifier_getAsIdentifier( +swift::Identifier BridgedAvailabilityDomainOrIdentifier_getAsIdentifier( BridgedAvailabilityDomainOrIdentifier cVal) { if (auto ident = cVal.unbridged().getAsIdentifier()) return *ident; @@ -378,8 +374,7 @@ BridgedVarDecl_asAbstractStorageDecl(BridgedVarDecl decl) { //===----------------------------------------------------------------------===// swift::Argument BridgedCallArgument::unbridged() const { - return swift::Argument(labelLoc.unbridged(), label.unbridged(), - argExpr.unbridged()); + return swift::Argument(labelLoc, label, argExpr.unbridged()); } //===----------------------------------------------------------------------===// @@ -387,7 +382,7 @@ swift::Argument BridgedCallArgument::unbridged() const { //===----------------------------------------------------------------------===// swift::LabeledStmtInfo BridgedLabeledStmtInfo::unbridged() const { - return {Name.unbridged(), Loc.unbridged()}; + return {Name, Loc}; } //===----------------------------------------------------------------------===// @@ -402,6 +397,10 @@ BridgedCanType BridgedASTType::getCanonicalType() const { return unbridged()->getCanonicalType(); } +BridgedDiagnosticArgument BridgedASTType::asDiagnosticArgument() const { + return swift::DiagnosticArgument(unbridged()); +} + bool BridgedASTType::hasArchetype() const { return unbridged()->hasArchetype(); } @@ -423,6 +422,18 @@ bool BridgedASTType::hasLocalArchetype() const { return unbridged()->hasLocalArchetype(); } +bool BridgedASTType::hasDynamicSelf() const { + return unbridged()->hasDynamicSelfType(); +} + +bool BridgedASTType::isArchetype() const { + return unbridged()->is(); +} + +bool BridgedASTType::archetypeRequiresClass() const { + return unbridged()->castTo()->requiresClass(); +} + bool BridgedASTType::isExistentialArchetype() const { return unbridged()->is(); } @@ -443,6 +454,10 @@ bool BridgedASTType::isClassExistential() const { return unbridged()->isClassExistentialType(); } +bool BridgedASTType::isGenericTypeParam() const { + return unbridged()->is(); +} + bool BridgedASTType::isEscapable() const { return unbridged()->isEscapable(); } @@ -513,10 +528,26 @@ bool BridgedASTType::isBuiltinVector() const { return unbridged()->is(); } +bool BridgedASTType::isBuiltinFixedArray() const { + return unbridged()->is(); +} + +bool BridgedASTType::isBox() const { + return unbridged()->is(); +} + BridgedASTType BridgedASTType::getBuiltinVectorElementType() const { return {unbridged()->castTo()->getElementType().getPointer()}; } +BridgedCanType BridgedASTType::getBuiltinFixedArrayElementType() const { + return unbridged()->castTo()->getElementType(); +} + +BridgedCanType BridgedASTType::getBuiltinFixedArraySizeType() const { + return unbridged()->castTo()->getSize(); +} + bool BridgedASTType::isBuiltinFixedWidthInteger(SwiftInt width) const { if (auto *intTy = unbridged()->getAs()) return intTy->isFixedWidth((unsigned)width); @@ -531,6 +562,10 @@ bool BridgedASTType::isUnownedStorageType() const { return unbridged()->is(); } +bool BridgedASTType::isBuiltinType() const { + return unbridged()->isBuiltinType(); +} + OptionalBridgedDeclObj BridgedASTType::getNominalOrBoundGenericNominal() const { return {unbridged()->getNominalOrBoundGenericNominal()}; } @@ -547,6 +582,14 @@ BridgedASTType BridgedASTType::getInstanceTypeOfMetatype() const { return {unbridged()->getAs()->getInstanceType().getPointer()}; } +BridgedASTType BridgedASTType::getStaticTypeOfDynamicSelf() const { + return {unbridged()->getAs()->getSelfType().getPointer()}; +} + +BridgedASTType BridgedASTType::getInterfaceTypeOfArchetype() const { + return {unbridged()->getAs()->getInterfaceType().getPointer()}; +} + BridgedASTType BridgedASTType::getSuperClassType() const { return {unbridged()->getSuperclass().getPointer()}; } @@ -555,6 +598,10 @@ BridgedASTType::MetatypeRepresentation BridgedASTType::getRepresentationOfMetaty return MetatypeRepresentation(unbridged()->getAs()->getRepresentation()); } +BridgedOptionalInt BridgedASTType::getValueOfIntegerType() const { + return getFromAPInt(unbridged()->getAs()->getValue()); +} + BridgedSubstitutionMap BridgedASTType::getContextSubstitutionMap() const { return unbridged()->getContextSubstitutionMap(); } @@ -567,17 +614,6 @@ BridgedASTType BridgedASTType::subst(BridgedSubstitutionMap substMap) const { return {unbridged().subst(substMap.unbridged()).getPointer()}; } - -BridgedASTType BridgedASTType::subst(BridgedASTType fromType, BridgedASTType toType) const { - auto *fromTy = fromType.unbridged()->castTo(); - swift::Type toTy = toType.unbridged(); - return {unbridged().subst([fromTy, toTy](swift::SubstitutableType *t) -> swift::Type { - if (t == fromTy) - return toTy; - return t; - }, swift::LookUpConformanceInModule(), swift::SubstFlags::SubstituteLocalArchetypes).getPointer()}; -} - BridgedConformance BridgedASTType::checkConformance(BridgedDeclObj proto) const { return swift::checkConformance(unbridged(), proto.getAs(), /*allowMissing=*/ false); } @@ -594,6 +630,9 @@ static_assert((int)BridgedASTType::MetatypeRepresentation::ObjC == (int)swift::M // MARK: BridgedCanType //===----------------------------------------------------------------------===// +BridgedCanType::BridgedCanType() : type(nullptr) { +} + BridgedCanType::BridgedCanType(swift::CanType ty) : type(ty.getPointer()) { } @@ -690,17 +729,14 @@ swift::LayoutConstraint BridgedLayoutConstraint::unbridged() const { return raw; } -bool BridgedLayoutConstraint_isNull(BridgedLayoutConstraint cConstraint) { - return cConstraint.unbridged().isNull(); -} +bool BridgedLayoutConstraint::getIsNull() const { return unbridged().isNull(); } -bool BridgedLayoutConstraint_isKnownLayout( - BridgedLayoutConstraint cConstraint) { - return cConstraint.unbridged()->isKnownLayout(); +bool BridgedLayoutConstraint::getIsKnownLayout() const { + return unbridged()->isKnownLayout(); } -bool BridgedLayoutConstraint_isTrivial(BridgedLayoutConstraint cConstraint) { - return cConstraint.unbridged()->isTrivial(); +bool BridgedLayoutConstraint::getIsTrivial() const { + return unbridged()->isTrivial(); } //===----------------------------------------------------------------------===// @@ -795,6 +831,10 @@ BridgedASTTypeArray BridgedGenericSignature::getGenericParams() const { return {unbridged().getGenericParams()}; } +BridgedASTType BridgedGenericSignature::mapTypeIntoContext(BridgedASTType type) const { + return {unbridged().getGenericEnvironment()->mapTypeIntoContext(type.unbridged()).getPointer()}; +} + //===----------------------------------------------------------------------===// // MARK: BridgedFingerprint //===----------------------------------------------------------------------===// @@ -823,9 +863,7 @@ swift::IfConfigClauseRangeInfo BridgedIfConfigClauseRangeInfo::unbridged() const break; } - return swift::IfConfigClauseRangeInfo(directiveLoc.unbridged(), - bodyLoc.unbridged(), - endLoc.unbridged(), + return swift::IfConfigClauseRangeInfo(directiveLoc, bodyLoc, endLoc, clauseKind); } @@ -855,7 +893,7 @@ BridgedRegexLiteralPatternFeature::BridgedRegexLiteralPatternFeature( BridgedRegexLiteralPatternFeature::UnbridgedTy BridgedRegexLiteralPatternFeature::unbridged() const { - return UnbridgedTy(Kind.unbridged(), Range.unbridged()); + return UnbridgedTy(Kind.unbridged(), Range); } BridgedRegexLiteralPatternFeatures::UnbridgedTy @@ -885,8 +923,8 @@ swift::CaptureListEntry BridgedCaptureListEntry::unbridged() const { return swift::CaptureListEntry(PBD); } -BridgedVarDecl BridegedCaptureListEntry_getVar(BridgedCaptureListEntry entry) { - return entry.unbridged().getVar(); +BridgedVarDecl BridgedCaptureListEntry::getVarDecl() const { + return unbridged().getVar(); } //===----------------------------------------------------------------------===// @@ -894,13 +932,13 @@ BridgedVarDecl BridegedCaptureListEntry_getVar(BridgedCaptureListEntry entry) { //===----------------------------------------------------------------------===// void BridgedFloatLiteralExpr_setNegative(BridgedFloatLiteralExpr cExpr, - BridgedSourceLoc cLoc) { - cExpr.unbridged()->setNegative(cLoc.unbridged()); + swift::SourceLoc loc) { + cExpr.unbridged()->setNegative(loc); } void BridgedIntegerLiteralExpr_setNegative(BridgedIntegerLiteralExpr cExpr, - BridgedSourceLoc cLoc) { - cExpr.unbridged()->setNegative(cLoc.unbridged()); + swift::SourceLoc loc) { + cExpr.unbridged()->setNegative(loc); } //===----------------------------------------------------------------------===// diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index e980d009d73f5..858e28d7dfc29 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -620,7 +620,7 @@ class ASTContext final { /// For example, if '-module-alias Foo=X -module-alias Bar=Y' input is passed in, the aliases Foo and Bar are /// the names of the imported or referenced modules in source files in the main module, and X and Y /// are the real (physical) module names on disk. - void setModuleAliases(const llvm::StringMap &aliasMap); + void setModuleAliases(const llvm::StringMap &aliasMap); /// Adds a given alias to the map of Identifiers between module aliases and /// their actual names. @@ -909,7 +909,7 @@ class ASTContext final { AvailabilityRange getSwiftAvailability(unsigned major, unsigned minor) const; // For each feature defined in FeatureAvailability, define two functions; - // the latter, with the suffix RuntimeAvailabilty, is for use with + // the latter, with the suffix RuntimeAvailability, is for use with // AvailabilityRange::forRuntimeTarget(), and only looks at the Swift // runtime version. #define FEATURE(N, V) \ @@ -1044,13 +1044,15 @@ class ASTContext final { // Builtin type and simple types that are used frequently. const CanType TheErrorType; /// This is the ErrorType singleton. - const CanType TheUnresolvedType; /// This is the UnresolvedType singleton. const CanType TheEmptyTupleType; /// This is '()', aka Void const CanType TheEmptyPackType; const CanType TheAnyType; /// This is 'Any', the empty protocol composition const CanType TheUnconstrainedAnyType; /// This is 'any ~Copyable & ~Escapable', /// the empty protocol composition /// without any implicit constraints. + const CanGenericTypeParamType TheSelfType; /// The protocol 'Self' type; + /// a generic parameter with + /// depth 0 index 0 #define SINGLETON_TYPE(SHORT_ID, ID) \ const CanType The##SHORT_ID##Type; #include "swift/AST/TypeNodes.def" @@ -1090,13 +1092,6 @@ class ASTContext final { /// Retrieve the module interface checker associated with this AST context. ModuleInterfaceChecker *getModuleInterfaceChecker() const; - /// Compute the extra implicit framework search paths on Apple platforms: - /// $SDKROOT/System/Library/Frameworks/ and $SDKROOT/Library/Frameworks/. - std::vector getDarwinImplicitFrameworkSearchPaths() const; - - /// Return a set of all possible filesystem locations where modules can be found. - llvm::StringSet<> getAllModuleSearchPathsSet() const; - /// Load extensions to the given nominal type from the external /// module loaders. /// @@ -1298,6 +1293,10 @@ class ASTContext final { return const_cast(this)->getStdlibModule(false); } + /// Names of underscored modules whose contents, if imported, should be + /// treated as separately-imported overlays of the standard library module. + ArrayRef StdlibOverlayNames; + /// Insert an externally-sourced module into the set of known loaded modules /// in this context. void addLoadedModule(ModuleDecl *M); @@ -1337,10 +1336,10 @@ class ASTContext final { getNormalConformance(Type conformingType, ProtocolDecl *protocol, SourceLoc loc, + TypeRepr *inheritedTypeRepr, DeclContext *dc, ProtocolConformanceState state, - ProtocolConformanceOptions options, - SourceLoc preconcurrencyLoc = SourceLoc()); + ProtocolConformanceOptions options); /// Produce a self-conformance for the given protocol. SelfProtocolConformance * @@ -1630,6 +1629,10 @@ class ASTContext final { AvailabilityDomain getTargetAvailabilityDomain() const; }; +inline SourceLoc extractNearestSourceLoc(const ASTContext *ctx) { + return SourceLoc(); +} + } // end namespace swift #endif diff --git a/include/swift/AST/ASTContextGlobalCache.h b/include/swift/AST/ASTContextGlobalCache.h index 1925fe7885f03..e4aeb0415232f 100644 --- a/include/swift/AST/ASTContextGlobalCache.h +++ b/include/swift/AST/ASTContextGlobalCache.h @@ -55,6 +55,7 @@ struct WitnessIsolationError { /// Describes an isolation error involving an associated conformance. struct AssociatedConformanceIsolationError { ProtocolConformance *isolatedConformance; + DiagnosticBehavior behavior = DiagnosticBehavior::Unspecified; /// Diagnose this associated conformance isolation error. void diagnose(const NormalProtocolConformance *conformance) const; @@ -83,6 +84,10 @@ struct ASTContext::GlobalCache { const NormalProtocolConformance *, std::vector > conformanceIsolationErrors; + + /// The static build configuration. This points to an instance of the Swift + /// StaticBuildConfiguration. + void *StaticBuildConfiguration = nullptr; }; } // end namespace diff --git a/include/swift/AST/ASTDemangler.h b/include/swift/AST/ASTDemangler.h index 702072ddb3c83..58bcf68281a23 100644 --- a/include/swift/AST/ASTDemangler.h +++ b/include/swift/AST/ASTDemangler.h @@ -32,8 +32,10 @@ #include namespace swift { - + +class Decl; class TypeDecl; +class DeclName; namespace Demangle { SWIFT_BEGIN_INLINE_NAMESPACE @@ -50,6 +52,9 @@ TypeDecl *getTypeDeclForUSR(ASTContext &ctx, llvm::StringRef usr, GenericSignature genericSig=GenericSignature()); +Decl *getDeclForUSR(ASTContext &ctx, llvm::StringRef usr, + GenericSignature genericSig = GenericSignature()); + /// An implementation of MetadataReader's BuilderType concept that /// just finds and builds things in the AST. class ASTBuilder { @@ -122,6 +127,17 @@ class ASTBuilder { Demangle::NodeFactory &getNodeFactory() { return Factory; } + /// Finds the \c Decl associated with the provided \p node. + /// Attempts to find a type declaration using \c createTypeDecl, if not found, + /// it performs a lookup for the declaration and returns the first declaration + /// for which \c isMatchingValueDecl returns true. + /// + /// \note \p isMatchingValueDecl is not evaluated for type declarations, it's + /// only used to choose among lookup results when \c createTypeDecl fails. + Decl * + findDecl(NodePointer node, + llvm::function_ref isMatchingValueDecl); + Type decodeMangledType(NodePointer node, bool forRequirement = true); Type createBuiltinType(StringRef builtinName, StringRef mangledName); @@ -142,6 +158,8 @@ class ASTBuilder { Type createBoundGenericType(GenericTypeDecl *decl, ArrayRef args); + OpaqueTypeDecl *resolveOpaqueTypeDecl(NodePointer opaqueDescriptor); + Type resolveOpaqueType(NodePointer opaqueDescriptor, ArrayRef> args, unsigned ordinal); @@ -286,7 +304,8 @@ class ASTBuilder { /// The module name encoded in the node is either the module's real or ABI /// name. Multiple modules can share the same name. This function returns /// all modules that contain that name. - llvm::ArrayRef findPotentialModules(NodePointer node); + llvm::ArrayRef findPotentialModules(NodePointer node, + ModuleDecl *&scratch); Demangle::NodePointer findModuleNode(NodePointer node); diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 037cd4e4f732c..c4933fc4009fd 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -246,6 +246,8 @@ class ASTMangler : public Mangler { std::string mangleInitializerEntity(const VarDecl *var, SymbolKind SKind); std::string mangleBackingInitializerEntity(const VarDecl *var, SymbolKind SKind = SymbolKind::Default); + std::string manglePropertyWrappedFieldInitAccessorEntity( + const VarDecl *var, SymbolKind SKind = SymbolKind::Default); std::string mangleInitFromProjectedValueEntity(const VarDecl *var, SymbolKind SKind = SymbolKind::Default); @@ -453,7 +455,8 @@ class ASTMangler : public Mangler { const ValueDecl *forDecl = nullptr); void appendDeclName( - const ValueDecl *decl, DeclBaseName name = DeclBaseName()); + const ValueDecl *decl, DeclBaseName name = DeclBaseName(), + bool skipLocalDiscriminator = false); GenericTypeParamType *appendAssocType(DependentMemberType *DepTy, GenericSignature sig, @@ -721,6 +724,7 @@ class ASTMangler : public Mangler { void appendInitializerEntity(const VarDecl *var); void appendBackingInitializerEntity(const VarDecl *var); + void appendPropertyWrappedFieldInitAccessorEntity(const VarDecl *var); void appendInitFromProjectedValueEntity(const VarDecl *var); CanType getDeclTypeForMangling(const ValueDecl *decl, diff --git a/include/swift/AST/ASTNode.h b/include/swift/AST/ASTNode.h index 5516762853b48..cf7470ddb1d02 100644 --- a/include/swift/AST/ASTNode.h +++ b/include/swift/AST/ASTNode.h @@ -21,6 +21,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/PointerUnion.h" #include "swift/Basic/Debug.h" +#include "swift/Basic/SourceManager.h" #include "swift/AST/TypeAlignments.h" namespace llvm { @@ -44,9 +45,15 @@ namespace swift { enum class PatternKind : uint8_t; enum class StmtKind; - struct ASTNode - : public llvm::PointerUnion { + namespace detail { + using ASTNodeBase = + llvm::PointerUnion; + } // end namespace detail + + struct ASTNode : public detail::ASTNodeBase { + using Base = detail::ASTNodeBase; + // Inherit the constructors from PointerUnion. using PointerUnion::PointerUnion; @@ -98,10 +105,24 @@ namespace swift { return llvm::hash_value(N.getOpaqueValue()); } }; + + /// Find the outermost range that \p range was originally generated from. + /// Returns an invalid source range if \p range wasn't generated from a macro. + SourceRange getUnexpandedMacroRange(const SourceManager &SM, + SourceRange range); + } // namespace swift namespace llvm { using swift::ASTNode; + + /// `isa`, `dyn_cast`, `cast` for `ASTNode`. + template + struct CastInfo : public CastInfo {}; + template + struct CastInfo + : public CastInfo {}; + template <> struct DenseMapInfo { static inline ASTNode getEmptyKey() { return DenseMapInfo::getEmptyKey(); diff --git a/include/swift/AST/ASTPrinter.h b/include/swift/AST/ASTPrinter.h index 9a8accb07bf13..9d6459d46d124 100644 --- a/include/swift/AST/ASTPrinter.h +++ b/include/swift/AST/ASTPrinter.h @@ -413,7 +413,8 @@ class ExtraIndentStreamPrinter : public StreamPrinter { void printContext(raw_ostream &os, DeclContext *dc); bool printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, - Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS); + Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS, + bool withExplicitObjCAttr = false); /// Print a keyword or punctuator directly by its kind. llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, tok keyword); @@ -441,7 +442,7 @@ StringRef getAccessorKindString(AccessorKind value); /// may be called multiple times if the declaration uses suppressible /// features. void printWithCompatibilityFeatureChecks(ASTPrinter &printer, - PrintOptions &options, + const PrintOptions &options, Decl *decl, llvm::function_ref printBody); diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 9881ff2407d1a..746e40ea13164 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -75,7 +75,7 @@ class GenericParamList; class TrailingWhereClause; class ParameterList; class PatternBindingEntry; -class SpecializeAttr; +class AbstractSpecializeAttr; class GenericContext; class DeclName; class StmtConditionElement; @@ -1241,10 +1241,10 @@ class TopLevelCodeScope final : public ASTScopeImpl { /// The \c _@specialize attribute. class SpecializeAttributeScope final : public ASTScopeImpl { public: - SpecializeAttr *const specializeAttr; + AbstractSpecializeAttr *const specializeAttr; AbstractFunctionDecl *const whatWasSpecialized; - SpecializeAttributeScope(SpecializeAttr *specializeAttr, + SpecializeAttributeScope(AbstractSpecializeAttr *specializeAttr, AbstractFunctionDecl *whatWasSpecialized) : ASTScopeImpl(ScopeKind::SpecializeAttribute), specializeAttr(specializeAttr), whatWasSpecialized(whatWasSpecialized) { diff --git a/include/swift/AST/ASTWalker.h b/include/swift/AST/ASTWalker.h index e8f42599f1f15..f8fed39a1c6fc 100644 --- a/include/swift/AST/ASTWalker.h +++ b/include/swift/AST/ASTWalker.h @@ -603,6 +603,13 @@ class ASTWalker { /// params in AbstractFunctionDecl and NominalTypeDecl. virtual bool shouldWalkIntoGenericParams() { return false; } + /// Whether the walker should walk into any attached CustomAttrs. + virtual bool shouldWalkIntoCustomAttrs() const { + // Default to false currently since some walkers don't handle this case + // well. + return false; + } + /// This method configures how the walker should walk the initializers of /// lazy variables. These initializers are semantically different from other /// initializers in their context and so sometimes should be visited as part diff --git a/include/swift/AST/AccessorKind.h b/include/swift/AST/AccessorKind.h new file mode 100644 index 0000000000000..930f8820a6c95 --- /dev/null +++ b/include/swift/AST/AccessorKind.h @@ -0,0 +1,37 @@ +//===-- AST/AccessorKind.h --------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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_AST_ACCESSOR_KIND_H +#define SWIFT_AST_ACCESSOR_KIND_H + +/// `AccessorKind.h` is imported into Swift. Be *very* careful with what you +/// include here and keep these includes minimal! +/// +/// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" + +namespace swift { + +// Note that the values of these enums line up with %select values in +// diagnostics. +enum class ENUM_EXTENSIBILITY_ATTR(closed) AccessorKind { +#define ACCESSOR(ID, KEYWORD) ID SWIFT_NAME(#KEYWORD), +#define INIT_ACCESSOR(ID, KEYWORD) \ + ID, // FIXME: We should be able to remove this once Linux CI host Swift is + // upgraded to 6.0 +#define LAST_ACCESSOR(ID) Last = ID +#include "swift/AST/AccessorKinds.def" +}; + +} // namespace swift + +#endif // SWIFT_AST_ACCESSOR_KIND_H diff --git a/include/swift/AST/AccessorKinds.def b/include/swift/AST/AccessorKinds.def index bdba3d38a4b3d..0257b018456fe 100644 --- a/include/swift/AST/AccessorKinds.def +++ b/include/swift/AST/AccessorKinds.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 - 2025 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,22 +20,22 @@ #error must define either ACCESSOR or ACCESSOR_KEYWORD #endif -/// ACCESSOR(ID) +/// ACCESSOR(ID, KEYWORD) /// There is an accessor with the enumerator value AccessorKind::ID. #if !defined(ACCESSOR) && defined(ACCESSOR_KEYWORD) -#define ACCESSOR(ID) +#define ACCESSOR(ID, KEYWORD) #endif /// SINGLETON_ACCESSOR(ID, KEYWORD) /// The given accessor has only one matching accessor keyword. /// -/// Defaults to ACCESSOR(ID) or ACCESSOR_KEYWORD(KEYWORD), depending on which -/// is defined. +/// Defaults to ACCESSOR(ID, KEYWORD) or ACCESSOR_KEYWORD(KEYWORD), depending +/// on which is defined. #ifndef SINGLETON_ACCESSOR #if defined(ACCESSOR_KEYWORD) #define SINGLETON_ACCESSOR(ID, KEYWORD) ACCESSOR_KEYWORD(KEYWORD) #else -#define SINGLETON_ACCESSOR(ID, KEYWORD) ACCESSOR(ID) +#define SINGLETON_ACCESSOR(ID, KEYWORD) ACCESSOR(ID, KEYWORD) #endif #endif @@ -116,6 +116,14 @@ #define INIT_ACCESSOR(ID, KEYWORD) SINGLETON_ACCESSOR(ID, KEYWORD) #endif +#ifndef BORROW_ACCESSOR +#define BORROW_ACCESSOR(ID, KEYWORD) SINGLETON_ACCESSOR(ID, KEYWORD) +#endif + +#ifndef MUTATE_ACCESSOR +#define MUTATE_ACCESSOR(ID, KEYWORD) SINGLETON_ACCESSOR(ID, KEYWORD) +#endif + /// This is a getter: a function that is called when a value is loaded /// from the storage. It returns an owned value of the storage type. /// @@ -217,8 +225,12 @@ MUTABLE_ADDRESSOR(MutableAddress, unsafeMutableAddress) /// re-writes assignment to initialization. INIT_ACCESSOR(Init, init) +BORROW_ACCESSOR(Borrow, borrow) + +MUTATE_ACCESSOR(Mutate, mutate) + #ifdef LAST_ACCESSOR -LAST_ACCESSOR(Init) +LAST_ACCESSOR(Mutate) #undef LAST_ACCESSOR #endif diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index 9d2f60388e90a..8bb4a06026259 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -28,6 +28,7 @@ class raw_ostream; namespace swift { class DeclContext; +class Initializer; class ModuleDecl; class VarDecl; class NominalTypeDecl; @@ -88,21 +89,75 @@ class ActorIsolation { /// Set to true if this was parsed from SIL. unsigned silParsed : 1; - unsigned parameterIndex : 27; + /// The opaque value of an EncodedParameterIndex. + /// Only meaningful for ActorInstance. + unsigned encodedParameterIndex : 27; - ActorIsolation(Kind kind, NominalTypeDecl *actor, unsigned parameterIndex); + class EncodedParameterIndex { + enum : unsigned { + SpecialIndex_Capture, + SpecialIndex_Self, + NumSpecialIndexes + }; + + /// Either a special index or (parameter index + NumSpecialIndexes). + unsigned value; - ActorIsolation(Kind kind, VarDecl *actor, unsigned parameterIndex); + constexpr EncodedParameterIndex(unsigned value) : value(value) {} - ActorIsolation(Kind kind, Expr *actor, unsigned parameterIndex); + public: + static constexpr EncodedParameterIndex parameter(unsigned index) { + return EncodedParameterIndex(NumSpecialIndexes + index); + } + static constexpr EncodedParameterIndex self() { + return EncodedParameterIndex(SpecialIndex_Self); + } + static constexpr EncodedParameterIndex capture() { + return EncodedParameterIndex(SpecialIndex_Capture); + } + + unsigned getParameterIndex() const { + assert(value >= NumSpecialIndexes); + return value - NumSpecialIndexes; + } + bool isSelf() const { + return value == SpecialIndex_Self; + } + bool isCapture() const { + return value == SpecialIndex_Capture; + } + + static EncodedParameterIndex fromOpaqueValue(unsigned value) { + return EncodedParameterIndex(value); + } + unsigned getOpaqueValue() const { + return value; + } + }; + + ActorIsolation(Kind kind, NominalTypeDecl *actor, + EncodedParameterIndex parameterIndex); + + ActorIsolation(Kind kind, VarDecl *actor, + EncodedParameterIndex parameterIndex); + + ActorIsolation(Kind kind, Expr *actor, + EncodedParameterIndex parameterIndex); ActorIsolation(Kind kind, Type globalActor); + EncodedParameterIndex getEncodedParameterIndex() const { + return EncodedParameterIndex::fromOpaqueValue(encodedParameterIndex); + } + public: // No-argument constructor needed for DenseMap use in PostfixCompletion.cpp explicit ActorIsolation(Kind kind = Unspecified, bool isSILParsed = false) : pointer(nullptr), kind(kind), isolatedByPreconcurrency(false), - silParsed(isSILParsed), parameterIndex(0) {} + silParsed(isSILParsed), encodedParameterIndex(0) { + // SIL's use of this has weaker invariants for now. + assert(kind != ActorInstance || isSILParsed); + } static ActorIsolation forUnspecified() { return ActorIsolation(Unspecified); @@ -125,19 +180,22 @@ class ActorIsolation { static ActorIsolation forActorInstanceParameter(NominalTypeDecl *actor, unsigned parameterIndex) { - return ActorIsolation(ActorInstance, actor, parameterIndex + 1); + return ActorIsolation(ActorInstance, actor, + EncodedParameterIndex::parameter(parameterIndex)); } static ActorIsolation forActorInstanceParameter(VarDecl *actor, unsigned parameterIndex) { - return ActorIsolation(ActorInstance, actor, parameterIndex + 1); + return ActorIsolation(ActorInstance, actor, + EncodedParameterIndex::parameter(parameterIndex)); } static ActorIsolation forActorInstanceParameter(Expr *actor, unsigned parameterIndex); static ActorIsolation forActorInstanceCapture(VarDecl *capturedActor) { - return ActorIsolation(ActorInstance, capturedActor, 0); + return ActorIsolation(ActorInstance, capturedActor, + EncodedParameterIndex::capture()); } static ActorIsolation forGlobalActor(Type globalActor) { @@ -153,21 +211,14 @@ class ActorIsolation { static std::optional forSILString(StringRef string) { auto kind = llvm::StringSwitch>(string) - .Case("unspecified", - std::optional(ActorIsolation::Unspecified)) - .Case("actor_instance", - std::optional(ActorIsolation::ActorInstance)) - .Case("nonisolated", - std::optional(ActorIsolation::Nonisolated)) - .Case("nonisolated_unsafe", std::optional( - ActorIsolation::NonisolatedUnsafe)) - .Case("global_actor", - std::optional(ActorIsolation::GlobalActor)) - .Case("global_actor_unsafe", - std::optional(ActorIsolation::GlobalActor)) + .Case("unspecified", ActorIsolation::Unspecified) + .Case("actor_instance", ActorIsolation::ActorInstance) + .Case("nonisolated", ActorIsolation::Nonisolated) + .Case("nonisolated_unsafe", ActorIsolation::NonisolatedUnsafe) + .Case("global_actor", ActorIsolation::GlobalActor) + .Case("global_actor_unsafe", ActorIsolation::GlobalActor) .Case("caller_isolation_inheriting", - std::optional( - ActorIsolation::CallerIsolationInheriting)) + ActorIsolation::CallerIsolationInheriting) .Default(std::nullopt); if (kind == std::nullopt) return std::nullopt; @@ -187,17 +238,22 @@ class ActorIsolation { bool isNonisolatedUnsafe() const { return kind == NonisolatedUnsafe; } /// Retrieve the parameter to which actor-instance isolation applies. - /// - /// Parameter 0 is `self`. - unsigned getActorInstanceParameter() const { + unsigned getActorInstanceParameterIndex() const { assert(getKind() == ActorInstance); - return parameterIndex; + return getEncodedParameterIndex().getParameterIndex(); + } + + /// Given that this is actor instance isolation, is it a capture? + bool isActorInstanceForCapture() const { + assert(getKind() == ActorInstance); + return getEncodedParameterIndex().isCapture(); } /// Returns true if this is an actor-instance isolation that additionally /// applies to the self parameter of a method. bool isActorInstanceForSelfParameter() const { - return getActorInstanceParameter() == 0; + assert(getKind() == ActorInstance); + return getEncodedParameterIndex().isSelf(); } bool isSILParsed() const { return silParsed; } @@ -217,6 +273,10 @@ class ActorIsolation { } } + /// In the debugger return the index for the stored actorInstance pointer + /// union index. Asserts if not an actor instance. + SWIFT_DEBUG_HELPER(unsigned getActorInstanceUnionIndex() const); + NominalTypeDecl *getActor() const; VarDecl *getActorInstance() const; @@ -281,13 +341,13 @@ class ActorIsolation { id.AddPointer(pointer); id.AddBoolean(isolatedByPreconcurrency); id.AddBoolean(silParsed); - id.AddInteger(parameterIndex); + id.AddInteger(encodedParameterIndex); } friend llvm::hash_code hash_value(const ActorIsolation &state) { return llvm::hash_combine(state.kind, state.pointer, state.isolatedByPreconcurrency, state.silParsed, - state.parameterIndex); + state.encodedParameterIndex); } void print(llvm::raw_ostream &os) const; @@ -375,6 +435,10 @@ InferredActorIsolation getInferredActorIsolation(ValueDecl *value); ActorIsolation __AbstractClosureExpr_getActorIsolation(AbstractClosureExpr *CE); +/// Determine how the given initialization context is isolated. +ActorIsolation getActorIsolation(Initializer *init, + bool ignoreDefaultArguments = false); + /// Determine how the given declaration context is isolated. /// \p getClosureActorIsolation allows the specification of actor isolation for /// closures that haven't been saved been saved to the AST yet. This is useful diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index 3f5e0cb309983..bdfca59bbb755 100644 --- a/include/swift/AST/AnyFunctionRef.h +++ b/include/swift/AST/AnyFunctionRef.h @@ -72,13 +72,13 @@ class AnyFunctionRef { CaptureInfo getCaptureInfo() const { if (auto *AFD = TheFunction.dyn_cast()) return AFD->getCaptureInfo(); - return TheFunction.get()->getCaptureInfo(); + return cast(TheFunction)->getCaptureInfo(); } ParameterList *getParameters() const { if (auto *AFD = TheFunction.dyn_cast()) return AFD->getParameters(); - return TheFunction.get()->getParameters(); + return cast(TheFunction)->getParameters(); } bool hasExternalPropertyWrapperParameters() const { @@ -90,7 +90,7 @@ class AnyFunctionRef { Type getType() const { if (auto *AFD = TheFunction.dyn_cast()) return AFD->getInterfaceType(); - return TheFunction.get()->getType(); + return cast(TheFunction)->getType(); } Type getBodyResultType() const { @@ -99,7 +99,7 @@ class AnyFunctionRef { return FD->mapTypeIntoContext(FD->getResultInterfaceType()); return TupleType::getEmpty(AFD->getASTContext()); } - return TheFunction.get()->getResultType(); + return cast(TheFunction)->getResultType(); } ArrayRef @@ -115,7 +115,7 @@ class AnyFunctionRef { BraceStmt *getBody() const { if (auto *AFD = TheFunction.dyn_cast()) return AFD->getBody(); - auto *ACE = TheFunction.get(); + auto *ACE = cast(TheFunction); if (auto *CE = dyn_cast(ACE)) return CE->getBody(); return cast(ACE)->getBody(); @@ -127,7 +127,7 @@ class AnyFunctionRef { return; } - auto *ACE = TheFunction.get(); + auto *ACE = cast(TheFunction); if (auto *CE = dyn_cast(ACE)) { CE->setBody(stmt); CE->setBodyState(ClosureExpr::BodyState::Parsed); @@ -143,7 +143,7 @@ class AnyFunctionRef { return; } - auto *ACE = TheFunction.get(); + auto *ACE = cast(TheFunction); if (auto *CE = dyn_cast(ACE)) { CE->setBody(stmt); CE->setBodyState(ClosureExpr::BodyState::TypeChecked); @@ -168,7 +168,7 @@ class AnyFunctionRef { DeclContext *getAsDeclContext() const { if (auto *AFD = TheFunction.dyn_cast()) return AFD; - return TheFunction.get(); + return cast(TheFunction); } AbstractFunctionDecl *getAbstractFunctionDecl() const { @@ -179,6 +179,14 @@ class AnyFunctionRef { return TheFunction.dyn_cast(); } + AccessorDecl *getAccessorDecl() const { + if (auto *accessor = dyn_cast_or_null( + TheFunction.dyn_cast())) { + return accessor; + } + return nullptr; + } + /// Whether this function is @Sendable. bool isSendable() const { if (auto *fnType = getType()->getAs()) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 9f6d49eb6bf36..5bf4de457bab6 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.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 - 2025 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,7 +16,6 @@ #ifndef SWIFT_ATTR_H #define SWIFT_ATTR_H - #include "swift/AST/ASTAllocated.h" #include "swift/AST/AttrKind.h" #include "swift/AST/AutoDiff.h" @@ -29,7 +28,7 @@ #include "swift/AST/LifetimeDependence.h" #include "swift/AST/MacroDeclaration.h" #include "swift/AST/Ownership.h" -#include "swift/AST/PlatformKind.h" +#include "swift/AST/PlatformKindUtils.h" #include "swift/AST/StorageImpl.h" #include "swift/Basic/Debug.h" #include "swift/Basic/EnumTraits.h" @@ -42,10 +41,10 @@ #include "swift/Basic/SourceLoc.h" #include "swift/Basic/UUID.h" #include "swift/Basic/Version.h" -#include "llvm/ADT/bit.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/bit.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TrailingObjects.h" @@ -107,7 +106,46 @@ class alignas(1 << AttrAlignInBits) AttributeBase class DeclAttributes; enum class DeclKind : uint8_t; - /// Represents one declaration attribute. +enum : unsigned { + NumInlineKindBits = + countBitsUsed(static_cast(InlineKind::Last_InlineKind)) +}; + +enum : unsigned { + NumEffectsKindBits = + countBitsUsed(static_cast(EffectsKind::Last_EffectsKind)) +}; + +enum : unsigned { + NumExposureKindBits = + countBitsUsed(static_cast(ExposureKind::Last_ExposureKind)) +}; + +enum : unsigned { + NumExternKindBits = + countBitsUsed(static_cast(ExternKind::Last_ExternKind)) +}; + +enum : unsigned { + NumNonIsolatedModifierBits = countBitsUsed( + static_cast(NonIsolatedModifier::Last_NonIsolatedModifier)) +}; + +enum : unsigned { + NumInheritActorContextKindBits = countBitsUsed(static_cast( + InheritActorContextModifier::Last_InheritActorContextKind)) +}; + +enum : unsigned { + NumNonexhaustiveModeBits = countBitsUsed( + static_cast(NonexhaustiveMode::Last_NonexhaustiveMode)) +}; + +enum : unsigned { NumDeclAttrKindBits = countBitsUsed(NumDeclAttrKinds - 1) }; + +enum : unsigned { NumTypeAttrKindBits = countBitsUsed(NumTypeAttrKinds - 1) }; + +/// Represents one declaration attribute. class DeclAttribute : public AttributeBase { friend class DeclAttributes; @@ -203,7 +241,7 @@ class DeclAttribute : public AttributeBase { ownership : NumReferenceOwnershipBits ); - SWIFT_INLINE_BITFIELD(SpecializeAttr, DeclAttribute, 1+1, + SWIFT_INLINE_BITFIELD(AbstractSpecializeAttr, DeclAttribute, 1+1, exported : 1, kind : 1 ); @@ -226,8 +264,12 @@ class DeclAttribute : public AttributeBase { isEarlyAdopter : 1 ); - SWIFT_INLINE_BITFIELD(NonisolatedAttr, DeclAttribute, 1, - isUnsafe : 1 + SWIFT_INLINE_BITFIELD(NonisolatedAttr, DeclAttribute, NumNonIsolatedModifierBits, + Modifier : NumNonIsolatedModifierBits + ); + + SWIFT_INLINE_BITFIELD(InheritActorContextAttr, DeclAttribute, NumInheritActorContextKindBits, + Modifier : NumInheritActorContextKindBits ); SWIFT_INLINE_BITFIELD_FULL(AllowFeatureSuppressionAttr, DeclAttribute, 1+31, @@ -237,8 +279,12 @@ class DeclAttribute : public AttributeBase { NumFeatures : 31 ); - SWIFT_INLINE_BITFIELD(ExecutionAttr, DeclAttribute, NumExecutionKindBits, - Behavior : NumExecutionKindBits + SWIFT_INLINE_BITFIELD(LifetimeAttr, DeclAttribute, 1, + isUnderscored : 1 + ); + + SWIFT_INLINE_BITFIELD(NonexhaustiveAttr, DeclAttribute, NumNonexhaustiveModeBits, + mode : NumNonexhaustiveModeBits ); } Bits; // clang-format on @@ -384,21 +430,14 @@ class DeclAttribute : public AttributeBase { /// valid if they match. EquivalentInABIAttr = 1ull << 18, - /// Attribute can be used in an \c \@abi attribute, but must match - /// equivalent on API decl; if omitted, API decl's attribute will be - /// cloned. Use where you would want to use \c EquivalentInABIAttr but - /// repeating the attribute is judged too burdensome. - InferredInABIAttr = 1ull << 19, - /// Use for attributes which are \em only valid on declarations that cannot /// have an \c @abi attribute, such as \c ImportDecl . - UnreachableInABIAttr = 1ull << 20, + UnreachableInABIAttr = 1ull << 19, }; enum : uint64_t { InABIAttrMask = ForbiddenInABIAttr | UnconstrainedInABIAttr - | EquivalentInABIAttr | InferredInABIAttr - | UnreachableInABIAttr + | EquivalentInABIAttr | UnreachableInABIAttr }; LLVM_READNONE @@ -694,22 +733,29 @@ class SectionAttr : public DeclAttribute { /// Defines the @_cdecl attribute. class CDeclAttr : public DeclAttribute { public: - CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit) - : DeclAttribute(DeclAttrKind::CDecl, AtLoc, Range, Implicit), Name(Name) { + CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit, + bool Underscored) + : DeclAttribute(DeclAttrKind::CDecl, AtLoc, Range, Implicit), + Name(Name), Underscored(Underscored) { } - CDeclAttr(StringRef Name, bool Implicit) - : CDeclAttr(Name, SourceLoc(), SourceRange(), Implicit) {} + CDeclAttr(StringRef Name, bool Implicit, bool Underscored) + : CDeclAttr(Name, SourceLoc(), SourceRange(), Implicit, Underscored) {} /// The symbol name. const StringRef Name; + /// Is this the version of the attribute that's underscored? + /// Used to preserve retro compatibility with early adopters. + const bool Underscored; + static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::CDecl; } CDeclAttr *clone(ASTContext &ctx) const { - return new (ctx) CDeclAttr(Name, AtLoc, Range, isImplicit()); + return new (ctx) CDeclAttr(Name, AtLoc, Range, isImplicit(), + Underscored); } bool isEquivalent(const CDeclAttr *other, Decl *attachedTo) const { @@ -1052,7 +1098,7 @@ class ObjCAttr final : public DeclAttribute, unsigned length = 2; if (auto name = getName()) length += name->getNumSelectorPieces(); - return {getTrailingObjects(), length}; + return getTrailingObjects(length); } /// Retrieve the trailing location information. @@ -1061,7 +1107,7 @@ class ObjCAttr final : public DeclAttribute, unsigned length = 2; if (auto name = getName()) length += name->getNumSelectorPieces(); - return {getTrailingObjects(), length}; + return getTrailingObjects(length); } public: @@ -1237,14 +1283,14 @@ class DynamicReplacementAttr final MutableArrayRef getTrailingLocations() { assert(Bits.DynamicReplacementAttr.HasTrailingLocationInfo); unsigned length = 2; - return {getTrailingObjects(), length}; + return getTrailingObjects(length); } /// Retrieve the trailing location information. ArrayRef getTrailingLocations() const { assert(Bits.DynamicReplacementAttr.HasTrailingLocationInfo); unsigned length = 2; // lParens, rParens - return {getTrailingObjects(), length}; + return getTrailingObjects(length); } public: @@ -1434,8 +1480,7 @@ class SPIAccessControlAttr final : public DeclAttribute, /// 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 }; + return getTrailingObjects(numSPIGroups); } static bool classof(const DeclAttribute *DA) { @@ -1730,15 +1775,17 @@ class SynthesizedProtocolAttr : public DeclAttribute { } }; -/// The @_specialize attribute, which forces specialization on the specified -/// type list. -class SpecializeAttr final +/// The @_specialize/@specialize attribute, which forces specialization on the +/// specified type list. +template +using SpecializeAttrTrailingObjects = llvm::TrailingObjects; + +class AbstractSpecializeAttr : public DeclAttribute, - private llvm::TrailingObjects { + private llvm::trailing_objects_internal::TrailingObjectsBase { friend class SpecializeAttrTargetDeclRequest; friend class SerializeAttrGenericSignatureRequest; - friend TrailingObjects; public: // NOTE: When adding new kinds, you must update the inline bitfield macro. @@ -1759,37 +1806,16 @@ class SpecializeAttr final size_t numTypeErasedParams; bool typeErasedParamsInitialized; - SpecializeAttr(SourceLoc atLoc, SourceRange Range, - TrailingWhereClause *clause, bool exported, +protected: + AbstractSpecializeAttr(DeclAttrKind DK, SourceLoc atLoc, SourceRange Range, + TrailingWhereClause *clause, + bool exported, SpecializationKind kind, GenericSignature specializedSignature, DeclNameRef targetFunctionName, ArrayRef spiGroups, ArrayRef availabilityAttrs, size_t typeErasedParamsCount); public: - static SpecializeAttr * - create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range, - TrailingWhereClause *clause, bool exported, SpecializationKind kind, - DeclNameRef targetFunctionName, ArrayRef spiGroups, - ArrayRef availabilityAttrs, - GenericSignature specializedSignature = nullptr); - - static SpecializeAttr *create(ASTContext &ctx, bool exported, - SpecializationKind kind, - ArrayRef spiGroups, - ArrayRef availabilityAttrs, - GenericSignature specializedSignature, - DeclNameRef replacedFunction); - - static SpecializeAttr *create(ASTContext &ctx, bool exported, - SpecializationKind kind, - ArrayRef spiGroups, - ArrayRef availabilityAttrs, - ArrayRef typeErasedParams, - GenericSignature specializedSignature, - DeclNameRef replacedFunction, - LazyMemberLoader *resolver, uint64_t data); - size_t numTrailingObjects(OverloadToken) const { return numSPIGroups; } @@ -1797,17 +1823,27 @@ class SpecializeAttr final size_t numTrailingObjects(OverloadToken) const { return numAvailableAttrs; } + // Helper to get the trailing objects of one of the subclasses. + template + const Type *getSubclassTrailingObjects() const; + + template + Type *getSubclassTrailingObjects() { + const auto *constThis = this; + return const_cast(constThis->getSubclassTrailingObjects()); + } + /// 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(), + return { getSubclassTrailingObjects(), numSPIGroups }; } ArrayRef getAvailableAttrs() const { - return {this->template getTrailingObjects(), + return {getSubclassTrailingObjects(), numAvailableAttrs}; } @@ -1815,26 +1851,36 @@ class SpecializeAttr final if (!typeErasedParamsInitialized) return {}; - return {this->template getTrailingObjects(), + return {getSubclassTrailingObjects(), numTypeErasedParams}; } void setTypeErasedParams(const ArrayRef typeErasedParams) { assert(typeErasedParams.size() == numTypeErasedParams); if (!typeErasedParamsInitialized) { - std::uninitialized_copy(typeErasedParams.begin(), typeErasedParams.end(), getTrailingObjects()); + std::uninitialized_copy(typeErasedParams.begin(), typeErasedParams.end(), + getSubclassTrailingObjects()); typeErasedParamsInitialized = true; } } + void setResolver(LazyMemberLoader *resolver, uint64_t resolverContextData) { + this->resolver = resolver; + this->resolverContextData = resolverContextData; + } + TrailingWhereClause *getTrailingWhereClause() const; + bool isPublic() const { + return getKind() == DeclAttrKind::Specialized; + } + bool isExported() const { - return Bits.SpecializeAttr.exported; + return Bits.AbstractSpecializeAttr.exported; } SpecializationKind getSpecializationKind() const { - return SpecializationKind(Bits.SpecializeAttr.kind); + return SpecializationKind(Bits.AbstractSpecializeAttr.kind); } bool isFullSpecialization() const { @@ -1856,15 +1902,140 @@ class SpecializeAttr final GenericSignature getSpecializedSignature(const AbstractFunctionDecl *forDecl) const; + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DeclAttrKind::Specialize || + DA->getKind() == DeclAttrKind::Specialized; + } + + UNIMPLEMENTED_CLONE(AbstractSpecializeAttr) + + bool isEquivalent(const AbstractSpecializeAttr *other, Decl *attachedTo) const; +}; + +/// The @_specialize attribute. +class SpecializeAttr final : public AbstractSpecializeAttr, + private SpecializeAttrTrailingObjects { + friend TrailingObjects; + friend AbstractSpecializeAttr; + + // WARNING: Do not add storage here. The base class uses TrailingObjects. +private: + SpecializeAttr(SourceLoc atLoc, SourceRange Range, + TrailingWhereClause *clause, + bool exported, + SpecializationKind kind, GenericSignature specializedSignature, + DeclNameRef targetFunctionName, ArrayRef spiGroups, + ArrayRef availabilityAttrs, + size_t typeErasedParamsCount) : + AbstractSpecializeAttr(DeclAttrKind::Specialize, atLoc, Range, clause, + exported, kind, specializedSignature, targetFunctionName, + spiGroups, availabilityAttrs, typeErasedParamsCount) {} + +public: + static SpecializeAttr * + create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range, + TrailingWhereClause *clause, bool exported, + SpecializationKind kind, + DeclNameRef targetFunctionName, ArrayRef spiGroups, + ArrayRef availabilityAttrs, + GenericSignature specializedSignature = nullptr); + + static SpecializeAttr *create(ASTContext &ctx, bool exported, + SpecializationKind kind, + ArrayRef spiGroups, + ArrayRef availabilityAttrs, + GenericSignature specializedSignature, + DeclNameRef replacedFunction); + + static SpecializeAttr *create(ASTContext &ctx, bool exported, + SpecializationKind kind, + ArrayRef spiGroups, + ArrayRef availabilityAttrs, + ArrayRef typeErasedParams, + GenericSignature specializedSignature, + DeclNameRef replacedFunction, + LazyMemberLoader *resolver, uint64_t data); + static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::Specialize; } UNIMPLEMENTED_CLONE(SpecializeAttr) - bool isEquivalent(const SpecializeAttr *other, Decl *attachedTo) const; + bool isEquivalent(const SpecializeAttr *other, Decl *attachedTo) const { + return AbstractSpecializeAttr::isEquivalent(other, attachedTo); + } }; +/// The @specialized attribute. +class SpecializedAttr final : public AbstractSpecializeAttr , + private SpecializeAttrTrailingObjects { + friend TrailingObjects; + friend AbstractSpecializeAttr; + + // WARNING: Do not add storage here. The base class uses TrailingObjects. +private: + + SpecializedAttr(SourceLoc atLoc, SourceRange Range, + TrailingWhereClause *clause, + bool exported, + SpecializationKind kind, GenericSignature specializedSignature, + DeclNameRef targetFunctionName, ArrayRef spiGroups, + ArrayRef availabilityAttrs, + size_t typeErasedParamsCount) : + AbstractSpecializeAttr(DeclAttrKind::Specialized, atLoc, Range, clause, + exported, kind, specializedSignature, targetFunctionName, + spiGroups, availabilityAttrs, typeErasedParamsCount) {} + +public: + static SpecializedAttr * + create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range, + TrailingWhereClause *clause, bool exported, + SpecializationKind kind, + DeclNameRef targetFunctionName, ArrayRef spiGroups, + ArrayRef availabilityAttrs, + GenericSignature specializedSignature = nullptr); + + static SpecializedAttr *create(ASTContext &ctx, bool exported, + SpecializationKind kind, + ArrayRef spiGroups, + ArrayRef availabilityAttrs, + GenericSignature specializedSignature, + DeclNameRef replacedFunction); + + static SpecializedAttr *create(ASTContext &ctx, bool exported, + SpecializationKind kind, + ArrayRef spiGroups, + ArrayRef availabilityAttrs, + ArrayRef typeErasedParams, + GenericSignature specializedSignature, + DeclNameRef replacedFunction, + LazyMemberLoader *resolver, uint64_t data); + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DeclAttrKind::Specialized; + } + + UNIMPLEMENTED_CLONE(SpecializedAttr) + + bool isEquivalent(const SpecializedAttr *other, Decl *attachedTo) const { + return AbstractSpecializeAttr::isEquivalent(other, attachedTo); + } +}; + +template +const Type *AbstractSpecializeAttr::getSubclassTrailingObjects() const { + if (auto attr = dyn_cast(this)) { + return attr->getTrailingObjects(); + } + if (auto attr = dyn_cast(this)) { + return attr->getTrailingObjects(); + } + llvm_unreachable("unhandled AbstractSpecializeAttr subclass?"); +} + + + class StorageRestrictionsAttr final : public DeclAttribute, private llvm::TrailingObjects { @@ -1887,11 +2058,11 @@ class StorageRestrictionsAttr final unsigned getNumAccessesProperties() const { return NumAccesses; } ArrayRef getInitializesNames() const { - return {getTrailingObjects(), NumInitializes}; + return getTrailingObjects(NumInitializes); } ArrayRef getAccessesNames() const { - return {getTrailingObjects() + NumInitializes, NumAccesses}; + return {getTrailingObjects() + NumInitializes, NumAccesses}; } ArrayRef getInitializesProperties(AccessorDecl *attachedTo) const; @@ -2172,6 +2343,8 @@ class CustomAttr final : public DeclAttribute { bool isEquivalent(const CustomAttr *other, Decl *attachedTo) const; + void printCustomAttr(ASTPrinter &Printer, const PrintOptions &Options) const; + private: friend class CustomAttrNominalRequest; void resetTypeInformation(TypeExpr *repr); @@ -2227,6 +2400,11 @@ class ProjectedValuePropertyAttr : public DeclAttribute { /// The variable \p foo was originally defined in another module called /// \p Original prior to OSX 10.15 class OriginallyDefinedInAttr: public DeclAttribute { + const StringRef ManglingModuleName; + const StringRef LinkerModuleName; + const PlatformKind Platform; + const llvm::VersionTuple MovedVersion; + public: OriginallyDefinedInAttr(SourceLoc AtLoc, SourceRange Range, StringRef OriginalModuleName, @@ -2248,16 +2426,22 @@ class OriginallyDefinedInAttr: public DeclAttribute { OriginallyDefinedInAttr *clone(ASTContext &C, bool implicit) const; // The original module name for mangling. - const StringRef ManglingModuleName; + StringRef getManglingModuleName() const { return ManglingModuleName; } // The original module name for linker directives. - const StringRef LinkerModuleName; + StringRef getLinkerModuleName() const { return LinkerModuleName; } /// The platform of the symbol. - const PlatformKind Platform; + PlatformKind getPlatform() const { return Platform; } - /// Indicates when the symbol was moved here. - const llvm::VersionTuple MovedVersion; + /// The version of the platform that the symbol was moved in, as it was + /// written in source. + llvm::VersionTuple getParsedMovedVersion() const { return MovedVersion; } + + /// The version of the platform that the symbol was moved in. This may be + /// different than the version that was written in source due to + /// canonicalization. + llvm::VersionTuple getMovedVersion() const; struct ActiveVersion { StringRef ManglingModuleName; @@ -2377,10 +2561,10 @@ class DifferentiableAttr final /// The parsed differentiability parameters, i.e. the list of parameters /// specified in 'wrt:'. ArrayRef getParsedParameters() const { - return {getTrailingObjects(), NumParsedParameters}; + return getTrailingObjects(NumParsedParameters); } MutableArrayRef getParsedParameters() { - return {getTrailingObjects(), NumParsedParameters}; + return getTrailingObjects(NumParsedParameters); } size_t numTrailingObjects(OverloadToken) const { return NumParsedParameters; @@ -2562,10 +2746,10 @@ class DerivativeAttr final /// The parsed differentiability parameters, i.e. the list of parameters /// specified in 'wrt:'. ArrayRef getParsedParameters() const { - return {getTrailingObjects(), NumParsedParameters}; + return getTrailingObjects(NumParsedParameters); } MutableArrayRef getParsedParameters() { - return {getTrailingObjects(), NumParsedParameters}; + return getTrailingObjects(NumParsedParameters); } size_t numTrailingObjects(OverloadToken) const { return NumParsedParameters; @@ -2653,10 +2837,10 @@ class TransposeAttr final /// The parsed linearity parameters, i.e. the list of parameters specified in /// 'wrt:'. ArrayRef getParsedParameters() const { - return {getTrailingObjects(), NumParsedParameters}; + return getTrailingObjects(NumParsedParameters); } MutableArrayRef getParsedParameters() { - return {getTrailingObjects(), NumParsedParameters}; + return getTrailingObjects(NumParsedParameters); } size_t numTrailingObjects(OverloadToken) const { return NumParsedParameters; @@ -2756,21 +2940,35 @@ class UnavailableFromAsyncAttr : public DeclAttribute { /// available for back deployment to older OSes via emission into the client /// binary. class BackDeployedAttr : public DeclAttribute { + const PlatformKind Platform; + const llvm::VersionTuple Version; + public: BackDeployedAttr(SourceLoc AtLoc, SourceRange Range, PlatformKind Platform, const llvm::VersionTuple &Version, bool Implicit) : DeclAttribute(DeclAttrKind::BackDeployed, AtLoc, Range, Implicit), Platform(Platform), Version(Version) {} - /// The platform the symbol is available for back deployment on. - const PlatformKind Platform; + /// The platform the decl is available for back deployment in. + PlatformKind getPlatform() const { return Platform; } - /// The earliest platform version that may use the back deployed implementation. - const llvm::VersionTuple Version; + /// The `before:` version tuple that was written in source. + llvm::VersionTuple getParsedVersion() const { return Version; } + + /// The `before:` version, which is the earliest platform version that may use + /// the back deployed implementation. This may be different from the version + /// that was written in source due to canonicalization. + llvm::VersionTuple getVersion() const; /// Returns true if this attribute is active given the current platform. bool isActivePlatform(const ASTContext &ctx, bool forTargetVariant) const; + /// Returns the `AvailabilityDomain` that the decl is available for back + /// deployment in. + AvailabilityDomain getAvailabilityDomain() const { + return AvailabilityDomain::forPlatform(Platform); + } + static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::BackDeployed; } @@ -2978,17 +3176,28 @@ class ObjCImplementationAttr final : public DeclAttribute { /// Represents nonisolated modifier. class NonisolatedAttr final : public DeclAttribute { public: - NonisolatedAttr(SourceLoc atLoc, SourceRange range, bool unsafe, - bool implicit) + NonisolatedAttr(SourceLoc atLoc, SourceRange range, + NonIsolatedModifier modifier, bool implicit) : DeclAttribute(DeclAttrKind::Nonisolated, atLoc, range, implicit) { - Bits.NonisolatedAttr.isUnsafe = unsafe; - assert((isUnsafe() == unsafe) && "not enough bits for unsafe state"); + Bits.NonisolatedAttr.Modifier = static_cast(modifier); + assert((getModifier() == modifier) && "not enough bits for modifier"); + } + + NonIsolatedModifier getModifier() const { + return static_cast(Bits.NonisolatedAttr.Modifier); } - NonisolatedAttr(bool unsafe, bool implicit) - : NonisolatedAttr({}, {}, unsafe, implicit) {} + bool isUnsafe() const { return getModifier() == NonIsolatedModifier::Unsafe; } + bool isNonSending() const { + return getModifier() == NonIsolatedModifier::NonSending; + } - bool isUnsafe() const { return Bits.NonisolatedAttr.isUnsafe; } + static NonisolatedAttr * + createImplicit(ASTContext &ctx, + NonIsolatedModifier modifier = NonIsolatedModifier::None) { + return new (ctx) NonisolatedAttr(/*atLoc*/ {}, /*range*/ {}, modifier, + /*implicit=*/true); + } static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::Nonisolated; @@ -2996,11 +3205,55 @@ class NonisolatedAttr final : public DeclAttribute { /// Create a copy of this attribute. NonisolatedAttr *clone(ASTContext &ctx) const { - return new (ctx) NonisolatedAttr(AtLoc, Range, isUnsafe(), isImplicit()); + return new (ctx) NonisolatedAttr(AtLoc, Range, getModifier(), isImplicit()); } bool isEquivalent(const NonisolatedAttr *other, Decl *attachedTo) const { - return isUnsafe() == other->isUnsafe(); + return getModifier() == other->getModifier(); + } +}; + +/// Represents @_inheritActorContext modifier. +class InheritActorContextAttr final : public DeclAttribute { +public: + InheritActorContextAttr(SourceLoc atLoc, SourceRange range, + InheritActorContextModifier modifier, bool implicit) + : DeclAttribute(DeclAttrKind::InheritActorContext, atLoc, range, + implicit) { + Bits.InheritActorContextAttr.Modifier = static_cast(modifier); + assert((getModifier() == modifier) && "not enough bits for modifier"); + } + + InheritActorContextModifier getModifier() const { + return static_cast( + Bits.InheritActorContextAttr.Modifier); + } + + bool isAlways() const { + return getModifier() == InheritActorContextModifier::Always; + } + + static InheritActorContextAttr * + createImplicit(ASTContext &ctx, InheritActorContextModifier modifier = + InheritActorContextModifier::None) { + return new (ctx) + InheritActorContextAttr(/*atLoc*/ {}, /*range*/ {}, modifier, + /*implicit=*/true); + } + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DeclAttrKind::InheritActorContext; + } + + /// Create a copy of this attribute. + InheritActorContextAttr *clone(ASTContext &ctx) const { + return new (ctx) + InheritActorContextAttr(AtLoc, Range, getModifier(), isImplicit()); + } + + bool isEquivalent(const InheritActorContextAttr *other, + Decl *attachedTo) const { + return getModifier() == other->getModifier(); } }; @@ -3171,26 +3424,33 @@ class LifetimeAttr final : public DeclAttribute { LifetimeEntry *entry; LifetimeAttr(SourceLoc atLoc, SourceRange baseRange, bool implicit, - LifetimeEntry *entry) + LifetimeEntry *entry, bool isUnderscored) : DeclAttribute(DeclAttrKind::Lifetime, atLoc, baseRange, implicit), - entry(entry) {} + entry(entry) { + Bits.LifetimeAttr.isUnderscored = isUnderscored; + } public: static LifetimeAttr *create(ASTContext &context, SourceLoc atLoc, SourceRange baseRange, bool implicit, - LifetimeEntry *entry); + LifetimeEntry *entry, bool isUnderscored); LifetimeEntry *getLifetimeEntry() const { return entry; } + bool isUnderscored() const { return bool(Bits.LifetimeAttr.isUnderscored); } + static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::Lifetime; } /// Create a copy of this attribute. LifetimeAttr *clone(ASTContext &ctx) const { - return new (ctx) LifetimeAttr(AtLoc, Range, isImplicit(), entry); + return new (ctx) + LifetimeAttr(AtLoc, Range, isImplicit(), entry, isUnderscored()); } + std::string getString() const; + bool isEquivalent(const LifetimeAttr *other, Decl *attachedTo) const; }; @@ -3231,8 +3491,8 @@ class AllowFeatureSuppressionAttr final bool getInverted() const { return Bits.AllowFeatureSuppressionAttr.Inverted; } ArrayRef getSuppressedFeatures() const { - return {getTrailingObjects(), - static_cast(Bits.AllowFeatureSuppressionAttr.NumFeatures)}; + return getTrailingObjects( + static_cast(Bits.AllowFeatureSuppressionAttr.NumFeatures)); } static bool classof(const DeclAttribute *DA) { @@ -3275,34 +3535,39 @@ class ABIAttr : public DeclAttribute { } }; -class ExecutionAttr : public DeclAttribute { +/// Defines a @nonexhaustive attribute. +class NonexhaustiveAttr : public DeclAttribute { public: - ExecutionAttr(SourceLoc AtLoc, SourceRange Range, - ExecutionKind behavior, - bool Implicit) - : DeclAttribute(DeclAttrKind::Execution, AtLoc, Range, Implicit) { - Bits.ExecutionAttr.Behavior = static_cast(behavior); + NonexhaustiveAttr(SourceLoc atLoc, SourceRange range, NonexhaustiveMode mode, + bool implicit = false) + : DeclAttribute(DeclAttrKind::Nonexhaustive, atLoc, range, implicit) { + Bits.NonexhaustiveAttr.mode = unsigned(mode); } - ExecutionAttr(ExecutionKind behavior, bool Implicit) - : ExecutionAttr(/*AtLoc=*/SourceLoc(), /*Range=*/SourceRange(), behavior, - Implicit) {} + NonexhaustiveAttr(NonexhaustiveMode mode) + : NonexhaustiveAttr(SourceLoc(), SourceRange(), mode) {} - ExecutionKind getBehavior() const { - return static_cast(Bits.ExecutionAttr.Behavior); + NonexhaustiveMode getMode() const { + return NonexhaustiveMode(Bits.NonexhaustiveAttr.mode); } static bool classof(const DeclAttribute *DA) { - return DA->getKind() == DeclAttrKind::Execution; + return DA->getKind() == DeclAttrKind::Nonexhaustive; } - UNIMPLEMENTED_CLONE(ExecutionAttr) + NonexhaustiveAttr *clone(ASTContext &ctx) const { + return new (ctx) NonexhaustiveAttr(AtLoc, Range, getMode(), isImplicit()); + } - bool isEquivalent(const ExecutionAttr *other, Decl *attachedTo) const { - return getBehavior() == other->getBehavior(); + bool isEquivalent(const NonexhaustiveAttr *other, Decl *attachedTo) const { + return getMode() == other->getMode(); } }; + +/// The kind of unary operator, if any. +enum class UnaryOperatorKind : uint8_t { None, Prefix, Postfix }; + /// Attributes that may be applied to declarations. class DeclAttributes { /// Linked list of declaration attributes. @@ -3605,13 +3870,23 @@ class SemanticAvailableAttr final { /// The source range of the `introduced:` version component. SourceRange getIntroducedSourceRange() const { return attr->IntroducedRange; } - /// Returns the effective introduction range indicated by this attribute. - /// This may correspond to the version specified by the `introduced:` - /// component (remapped or canonicalized if necessary) or it may be "always" - /// for an attribute indicating availability in a version-less domain. Returns - /// `std::nullopt` if the attribute does not indicate introduction. + /// See `getIntroducedDomainAndRange()`. std::optional - getIntroducedRange(const ASTContext &Ctx) const; + getIntroducedRange(const ASTContext &ctx) const { + if (auto domainAndRange = getIntroducedDomainAndRange(ctx)) + return domainAndRange->getRange(); + return std::nullopt; + } + + /// Returns the effective introduction range indicated by this attribute, + /// along with the domain that it applies to (which may be different than the + /// domain which the attribute was written with if a remap is required). This + /// may correspond to the version specified by the `introduced:` component + /// (remapped or canonicalized if necessary) or it may be "always" for an + /// attribute indicating availability in a version-less domain. Returns + /// `std::nullopt` if the attribute does not indicate introduction. + std::optional + getIntroducedDomainAndRange(const ASTContext &ctx) const; /// The version tuple for the `deprecated:` component. std::optional getDeprecated() const; @@ -3619,13 +3894,23 @@ class SemanticAvailableAttr final { /// The source range of the `deprecated:` version component. SourceRange getDeprecatedSourceRange() const { return attr->DeprecatedRange; } - /// Returns the effective deprecation range indicated by this attribute. - /// This may correspond to the version specified by the `deprecated:` - /// component (remapped or canonicalized if necessary) or it may be "always" - /// for an unconditional deprecation attribute. Returns `std::nullopt` if the - /// attribute does not indicate deprecation. + /// See `getDeprecatedDomainAndRange()`. std::optional - getDeprecatedRange(const ASTContext &Ctx) const; + getDeprecatedRange(const ASTContext &ctx) const { + if (auto domainAndRange = getDeprecatedDomainAndRange(ctx)) + return domainAndRange->getRange(); + return std::nullopt; + } + + /// Returns the effective deprecation range indicated by this attribute, along + /// with the domain that it applies to (which may be different than the domain + /// which the attribute was written with if a remap is required). This may + /// correspond to the version specified by the `deprecated:` component + /// (remapped or canonicalized if necessary) or it may be "always" for an + /// unconditional deprecation attribute. Returns `std::nullopt` if the + /// attribute does not indicate deprecation. + std::optional + getDeprecatedDomainAndRange(const ASTContext &ctx) const; /// The version tuple for the `obsoleted:` component. std::optional getObsoleted() const; @@ -3633,13 +3918,23 @@ class SemanticAvailableAttr final { /// The source range of the `obsoleted:` version component. SourceRange getObsoletedSourceRange() const { return attr->ObsoletedRange; } - /// Returns the effective obsoletion range indicated by this attribute. - /// This always corresponds to the version specified by the `obsoleted:` - /// component (remapped or canonicalized if necessary). Returns `std::nullopt` - /// if the attribute does not indicate obsoletion (note that unavailability is - /// separate from obsoletion. + /// See `getObsoletedDomainAndRange()`. std::optional - getObsoletedRange(const ASTContext &Ctx) const; + getObsoletedRange(const ASTContext &ctx) const { + if (auto domainAndRange = getObsoletedDomainAndRange(ctx)) + return domainAndRange->getRange(); + return std::nullopt; + } + + /// Returns the effective obsoletion range indicated by this attribute, along + /// with the domain that it applies to (which may be different than the domain + /// which the attribute was written with if a remap is required). This may + /// correspond to the version specified by the `obsoleted:` component + /// (remapped or canonicalized if necessary) or it may be "always" if the + /// attribute represents unconditional unavailability. Returns `std::nullopt` + /// if the attribute does not indicate obsoletion. + std::optional + getObsoletedDomainAndRange(const ASTContext &ctx) const; /// Returns the `message:` field of the attribute, or an empty string. StringRef getMessage() const { return attr->Message; } @@ -3677,7 +3972,7 @@ class SemanticAvailableAttr final { /// Whether this is a language mode specific attribute. bool isSwiftLanguageModeSpecific() const { - return getDomain().isSwiftLanguage() && isVersionSpecific(); + return getDomain().isSwiftLanguageMode() && isVersionSpecific(); } /// Whether this is a PackageDescription version specific attribute. @@ -3762,10 +4057,6 @@ class alignas(1 << AttrAlignInBits) TypeAttribute SWIFT_INLINE_BITFIELD_FULL(IsolatedTypeAttr, TypeAttribute, 8, Kind : 8 ); - - SWIFT_INLINE_BITFIELD_FULL(ExecutionTypeAttr, TypeAttribute, 8, - Behavior : 8 - ); } Bits; // clang-format on @@ -3781,6 +4072,10 @@ class alignas(1 << AttrAlignInBits) TypeAttribute TypeAttrKind getKind() const { return TypeAttrKind(Bits.TypeAttribute.Kind); } + + /// - Note: Do not call this directly when emitting a diagnostic. Instead, + /// define the diagnostic to accept a `const TypeAttribute *` and use the + /// appropriate format specifier. const char *getAttrName() const { return getAttrName(getKind()); } @@ -4033,28 +4328,6 @@ class IsolatedTypeAttr : public SimpleTypeAttrWithArgs { void printImpl(ASTPrinter &printer, const PrintOptions &options) const; }; -/// The @execution function type attribute. -class ExecutionTypeAttr : public SimpleTypeAttrWithArgs { - SourceLoc BehaviorLoc; - -public: - ExecutionTypeAttr(SourceLoc atLoc, SourceLoc kwLoc, SourceRange parensRange, - Located behavior) - : SimpleTypeAttr(atLoc, kwLoc, parensRange), BehaviorLoc(behavior.Loc) { - Bits.ExecutionTypeAttr.Behavior = uint8_t(behavior.Item); - } - - ExecutionKind getBehavior() const { - return ExecutionKind(Bits.ExecutionTypeAttr.Behavior); - } - - SourceLoc getBehaviorLoc() const { - return BehaviorLoc; - } - - void printImpl(ASTPrinter &printer, const PrintOptions &options) const; -}; - using TypeOrCustomAttr = llvm::PointerUnion; diff --git a/include/swift/AST/AttrKind.h b/include/swift/AST/AttrKind.h index d56edbc4b6e31..271f90d31b662 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.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 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -17,151 +17,156 @@ #ifndef SWIFT_ATTRKIND_H #define SWIFT_ATTRKIND_H -#include "swift/Basic/InlineBitfield.h" -#include "swift/Basic/LLVM.h" -#include "swift/Config.h" -#include "llvm/Support/DataTypes.h" +/// `AttrKind.h` is imported into Swift. Be *very* careful with what you +/// include here and keep these includes minimal! +/// +/// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" +#include + +namespace llvm { +class StringRef; +} namespace swift { /// The associativity of a binary operator. -enum class Associativity : uint8_t { +enum class ENUM_EXTENSIBILITY_ATTR(closed) Associativity : uint8_t { /// Non-associative operators cannot be written next to other /// operators with the same precedence. Relational operators are /// typically non-associative. - None, + None SWIFT_NAME("none"), /// Left-associative operators associate to the left if written next /// to other left-associative operators of the same precedence. - Left, + Left SWIFT_NAME("left"), /// Right-associative operators associate to the right if written /// next to other right-associative operators of the same precedence. - Right + Right SWIFT_NAME("right") }; /// Returns the in-source spelling of the given associativity. -StringRef getAssociativitySpelling(Associativity value); - -/// The kind of unary operator, if any. -enum class UnaryOperatorKind : uint8_t { - None, - Prefix, - Postfix -}; +SWIFT_UNAVAILABLE("Unavailable in Swift") +llvm::StringRef getAssociativitySpelling(Associativity value); /// Access control levels. // These are used in diagnostics and with < and similar operations, // so please do not reorder existing values. -enum class AccessLevel : uint8_t { +enum class ENUM_EXTENSIBILITY_ATTR(closed) AccessLevel : uint8_t { /// Private access is limited to the current scope. - Private = 0, + Private SWIFT_NAME("private") = 0, /// File-private access is limited to the current file. - FilePrivate, + FilePrivate SWIFT_NAME("fileprivate"), /// Internal access is limited to the current module. - Internal, + Internal SWIFT_NAME("internal"), /// Package access is not limited, but some capabilities may be /// restricted outside of the current package containing modules. /// It's similar to Public in that it's accessible from other modules /// and subclassable only within the defining module as long as /// the modules are in the same package. - Package, + Package SWIFT_NAME("package"), /// Public access is not limited, but some capabilities may be /// restricted outside of the current module. - Public, + Public SWIFT_NAME("public"), /// Open access is not limited, and all capabilities are unrestricted. - Open, + Open SWIFT_NAME("open"), }; /// Returns the in-source spelling of the given access level. -StringRef getAccessLevelSpelling(AccessLevel value); +SWIFT_UNAVAILABLE("Unavailable in Swift") +llvm::StringRef getAccessLevelSpelling(AccessLevel value); -enum class InlineKind : uint8_t { - Never = 0, - Always = 1, +enum class ENUM_EXTENSIBILITY_ATTR(closed) InlineKind : uint8_t { + Never SWIFT_NAME("never") = 0, + AlwaysUnderscored SWIFT_NAME("alwaysUnderscored") = 1, + Always SWIFT_NAME("always") = 2, Last_InlineKind = Always }; -enum : unsigned { NumInlineKindBits = - countBitsUsed(static_cast(InlineKind::Last_InlineKind)) }; - - /// This enum represents the possible values of the @_effects attribute. /// These values are ordered from the strongest guarantee to the weakest, /// so please do not reorder existing values. -enum class EffectsKind : uint8_t { - ReadNone, - ReadOnly, - ReleaseNone, - ReadWrite, - Unspecified, - Custom, - Last_EffectsKind = Unspecified +enum class ENUM_EXTENSIBILITY_ATTR(closed) EffectsKind : uint8_t { + ReadNone SWIFT_NAME("readnone"), + ReadOnly SWIFT_NAME("readonly"), + ReleaseNone SWIFT_NAME("releasenone"), + ReadWrite SWIFT_NAME("readwrite"), + Unspecified SWIFT_NAME("unspecified"), + Custom SWIFT_NAME("custom"), + Last_EffectsKind = Custom }; -enum : unsigned { NumEffectsKindBits = - countBitsUsed(static_cast(EffectsKind::Last_EffectsKind)) }; - /// This enum represents the possible values of the @_expose attribute. -enum class ExposureKind: uint8_t { - Cxx, - Wasm, +enum class ENUM_EXTENSIBILITY_ATTR(closed) ExposureKind : uint8_t { + Cxx SWIFT_NAME("cxx"), + NotCxx SWIFT_NAME("notcxx"), + Wasm SWIFT_NAME("wasm"), Last_ExposureKind = Wasm }; -enum : unsigned { NumExposureKindBits = - countBitsUsed(static_cast(ExposureKind::Last_ExposureKind)) }; - /// This enum represents the possible values of the @_extern attribute. -enum class ExternKind: uint8_t { +enum class ENUM_EXTENSIBILITY_ATTR(closed) ExternKind : uint8_t { /// Reference an externally defined C function. /// The imported function has C function pointer representation, /// and is called using the C calling convention. - C, + C SWIFT_NAME("c"), /// Reference an externally defined function through WebAssembly's /// import mechanism. /// This does not specify the calling convention and can be used /// with other extern kinds together. /// Effectively, this is no-op on non-WebAssembly targets. - Wasm, + Wasm SWIFT_NAME("wasm"), Last_ExternKind = Wasm }; -enum : unsigned { NumExternKindBits = - countBitsUsed(static_cast(ExternKind::Last_ExternKind)) }; - -enum class ExecutionKind : uint8_t { - Concurrent = 0, - Caller, - Last_ExecutionKind = Caller +enum class ENUM_EXTENSIBILITY_ATTR(closed) NonIsolatedModifier : uint8_t { + None SWIFT_NAME("none") = 0, + Unsafe SWIFT_NAME("unsafe"), + NonSending SWIFT_NAME("nonsending"), + Last_NonIsolatedModifier = NonSending }; -enum : unsigned { NumExecutionKindBits = - countBitsUsed(static_cast(ExecutionKind::Last_ExecutionKind)) }; +enum class ENUM_EXTENSIBILITY_ATTR(closed) + InheritActorContextModifier : uint8_t { + /// Inherit the actor execution context if the isolated parameter was + /// captured by the closure, context is nonisolated or isolated to a + /// global actor. + None SWIFT_NAME("none") = 0, + /// Always inherit the actor context, even when the isolated parameter + /// for the context is not closed over explicitly. + Always SWIFT_NAME("always"), + Last_InheritActorContextKind = Always + }; + +enum class ENUM_EXTENSIBILITY_ATTR(closed) NonexhaustiveMode : uint8_t { + Error SWIFT_NAME("error") = 0, + Warning SWIFT_NAME("warning") = 1, + Last_NonexhaustiveMode = Warning +}; -enum class DeclAttrKind : unsigned { +enum class ENUM_EXTENSIBILITY_ATTR(closed) DeclAttrKind : unsigned { #define DECL_ATTR(_, CLASS, ...) CLASS, #define LAST_DECL_ATTR(CLASS) Last_DeclAttr = CLASS, #include "swift/AST/DeclAttr.def" }; -StringRef getDeclAttrKindID(DeclAttrKind kind); +SWIFT_UNAVAILABLE("Unavailable in Swift") +llvm::StringRef getDeclAttrKindID(DeclAttrKind kind); enum : unsigned { - NumDeclAttrKinds = static_cast(DeclAttrKind::Last_DeclAttr) + 1, - NumDeclAttrKindBits = countBitsUsed(NumDeclAttrKinds - 1), + NumDeclAttrKinds = static_cast(DeclAttrKind::Last_DeclAttr) + 1 }; // Define enumerators for each type attribute, e.g. TypeAttrKind::Weak. -enum class TypeAttrKind { +enum class ENUM_EXTENSIBILITY_ATTR(closed) TypeAttrKind { #define TYPE_ATTR(_, CLASS) CLASS, #define LAST_TYPE_ATTR(CLASS) Last_TypeAttr = CLASS, #include "swift/AST/TypeAttr.def" }; enum : unsigned { - NumTypeAttrKinds = static_cast(TypeAttrKind::Last_TypeAttr) + 1, - NumTypeAttrKindBits = countBitsUsed(NumTypeAttrKinds - 1), + NumTypeAttrKinds = static_cast(TypeAttrKind::Last_TypeAttr) + 1 }; } // end namespace swift diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index d4d2574882b6c..28f24ffb6f528 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -422,7 +422,14 @@ class DerivativeFunctionTypeError Kind kind; /// The type and index of a differentiability parameter or result. - using TypeAndIndex = std::pair; + /// std::pair does not have a trivial copy constructor on FreeBSD for + /// ABI reasons, so we have to define our own type here instead + struct TypeAndIndex { + Type first; + unsigned second; + + TypeAndIndex(Type type, unsigned index) : first(type), second(index) {} + }; private: union Value { diff --git a/include/swift/AST/AvailabilityConstraint.h b/include/swift/AST/AvailabilityConstraint.h index ba9e70230dfc1..807078f729e90 100644 --- a/include/swift/AST/AvailabilityConstraint.h +++ b/include/swift/AST/AvailabilityConstraint.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Copyright (c) 2024 - 2025 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,9 +20,10 @@ #include "swift/AST/Attr.h" #include "swift/AST/AvailabilityDomain.h" #include "swift/AST/AvailabilityRange.h" -#include "swift/AST/PlatformKind.h" +#include "swift/AST/PlatformKindUtils.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" +#include "llvm/Support/raw_ostream.h" namespace swift { @@ -30,52 +31,96 @@ class ASTContext; class AvailabilityContext; class Decl; -/// Represents the reason a declaration could be considered unavailable in a -/// certain context. +/// Represents the reason a declaration is considered not available in a +/// specific `AvailabilityContext`. class AvailabilityConstraint { public: - /// The reason that the availability constraint is unsatisfied. + /// The reason that a declaration is not available in a context. Broadly, the + /// declaration may either be "unintroduced" or "unavailable" depending on its + /// `@available` attributes. A declaration that is unintroduced can become + /// available if availability constraints are added to the context. For + /// unavailable declarations, on the other hand, either changing the + /// deployment target or making the context itself unavailable are necessary + /// to satisfy the constraint. + /// + /// For example, take the following declaration `f()`: + /// + /// @available(macOS, introduced: 11.0, obsoleted: 14.0) + /// func f() { ... } + /// + /// In contexts that may run on earlier OSes, references to `f()` yield + /// an `Unintroduced` constraint: + /// + /// @available(macOS 10.15, *) + /// func g() { + /// f() // error: 'f()' is only available in macOS 11.0 or newer + /// } + /// + /// macOS ... 11.0 14.0 ... + /// f() |----------------[=======================)-----------------| + /// g() |-----------[==============================================) + /// ^ + /// Unintroduced + /// + /// On the other hand, in contexts where deployment target is high enough to + /// make `f()` obsolete, references to it yield an `UnavailableObsolete` + /// constraint: + /// + /// // compiled with -target arm64-apple-macos14 + /// func h() { + /// f() // error: 'f()' is unavailable in macOS + /// } + /// + /// macOS ... 11.0 14.0 ... + /// f() |----------------[=======================)-----------------| + /// h() |----------------------------------------[=================) + /// ^ + /// UnavailableObsolete + /// + /// References to declarations that are unavailable in all versions of a + /// domain generate `UnavailableUnconditional` constraints unless the context + /// is also unavailable under the same conditions: + /// + /// @available(macOS, unavailable) + /// func foo() { ... } + /// + /// func bar() { + /// foo() // error: 'foo()' is unavailable in macOS + /// } + /// + /// @available(macOS, unavailable) + /// func baz() { + /// foo() // OK + /// } + /// + /// @available(*, unavailable) + /// func qux() { + /// foo() // also OK + /// } /// /// NOTE: The order of this enum matters. Reasons are defined in descending /// priority order. enum class Reason { - /// The declaration is referenced in a context in which it is generally - /// unavailable. For example, a reference to a declaration that is - /// unavailable on macOS from a context that may execute on macOS has this - /// constraint. - UnconditionallyUnavailable, - - /// The declaration is referenced in a context in which it is considered - /// obsolete. For example, a reference to a declaration that is obsolete in - /// macOS 13 from a context that may execute on macOS 13 or later has this - /// constraint. - Obsoleted, - - /// The declaration is not available in the deployment configuration - /// specified for this compilation. For example, the declaration might only - /// be introduced in the Swift 6 language mode while the module is being - /// compiled in the Swift 5 language mode. These availability constraints - /// cannot be satisfied by adding constraining contextual availability using - /// `@available` attributes or `if #available` queries. - UnavailableForDeployment, - - /// The declaration is referenced in a context that does not have adequate - /// availability constraints. For example, a reference to a declaration that - /// was introduced in macOS 13 from a context that may execute on earlier - /// versions of macOS cannot satisfy this constraint. The constraint - /// can be satisfied, though, by introducing an `@available` attribute or an - /// `if #available(...)` query. - PotentiallyUnavailable, - }; + /// The declaration is unconditionally unavailable, e.g. because of + /// `@available(macOS, unavailable)`. + UnavailableUnconditionally, - /// Classifies constraints into different high level categories. - enum class Kind { - /// There are no contexts in which the declaration would be available. - Unavailable, + /// The declaration is obsolete, e.g. because of + /// `@available(macOS, obsolete: 14.0)` in a program with a deployment + /// target of `macOS 14` or later. + UnavailableObsolete, - /// There are some contexts in which the declaration would be available if - /// additional constraints were added. - PotentiallyAvailable, + /// The declaration is only available for later deployment configurations, + /// e.g. because of `@available(swift 6)` in a program compiled with + /// `-swift-version 5`. + UnavailableUnintroduced, + + /// The declaration has not yet been introduced, e.g. because of + /// `@available(macOS 14, *)` in a context that may run on macOS 13 or + /// later. The constraint may be satisfied adding an `@available` attribute + /// or an `if #available(...)` query with sufficient introduction + /// constraints to the context. + Unintroduced, }; private: @@ -86,22 +131,22 @@ class AvailabilityConstraint { public: static AvailabilityConstraint - unconditionallyUnavailable(SemanticAvailableAttr attr) { - return AvailabilityConstraint(Reason::UnconditionallyUnavailable, attr); + unavailableUnconditionally(SemanticAvailableAttr attr) { + return AvailabilityConstraint(Reason::UnavailableUnconditionally, attr); } - static AvailabilityConstraint obsoleted(SemanticAvailableAttr attr) { - return AvailabilityConstraint(Reason::Obsoleted, attr); + static AvailabilityConstraint + unavailableObsolete(SemanticAvailableAttr attr) { + return AvailabilityConstraint(Reason::UnavailableObsolete, attr); } static AvailabilityConstraint - unavailableForDeployment(SemanticAvailableAttr attr) { - return AvailabilityConstraint(Reason::UnavailableForDeployment, attr); + unavailableUnintroduced(SemanticAvailableAttr attr) { + return AvailabilityConstraint(Reason::UnavailableUnintroduced, attr); } - static AvailabilityConstraint - potentiallyUnavailable(SemanticAvailableAttr attr) { - return AvailabilityConstraint(Reason::PotentiallyUnavailable, attr); + static AvailabilityConstraint unintroduced(SemanticAvailableAttr attr) { + return AvailabilityConstraint(Reason::Unintroduced, attr); } Reason getReason() const { return attrAndReason.getInt(); } @@ -109,37 +154,32 @@ class AvailabilityConstraint { return static_cast(attrAndReason.getPointer()); } - Kind getKind() const { + /// Returns true if the constraint cannot be satisfied using a runtime + /// availability query (`if #available(...)`). + bool isUnavailable() const { switch (getReason()) { - case Reason::UnconditionallyUnavailable: - case Reason::Obsoleted: - case Reason::UnavailableForDeployment: - return Kind::Unavailable; - case Reason::PotentiallyUnavailable: - return Kind::PotentiallyAvailable; + case Reason::UnavailableUnconditionally: + case Reason::UnavailableObsolete: + case Reason::UnavailableUnintroduced: + return true; + case Reason::Unintroduced: + return false; } } - /// Returns true if the constraint cannot be satisfied at runtime. - bool isUnavailable() const { return getKind() == Kind::Unavailable; } - - /// Returns true if the constraint is unsatisfied but could be satisfied at - /// runtime in a more constrained context. - bool isPotentiallyAvailable() const { - return getKind() == Kind::PotentiallyAvailable; - } - /// Returns the domain that the constraint applies to. AvailabilityDomain getDomain() const { return getAttr().getDomain(); } - /// Returns the required range for `IntroducedInNewerVersion` requirements, or - /// `std::nullopt` otherwise. - std::optional - getPotentiallyUnavailableRange(const ASTContext &ctx) const; + /// Returns the domain and range (remapped if necessary) in which the + /// constraint must be satisfied. How the range should be interpreted depends + /// on the reason for the constraint. + AvailabilityDomainAndRange getDomainAndRange(const ASTContext &ctx) const; /// Some availability constraints are active for type-checking but cannot /// be translated directly into an `if #available(...)` runtime query. bool isActiveForRuntimeQueries(const ASTContext &ctx) const; + + void print(raw_ostream &os) const; }; /// Represents a set of availability constraints that restrict use of a @@ -161,6 +201,8 @@ class DeclAvailabilityConstraints { using const_iterator = Storage::const_iterator; const_iterator begin() const { return constraints.begin(); } const_iterator end() const { return constraints.end(); } + + void print(raw_ostream &os) const; }; enum class AvailabilityConstraintFlag : uint8_t { @@ -174,15 +216,55 @@ enum class AvailabilityConstraintFlag : uint8_t { /// Include constraints for all domains, regardless of whether they are active /// or relevant to type checking. IncludeAllDomains = 1 << 1, + + /// By default, non-type declarations that are universally unavailable are + /// always diagnosed, regardless of whether the context of the reference + /// is also universally unavailable. If this flag is set, though, those + /// references are allowed. + AllowUniversallyUnavailableInCompatibleContexts = 1 << 2, }; using AvailabilityConstraintFlags = OptionSet; -/// Returns the set of availability constraints that restrict use of \p decl +/// Returns the set of availability constraints that restricts use of \p decl /// when it is referenced from the given context. In other words, it is the -/// collection of of `@available` attributes with unsatisfied conditions. +/// collection of `@available` attributes with unsatisfied conditions. DeclAvailabilityConstraints getAvailabilityConstraintsForDecl( const Decl *decl, const AvailabilityContext &context, AvailabilityConstraintFlags flags = std::nullopt); + +/// Returns the availability constraints that restricts use of \p decl +/// in \p domain when it is referenced from the given context. In other words, +/// it is the unsatisfied `@available` attribute that applies to \p domain in +/// the given context. +std::optional getAvailabilityConstraintForDeclInDomain( + const Decl *decl, const AvailabilityContext &context, + AvailabilityDomain domain, + AvailabilityConstraintFlags flags = std::nullopt); + +/// Computes the set of constraints that indicate whether a decl is "runtime +/// unavailable" (can never be reached at runtime) and adds the domain for each +/// of those constraints to the \p domains vector. +void getRuntimeUnavailableDomains( + const DeclAvailabilityConstraints &constraints, + llvm::SmallVectorImpl &domains, const ASTContext &ctx); + } // end namespace swift +namespace llvm { + +inline llvm::raw_ostream & +operator<<(llvm::raw_ostream &os, + const swift::AvailabilityConstraint &constraint) { + constraint.print(os); + return os; +} + +inline llvm::raw_ostream & +operator<<(llvm::raw_ostream &os, + const swift::DeclAvailabilityConstraints &constraints) { + constraints.print(os); + return os; +} + +} // end namespace llvm #endif diff --git a/include/swift/AST/AvailabilityContext.h b/include/swift/AST/AvailabilityContext.h index 5bc3bfd3712ad..01b2cb3e1b6d5 100644 --- a/include/swift/AST/AvailabilityContext.h +++ b/include/swift/AST/AvailabilityContext.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Copyright (c) 2024 - 2025 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,9 +20,10 @@ #include "swift/AST/AvailabilityDomain.h" #include "swift/AST/AvailabilityRange.h" -#include "swift/AST/PlatformKind.h" +#include "swift/AST/PlatformKindUtils.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" +#include "llvm/Support/raw_ostream.h" #include namespace swift { @@ -75,6 +76,9 @@ class AvailabilityContext { /// of the given declaration. static AvailabilityContext forDeclSignature(const Decl *decl); + /// Returns the unconstrained availability context. + static AvailabilityContext forAlwaysAvailable(const ASTContext &ctx); + /// Returns the range of platform versions which may execute code in the /// availability context, starting at its introduction version. // FIXME: [availability] Remove; superseded by getAvailableRange(). @@ -145,4 +149,14 @@ class AvailabilityContext { } // end namespace swift +namespace llvm { + +inline llvm::raw_ostream & +operator<<(llvm::raw_ostream &os, const swift::AvailabilityContext &context) { + context.print(os); + return os; +} + +} // end namespace llvm + #endif diff --git a/include/swift/AST/AvailabilityContextStorage.h b/include/swift/AST/AvailabilityContextStorage.h index a110d8ccdce1d..beae4a6df6ba1 100644 --- a/include/swift/AST/AvailabilityContextStorage.h +++ b/include/swift/AST/AvailabilityContextStorage.h @@ -82,7 +82,7 @@ class AvailabilityContext::Storage final const ASTContext &ctx); llvm::ArrayRef getDomainInfos() const { - return llvm::ArrayRef(getTrailingObjects(), domainInfoCount); + return getTrailingObjects(domainInfoCount); } llvm::SmallVector copyDomainInfos() const { diff --git a/include/swift/AST/AvailabilityDomain.h b/include/swift/AST/AvailabilityDomain.h index 8aa9266115c25..2318ddd06461f 100644 --- a/include/swift/AST/AvailabilityDomain.h +++ b/include/swift/AST/AvailabilityDomain.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Copyright (c) 2024 - 2025 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,19 +21,23 @@ #include "swift/AST/ASTAllocated.h" #include "swift/AST/AvailabilityRange.h" #include "swift/AST/Identifier.h" -#include "swift/AST/PlatformKind.h" +#include "swift/AST/PlatformKindUtils.h" +#include "swift/AST/TypeAlignments.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/SourceLoc.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/ADT/PointerUnion.h" +#include "llvm/Support/raw_ostream.h" namespace swift { class ASTContext; class CustomAvailabilityDomain; class DeclContext; +class FuncDecl; class ModuleDecl; +class ValueDecl; /// Represents a dimension of availability (e.g. macOS platform or Swift /// language mode). @@ -45,7 +49,10 @@ class AvailabilityDomain final { Universal, /// Represents availability with respect to Swift language mode. - SwiftLanguage, + SwiftLanguageMode, + + /// Represents availability with respect to the Swift runtime. + SwiftRuntime, /// Represents PackageDescription availability. PackageDescription, @@ -117,9 +124,9 @@ class AvailabilityDomain final { AvailabilityDomain(Storage storage) : storage(storage) {}; std::optional getInlineDomain() const { - return storage.is() + return isa(storage) ? static_cast>( - storage.get()) + cast(storage)) : std::nullopt; } @@ -136,8 +143,12 @@ class AvailabilityDomain final { return AvailabilityDomain(platformKind); } - static AvailabilityDomain forSwiftLanguage() { - return AvailabilityDomain(Kind::SwiftLanguage); + static AvailabilityDomain forSwiftLanguageMode() { + return AvailabilityDomain(Kind::SwiftLanguageMode); + } + + static AvailabilityDomain forSwiftRuntime() { + return AvailabilityDomain(Kind::SwiftRuntime); } static AvailabilityDomain forPackageDescription() { @@ -148,6 +159,10 @@ class AvailabilityDomain final { return AvailabilityDomain(Kind::Embedded); } + /// If `decl` represents an availability domain, returns the corresponding + /// `AvailabilityDomain` value. Otherwise, returns `std::nullopt`. + static std::optional forCustom(ValueDecl *decl); + static AvailabilityDomain forCustom(const CustomAvailabilityDomain *domain) { return AvailabilityDomain(domain); } @@ -173,7 +188,11 @@ class AvailabilityDomain final { bool isPlatform() const { return getKind() == Kind::Platform; } - bool isSwiftLanguage() const { return getKind() == Kind::SwiftLanguage; } + bool isSwiftLanguageMode() const { + return getKind() == Kind::SwiftLanguageMode; + } + + bool isSwiftRuntime() const { return getKind() == Kind::SwiftRuntime; } bool isPackageDescription() const { return getKind() == Kind::PackageDescription; @@ -195,7 +214,7 @@ class AvailabilityDomain final { /// the domain. Returns `nullptr` otherwise. const CustomAvailabilityDomain *getCustomDomain() const { if (isCustom()) - return storage.get(); + return cast(storage); return nullptr; } @@ -203,6 +222,10 @@ class AvailabilityDomain final { /// version ranges. bool isVersioned() const; + /// Returns true if the given version is a valid version number for this + /// domain. It is an error to call this on an un-versioned domain. + bool isVersionValid(const llvm::VersionTuple &version) const; + /// Returns true if availability of the domain can be refined using /// `@available` attributes and `if #available` queries. If not, then the /// domain's availability is fixed by compilation settings. For example, @@ -215,11 +238,16 @@ class AvailabilityDomain final { /// Returns true if this domain is considered active in the current /// compilation context. - bool isActive(const ASTContext &ctx) const; + bool isActive(const ASTContext &ctx, bool forTargetVariant = false) const; /// Returns true if this domain is a platform domain and is considered active /// in the current compilation context. - bool isActivePlatform(const ASTContext &ctx) const; + bool isActivePlatform(const ASTContext &ctx, + bool forTargetVariant = false) const; + + /// Returns true if availability in this domain must be specified alone in + /// `@available` attributes and `if #available` queries. + bool mustBeSpecifiedAlone() const; /// Returns the domain's minimum available range for type checking. For /// example, for the domain of the platform that compilation is targeting, @@ -236,6 +264,10 @@ class AvailabilityDomain final { /// Returns the string to use when printing an `@available` attribute. llvm::StringRef getNameForAttributePrinting() const; + /// Returns the decl that represents the domain, or `nullptr` if the domain + /// does not have a decl. + ValueDecl *getDecl() const; + /// Returns the module that the domain belongs to, if it is a custom domain. ModuleDecl *getModule() const; @@ -244,6 +276,12 @@ class AvailabilityDomain final { /// universal domain (`*`) is the bottom element. bool contains(const AvailabilityDomain &other) const; + /// Returns true if availability in `other` is a subset of availability in + /// this domain or vice-versa. + bool isRelated(const AvailabilityDomain &other) const { + return contains(other) || other.contains(*this); + } + /// Returns true for domains that are not contained by any domain other than /// the universal domain. bool isRoot() const; @@ -253,6 +291,26 @@ class AvailabilityDomain final { /// descendants of the iOS domain. AvailabilityDomain getRootDomain() const; + /// Returns the canonical domain that versions in this domain must be remapped + /// to before making availability comparisons in the current compilation + /// context. Sets \p didRemap to `true` if a remap was required. + const AvailabilityDomain getRemappedDomain(const ASTContext &ctx, + bool &didRemap) const; + + /// Returns the canonical domain that versions in this domain must be remapped + /// to before making availability comparisons in the current compilation + /// context. + const AvailabilityDomain getRemappedDomain(const ASTContext &ctx) const { + bool unused; + return getRemappedDomain(ctx, unused); + } + + /// Returns true for a domain that is permanently always available, and + /// therefore availability constraints in the domain are effectively the same + /// as constraints in the `*` domain. This is used to diagnose unnecessary + /// `@available` attributes and `if #available` statements. + bool isPermanentlyAlwaysEnabled() const; + bool operator==(const AvailabilityDomain &other) const { return storage.getOpaqueValue() == other.storage.getOpaqueValue(); } @@ -292,11 +350,15 @@ struct StableAvailabilityDomainComparator { }; /// Represents an availability domain that has been defined in a module. -class CustomAvailabilityDomain : public llvm::FoldingSetNode { +class alignas(1 << CustomAvailabilityDomainAlignInBits) CustomAvailabilityDomain + : public llvm::FoldingSetNode { public: - enum class Kind { + enum class Kind : uint8_t { /// A domain that is known to be enabled at compile time. Enabled, + /// A domain that is known to be enabled at compile time and is also assumed + /// to be enabled for all deployments. + AlwaysEnabled, /// A domain that is known to be disabled at compile time. Disabled, /// A domain with an enablement state that must be queried at runtime. @@ -305,28 +367,52 @@ class CustomAvailabilityDomain : public llvm::FoldingSetNode { private: Identifier name; - Kind kind; ModuleDecl *mod; + ValueDecl *decl; + FuncDecl *predicateFunc; + Kind kind; + + struct { + /// Whether the "isPermanentlyEnabled" bit has been computed yet. + unsigned isPermanentlyEnabledComputed : 1; + /// Whether the domain is permanently enabled, which makes constraints in + /// the domain equivalent to those in the `*` domain. + unsigned isPermanentlyEnabled : 1; + } flags = {}; + + friend class IsCustomAvailabilityDomainPermanentlyEnabled; - CustomAvailabilityDomain(Identifier name, ModuleDecl *mod, Kind kind); + CustomAvailabilityDomain(Identifier name, Kind kind, ModuleDecl *mod, + ValueDecl *decl, FuncDecl *predicateFunc); public: - static const CustomAvailabilityDomain *get(StringRef name, ModuleDecl *mod, - Kind kind, const ASTContext &ctx); + static const CustomAvailabilityDomain *get(StringRef name, Kind kind, + ModuleDecl *mod, ValueDecl *decl, + FuncDecl *predicateFunc, + const ASTContext &ctx); Identifier getName() const { return name; } Kind getKind() const { return kind; } ModuleDecl *getModule() const { return mod; } + ValueDecl *getDecl() const { return decl; } + + /// Returns the function that should be called at runtime to determine whether + /// the domain is available, or `nullptr` if the domain's availability is not + /// determined at runtime. + FuncDecl *getPredicateFunc() const { return predicateFunc; } /// Uniquing for `ASTContext`. static void Profile(llvm::FoldingSetNodeID &ID, Identifier name, - ModuleDecl *mod, Kind kind); + ModuleDecl *mod); - void Profile(llvm::FoldingSetNodeID &ID) const { - Profile(ID, name, mod, kind); - } + void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, name, mod); } }; +inline void simple_display(llvm::raw_ostream &os, + const CustomAvailabilityDomain *domain) { + os << domain->getName(); +} + /// Represents either a resolved availability domain or an identifier written /// in source that has not yet been resolved to a domain. class AvailabilityDomainOrIdentifier { @@ -356,9 +442,9 @@ class AvailabilityDomainOrIdentifier { : storage(domain) {}; bool isDomain() const { - return storage.getPointer().is(); + return isa(storage.getPointer()); } - bool isIdentifier() const { return storage.getPointer().is(); } + bool isIdentifier() const { return isa(storage.getPointer()); } /// Overwrites the existing domain or identifier with the given domain. void setDomain(AvailabilityDomain domain) { storage = Storage(domain); } @@ -366,7 +452,7 @@ class AvailabilityDomainOrIdentifier { /// Returns the resolved domain, or `std::nullopt` if there isn't one. std::optional getAsDomain() const { if (isDomain()) - return storage.getPointer().get(); + return cast(storage.getPointer()); return std::nullopt; } @@ -374,7 +460,7 @@ class AvailabilityDomainOrIdentifier { /// been resolved. std::optional getAsIdentifier() const { if (isIdentifier()) - return storage.getPointer().get(); + return cast(storage.getPointer()); return std::nullopt; } @@ -408,6 +494,20 @@ class AvailabilityDomainOrIdentifier { void print(llvm::raw_ostream &os) const; }; +/// Represents an `AvailabilityRange` paired with the `AvailabilityDomain` that +/// the range applies to. +class AvailabilityDomainAndRange { + AvailabilityDomain domain; + AvailabilityRange range; + +public: + AvailabilityDomainAndRange(AvailabilityDomain domain, AvailabilityRange range) + : domain(domain), range(range) {}; + + AvailabilityDomain getDomain() const { return domain; } + AvailabilityRange getRange() const { return range; } +}; + } // end namespace swift namespace llvm { @@ -469,6 +569,12 @@ struct PointerLikeTypeTraits { }; }; +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const swift::AvailabilityDomain &domain) { + domain.print(os); + return os; +} + } // end namespace llvm #endif diff --git a/include/swift/AST/AvailabilityInference.h b/include/swift/AST/AvailabilityInference.h index cfc9d871476c4..efb9fd7290fa2 100644 --- a/include/swift/AST/AvailabilityInference.h +++ b/include/swift/AST/AvailabilityInference.h @@ -31,10 +31,6 @@ class SemanticAvailableAttr; class AvailabilityInference { public: - /// Returns the decl that should be considered the parent decl of the given - /// decl when looking for inherited availability annotations. - static const Decl *parentDeclForInferredAvailability(const Decl *D); - /// Infers the common availability required to access an array of /// declarations and adds attributes reflecting that availability /// to ToDecl. @@ -47,9 +43,6 @@ class AvailabilityInference { /// Returns the range of platform versions in which the decl is available. static AvailabilityRange availableRange(const Decl *D); - /// Returns true is the declaration is `@_spi_available`. - static bool isAvailableAsSPI(const Decl *D); - /// Returns the context for which the declaration /// is annotated as available, or None if the declaration /// has no availability annotation. @@ -57,7 +50,7 @@ class AvailabilityInference { annotatedAvailableRange(const Decl *D); static AvailabilityRange - annotatedAvailableRangeForAttr(const Decl *D, const SpecializeAttr *attr, + annotatedAvailableRangeForAttr(const Decl *D, const AbstractSpecializeAttr *attr, ASTContext &ctx); /// For the attribute's introduction version, update the platform and version @@ -81,11 +74,6 @@ class AvailabilityInference { const SemanticAvailableAttr &attr, const ASTContext &ctx, AvailabilityDomain &domain, llvm::VersionTuple &platformVer); - static void - updateAvailabilityDomainForFallback(const SemanticAvailableAttr &attr, - const ASTContext &ctx, - AvailabilityDomain &domain); - /// For the attribute's before version, update the platform and version /// values to the re-mapped platform's, if using a fallback platform. /// Returns `true` if a remap occured. diff --git a/include/swift/AST/AvailabilityQuery.h b/include/swift/AST/AvailabilityQuery.h new file mode 100644 index 0000000000000..ca9f8af38e753 --- /dev/null +++ b/include/swift/AST/AvailabilityQuery.h @@ -0,0 +1,158 @@ +//===--- AvailabilityQuery.h - Swift Availability Query ASTs ----*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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 availability query AST classes. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_AVAILABILITY_QUERY_H +#define SWIFT_AST_AVAILABILITY_QUERY_H + +#include "swift/AST/AvailabilityDomain.h" +#include "swift/AST/AvailabilityRange.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/VersionTuple.h" + +namespace swift { +class ASTContext; +class FuncDecl; + +/// Represents the information needed to evaluate an `#if available` query +/// (either at runtime or compile-time). +class AvailabilityQuery final { + AvailabilityDomain domain; + std::optional primaryRange; + std::optional variantRange; + + enum class ResultKind : uint8_t { + /// The result of the query is true at compile-time. + ConstTrue = 0, + /// The result of the query is false at compile-time. + ConstFalse = 1, + /// The result of the query must be determined at runtime. + Dynamic = 2, + }; + ResultKind kind; + + bool unavailable; + + AvailabilityQuery(AvailabilityDomain domain, ResultKind kind, + const std::optional &primaryRange, + const std::optional &variantRange) + : domain(domain), primaryRange(primaryRange), variantRange(variantRange), + kind(kind), unavailable(false) {}; + +public: + /// Returns an `AvailabilityQuery` for a query that evaluates to true or + /// false at compile-time. + static AvailabilityQuery constant(AvailabilityDomain domain, bool value) { + return AvailabilityQuery( + domain, value ? ResultKind::ConstTrue : ResultKind::ConstFalse, + std::nullopt, std::nullopt); + } + + /// Returns an `AvailabilityQuery` for a query that evaluates to true or + /// false at compile-time in the universal availability domain. + static AvailabilityQuery universallyConstant(bool value) { + return AvailabilityQuery(AvailabilityDomain::forUniversal(), + value ? ResultKind::ConstTrue + : ResultKind::ConstFalse, + std::nullopt, std::nullopt); + } + + /// Returns an `AvailabilityQuery` for a query that must be evaluated at + /// runtime with the given arguments, which may be zero, one, or two version + /// tuples that should be passed to the query function. + static AvailabilityQuery + dynamic(AvailabilityDomain domain, + const std::optional &primaryRange, + const std::optional &variantRange) { + return AvailabilityQuery(domain, ResultKind::Dynamic, primaryRange, + variantRange); + } + + /// Returns a copy of the `AvailabilityQuery` that has been modified to + /// represent an `if #unavailable` query if `isUnavailability` is true, or an + /// `if #available` query otherwise. + AvailabilityQuery asUnavailable(bool isUnavailability) const { + if (isUnavailability != unavailable) { + AvailabilityQuery copy = *this; + copy.unavailable = isUnavailability; + return copy; + } + return *this; + } + + /// Returns the domain that the query applies to. + AvailabilityDomain getDomain() const { return domain; } + + /// Returns true if the query's result is determined at compile-time. + bool isConstant() const { return kind != ResultKind::Dynamic; } + + /// Returns true if the query was spelled `#unavailable`. + bool isUnavailability() const { return unavailable; } + + /// Returns the boolean result of the query if it is known at compile-time, or + /// `std::nullopt` otherwise. The returned value accounts for whether the + /// query was spelled `#unavailable`. + std::optional getConstantResult() const { + switch (kind) { + case ResultKind::ConstTrue: + return !unavailable; + case ResultKind::ConstFalse: + return unavailable; + case ResultKind::Dynamic: + return std::nullopt; + } + } + + /// Returns the availability range that is the first argument to query + /// function. + std::optional getPrimaryRange() const { + return primaryRange; + } + + /// Returns the version tuple that is the first argument to query function. + std::optional getPrimaryArgument() const { + if (!primaryRange) + return std::nullopt; + return primaryRange->getRawMinimumVersion(); + } + + /// Returns the availability range that is the second argument to query + /// function. This represents the `-target-variant` version when compiling a + /// zippered library. + std::optional getVariantRange() const { + return variantRange; + } + + /// Returns the version tuple that is the second argument to query function. + /// This represents the `-target-variant` version when compiling a zippered + /// library. + std::optional getVariantArgument() const { + if (!variantRange) + return std::nullopt; + return variantRange->getRawMinimumVersion(); + } + + /// Returns the `FuncDecl *` that should be invoked at runtime to evaluate + /// the query, and populates `arguments` with the arguments to invoke it with + /// (the integer components of the version tuples that are being tested). If + /// the query does not have a dynamic result, returns `nullptr`. + FuncDecl * + getDynamicQueryDeclAndArguments(llvm::SmallVectorImpl &arguments, + ASTContext &ctx) const; +}; + +} // end namespace swift + +#endif diff --git a/include/swift/AST/AvailabilityRange.h b/include/swift/AST/AvailabilityRange.h index c679ea0b01db8..5580b6bae2c57 100644 --- a/include/swift/AST/AvailabilityRange.h +++ b/include/swift/AST/AvailabilityRange.h @@ -21,7 +21,7 @@ #include "swift/Basic/LLVM.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/Support/VersionTuple.h" -#include +#include "llvm/Support/raw_ostream.h" namespace swift { class ASTContext; @@ -317,11 +317,20 @@ class AvailabilityRange { /// Returns a representation of this range as a string for debugging purposes. std::string getAsString() const { - return "AvailabilityRange(" + getVersionString() + ")"; + return "AvailabilityRange(" + getVersionDescription() + ")"; } - /// Returns a representation of the raw version range as a string for + /// Returns a representation of the version range as a string for /// debugging purposes. + std::string getVersionDescription() const { + if (Range.hasLowerEndpoint()) + return Range.getLowerEndpoint().getAsString(); + else + return Range.isEmpty() ? "never" : "always"; + } + + /// Returns a string representation of the raw version range. It is an error + /// to call this if the range is "always" or "never". std::string getVersionString() const { ASSERT(Range.hasLowerEndpoint()); return Range.getLowerEndpoint().getAsString(); @@ -330,4 +339,20 @@ class AvailabilityRange { } // end namespace swift +namespace llvm { + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const swift::VersionRange &range) { + os << range.getAsString(); + return os; +} + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const swift::AvailabilityRange &range) { + os << range.getAsString(); + return os; +} + +} // namespace llvm + #endif diff --git a/include/swift/AST/AvailabilityScope.h b/include/swift/AST/AvailabilityScope.h index 210d8289083f6..e56d1260a727f 100644 --- a/include/swift/AST/AvailabilityScope.h +++ b/include/swift/AST/AvailabilityScope.h @@ -272,8 +272,11 @@ class AvailabilityScope : public ASTAllocated { AvailabilityDomain Domain, const llvm::VersionTuple &Version) const; /// Returns the availability version range that was explicitly written in - /// source, if applicable. Otherwise, returns null. - std::optional getExplicitAvailabilityRange() const; + /// source for the given domain, if applicable. Otherwise, returns + /// `std::nullopt`. + std::optional + getExplicitAvailabilityRange(AvailabilityDomain Domain, + ASTContext &Ctx) const; /// Returns the source range this scope represents. SourceRange getSourceRange() const { return SrcRange; } diff --git a/include/swift/AST/AvailabilitySpec.h b/include/swift/AST/AvailabilitySpec.h index b07217a0fa94d..dbb951f2b15f3 100644 --- a/include/swift/AST/AvailabilitySpec.h +++ b/include/swift/AST/AvailabilitySpec.h @@ -1,8 +1,8 @@ -//===--- AvailabilitySpec.h - Swift Availability Query ASTs -----*- C++ -*-===// +//===--- AvailabilitySpec.h - Swift Availability Spec ASTs ------*- C++ -*-===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2025 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,13 +20,14 @@ #include "swift/AST/ASTAllocated.h" #include "swift/AST/AvailabilityDomain.h" #include "swift/AST/Identifier.h" -#include "swift/AST/PlatformKind.h" +#include "swift/AST/PlatformKindUtils.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceLoc.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/VersionTuple.h" +#include "llvm/Support/raw_ostream.h" namespace swift { class ASTContext; @@ -241,4 +242,20 @@ class AvailabilityMacroMap { } // end namespace swift +namespace llvm { + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const swift::AvailabilitySpec *spec) { + spec->print(os); + return os; +} + +inline llvm::raw_ostream & +operator<<(llvm::raw_ostream &os, const swift::SemanticAvailabilitySpec &spec) { + spec.print(os); + return os; +} + +} // end namespace llvm + #endif diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 1d4ad98881ceb..b39972c7be964 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -240,6 +240,7 @@ BUILTIN_SIL_OPERATION(LoadRaw, "loadRaw", Special) BUILTIN_SIL_OPERATION(LoadInvariant, "loadInvariant", Special) /// Take has type (Builtin.RawPointer) -> T +/// where T: ~Copyable & ~Escapable BUILTIN_SIL_OPERATION(Take, "take", Special) /// Destroy has type (T.Type, Builtin.RawPointer) -> () @@ -673,9 +674,22 @@ BUILTIN_MISC_OPERATION(ExtractElement, "extractelement", "n", Special) /// InsertElement has type (Vector, T, Int32) -> Vector. BUILTIN_MISC_OPERATION(InsertElement, "insertelement", "n", Special) -// Shufflevector has type (VecN, VecN, VecM) -> VecM +/// Select has type either +/// (VecN, VecN, VecN) -> VecN +/// or +/// (Int1, T, T) -> T +/// T must be trivial. +BUILTIN_MISC_OPERATION(Select, "select", "n", Special) + +/// Shufflevector has type (VecN, VecN, VecM) -> VecM BUILTIN_MISC_OPERATION(ShuffleVector, "shufflevector", "n", Special) +/// Interleave has type (VecN, VecN) -> (VecN, VecN) +BUILTIN_MISC_OPERATION(Interleave, "interleave", "n", Special) + +/// Deinterleave has type (VecN, VecN) -> (VecN, VecN) +BUILTIN_MISC_OPERATION(Deinterleave, "deinterleave", "n", Special) + /// StaticReport has type (Builtin.Int1, Builtin.Int1, Builtin.RawPointer) -> () BUILTIN_MISC_OPERATION(StaticReport, "staticReport", "", Special) @@ -844,6 +858,10 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(Alignof, "alignof", "n", Special) /// own rules. BUILTIN_MISC_OPERATION_WITH_SILGEN(ZeroInitializer, "zeroInitializer", "n", Special) +/// Like `zeroInitializer`, but does not actually initialize the memory. +/// It only indicates to mandatory passes that the memory is going to be initialized. +BUILTIN_MISC_OPERATION(PrepareInitialization, "prepareInitialization", "n", Special) + // getCurrentExecutor: () async -> Builtin.Executor? // // Retrieve the SerialExecutorRef on which the current asynchronous diff --git a/include/swift/AST/Builtins.h b/include/swift/AST/Builtins.h index 799fd3514feec..213a61ff50a70 100644 --- a/include/swift/AST/Builtins.h +++ b/include/swift/AST/Builtins.h @@ -121,12 +121,13 @@ class BuiltinInfo { /// The information identifying the llvm intrinsic - its id and types. class IntrinsicInfo { - mutable llvm::AttributeList Attrs = - llvm::DenseMapInfo::getEmptyKey(); + mutable llvm::AttributeSet FnAttrs = + llvm::DenseMapInfo::getEmptyKey(); + public: llvm::Intrinsic::ID ID; SmallVector Types; - const llvm::AttributeList &getOrCreateAttributes(ASTContext &Ctx) const; + const llvm::AttributeSet &getOrCreateFnAttributes(ASTContext &Ctx) const; }; /// Turn a string like "release" into the LLVM enum. diff --git a/include/swift/AST/CanTypeVisitor.h b/include/swift/AST/CanTypeVisitor.h index 658f01488ecbf..d0b163630973b 100644 --- a/include/swift/AST/CanTypeVisitor.h +++ b/include/swift/AST/CanTypeVisitor.h @@ -70,6 +70,70 @@ class CanTypeVisitor { #include "swift/AST/TypeNodes.def" }; +/// This is a convenience refinement of CanTypeVisitor which forwards the +/// paired nominal type methods to a common implementation. +/// +/// The delegation flow is: +/// {ClassType, BoundGenericClassType} -> AnyClassType -> AnyNominalType -> Type +/// {EnumType, BoundGenericEnumType} -> AnyEnumType -> AnyNominalType -> Type +/// {StructType, BoundGenericStructType} -> AnyStructType -> AnyNominalType -> Type +/// ProtocolType -> AnyNominalType -> Type +/// +/// The new visitAny*Type methods take the appropriate Decl* as their second +/// argument. +template +class CanTypeVisitor_AnyNominal : public CanTypeVisitor { +public: + RetTy visitClassType(CanClassType T, Args... args) { + return static_cast(this) + ->visitAnyClassType(T, T->getDecl(), ::std::forward(args)...); + } + RetTy visitBoundGenericClassType(CanBoundGenericClassType T, Args... args) { + return static_cast(this) + ->visitAnyClassType(T, T->getDecl(), ::std::forward(args)...); + } + RetTy visitAnyClassType(CanType T, ClassDecl *D, Args... args) { + return static_cast(this) + ->visitAnyNominalType(T, D, ::std::forward(args)...); + } + + RetTy visitStructType(CanStructType T, Args... args) { + return static_cast(this) + ->visitAnyStructType(T, T->getDecl(), ::std::forward(args)...); + } + RetTy visitBoundGenericStructType(CanBoundGenericStructType T, Args... args) { + return static_cast(this) + ->visitAnyStructType(T, T->getDecl(), ::std::forward(args)...); + } + RetTy visitAnyStructType(CanType T, StructDecl *D, Args... args) { + return static_cast(this) + ->visitAnyNominalType(T, D, ::std::forward(args)...); + } + + RetTy visitEnumType(CanEnumType T, Args... args) { + return static_cast(this) + ->visitAnyEnumType(T, T->getDecl(), ::std::forward(args)...); + } + RetTy visitBoundGenericEnumType(CanBoundGenericEnumType T, Args... args) { + return static_cast(this) + ->visitAnyEnumType(T, T->getDecl(), ::std::forward(args)...); + } + RetTy visitAnyEnumType(CanType T, EnumDecl *D, Args... args) { + return static_cast(this) + ->visitAnyNominalType(T, D, ::std::forward(args)...); + } + + RetTy visitProtocolType(CanProtocolType T, Args... args) { + return static_cast(this) + ->visitAnyNominalType(T, T->getDecl(), ::std::forward(args)...); + } + + RetTy visitAnyNominalType(CanType T, NominalTypeDecl *D, Args... args) { + return static_cast(this) + ->visitType(T, ::std::forward(args)...); + } +}; + } // end namespace swift #endif diff --git a/include/swift/AST/CatchNode.h b/include/swift/AST/CatchNode.h index 67525f13acbeb..f7e05b3513f79 100644 --- a/include/swift/AST/CatchNode.h +++ b/include/swift/AST/CatchNode.h @@ -22,12 +22,16 @@ namespace swift { +namespace detail { +using CatchNodeBase = llvm::PointerUnion; +} // end namespace detail + /// An AST node that represents a point where a thrown error can be caught and /// or rethrown, which includes functions do...catch statements. -class CatchNode: public llvm::PointerUnion< - AbstractFunctionDecl *, ClosureExpr *, DoCatchStmt *, AnyTryExpr * - > { +class CatchNode : public detail::CatchNodeBase { public: + using Base = detail::CatchNodeBase; using PointerUnion::PointerUnion; /// Determine the thrown error type within the region of this catch node @@ -62,4 +66,17 @@ SourceLoc extractNearestSourceLoc(CatchNode catchNode); } // end namespace swift +namespace llvm { + +using swift::CatchNode; + +/// `isa`, `dyn_cast`, `cast` for `CatchNode`. +template +struct CastInfo : public CastInfo {}; +template +struct CastInfo + : public CastInfo {}; + +} // end namespace llvm + #endif // SWIFT_AST_CATCHNODE_H diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index 8213540c0a595..8175e797337af 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -216,6 +216,9 @@ class ClangModuleLoader : public ModuleLoader { DeclContext *newContext, ClangInheritanceInfo inheritance) = 0; + /// Returnes the original method if \param decl is a clone from a base class + virtual ValueDecl *getOriginalForClonedMember(const ValueDecl *decl) = 0; + /// Emits diagnostics for any declarations named name /// whose direct declaration context is a TU. virtual void diagnoseTopLevelValue(const DeclName &name) = 0; @@ -295,6 +298,9 @@ class ClangModuleLoader : public ModuleLoader { virtual FuncDecl *getDefaultArgGenerator(const clang::ParmVarDecl *param) = 0; + virtual FuncDecl * + getAvailabilityDomainPredicate(const clang::VarDecl *var) = 0; + virtual std::optional importFunctionReturnType(const clang::FunctionDecl *clangDecl, DeclContext *dc) = 0; diff --git a/include/swift/AST/ClangNode.h b/include/swift/AST/ClangNode.h index 9891d1915dc8b..062cd51d341ba 100644 --- a/include/swift/AST/ClangNode.h +++ b/include/swift/AST/ClangNode.h @@ -14,6 +14,7 @@ #define SWIFT_CLANGNODE_H #include "swift/Basic/Debug.h" +#include "swift/Basic/LLVM.h" #include "llvm/ADT/PointerUnion.h" namespace clang { @@ -76,16 +77,16 @@ class ClangNode { } const clang::Decl *castAsDecl() const { - return Ptr.get>().value; + return cast>(Ptr).value; } const clang::MacroInfo *castAsMacroInfo() const { - return Ptr.get>().value; + return cast>(Ptr).value; } const clang::ModuleMacro *castAsModuleMacro() const { - return Ptr.get>().value; + return cast>(Ptr).value; } const clang::Module *castAsModule() const { - return Ptr.get>().value; + return cast>(Ptr).value; } // Get the MacroInfo for a local definition, one imported from a diff --git a/include/swift/AST/Concurrency.h b/include/swift/AST/Concurrency.h index 3d19fa40bbe80..c5e0de233c4b4 100644 --- a/include/swift/AST/Concurrency.h +++ b/include/swift/AST/Concurrency.h @@ -19,6 +19,11 @@ namespace swift { +/// Find the imported module that treats the given nominal type as "preconcurrency", or return `nullptr` +/// if there is no such module. +ModuleDecl *moduleImportForPreconcurrency(NominalTypeDecl *nominal, + const DeclContext *fromDC); + /// Determinate the appropriate diagnostic behavior to used when emitting /// concurrency diagnostics when referencing the given nominal type from the /// given declaration context. diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 5a5d40dc56920..04767c21d76e9 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -19,6 +19,7 @@ #include "swift/AST/AccessScope.h" #include "swift/AST/Attr.h" +#include "swift/AST/AvailabilityQuery.h" #include "swift/AST/AvailabilityRange.h" #include "swift/AST/CaptureInfo.h" #include "swift/AST/ClangNode.h" @@ -169,6 +170,7 @@ enum class DescriptiveDeclKind : uint8_t { PostfixOperator, PrecedenceGroup, TypeAlias, + GenericTypeAlias, GenericTypeParam, AssociatedType, Type, @@ -183,7 +185,6 @@ enum class DescriptiveDeclKind : uint8_t { GenericClass, GenericActor, GenericDistributedActor, - GenericType, Subscript, StaticSubscript, ClassSubscript, @@ -213,7 +214,10 @@ enum class DescriptiveDeclKind : uint8_t { OpaqueResultType, OpaqueVarType, Macro, - MacroExpansion + MacroExpansion, + Using, + BorrowAccessor, + MutateAccessor, }; /// Describes which spelling was used in the source for the 'static' or 'class' @@ -267,6 +271,16 @@ static_assert(uint8_t(SelfAccessKind::LastSelfAccessKind) < "Self Access Kind is too small to fit in SelfAccess kind bits. " "Please expand "); +enum class UsingSpecifier : uint8_t { + MainActor, + Nonisolated, + LastSpecifier = Nonisolated, +}; +enum : unsigned { + NumUsingSpecifierBits = + countBitsUsed(static_cast(UsingSpecifier::LastSpecifier)) +}; + /// Diagnostic printing of \c SelfAccessKind. llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, SelfAccessKind SAK); @@ -310,6 +324,9 @@ struct OverloadSignature { /// Whether this is a macro. unsigned IsMacro : 1; + /// Whether this is a generic argument. + unsigned IsGenericArg : 1; + /// Whether this signature is part of a protocol extension. unsigned InProtocolExtension : 1; @@ -323,8 +340,10 @@ struct OverloadSignature { OverloadSignature() : UnaryOperator(UnaryOperatorKind::None), IsInstanceMember(false), IsVariable(false), IsFunction(false), IsAsyncFunction(false), - IsDistributed(false), InProtocolExtension(false), - InExtensionOfGenericType(false), HasOpaqueReturnType(false) { } + IsDistributed(false), IsEnumElement(false), IsNominal(false), + IsTypeAlias(false), IsMacro(false), IsGenericArg(false), + InProtocolExtension(false), InExtensionOfGenericType(false), + HasOpaqueReturnType(false) { } }; /// Determine whether two overload signatures conflict. @@ -806,8 +825,8 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi /// Whether this module has enabled strict memory safety checking. StrictMemorySafety : 1, - /// Whether this module has enabled `ExtensibleEnums` feature. - ExtensibleEnums : 1 + /// Whether this module uses deferred code generation in Embedded Swift. + DeferredCodeGen : 1 ); SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2, @@ -825,6 +844,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi NumPathElements : 8 ); + SWIFT_INLINE_BITFIELD(UsingDecl, Decl, NumUsingSpecifierBits, + Specifier : NumUsingSpecifierBits + ); + SWIFT_INLINE_BITFIELD(ExtensionDecl, Decl, 4+1, /// An encoding of the default and maximum access level for this extension. /// The value 4 corresponds to AccessLevel::Public @@ -1004,7 +1027,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi if (auto dc = Context.dyn_cast()) return dc->getASTContext(); - return *Context.get(); + return *cast(Context); } const DeclAttributes &getAttrs() const { @@ -1050,6 +1073,14 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi /// behaviors for it and, if it's an extension, its members. bool isObjCImplementation() const; + /// True if this declaration should never have its implementation made + /// available to any client. This overrides cross-module optimization and + /// optimizations that might use the implementation, such that the only + /// implementation of this function is the one compiled into its owning + /// module. Practically speaking, this prohibits serialization of the SIL + /// for this definition. + bool isNeverEmittedIntoClient() const; + using AuxiliaryDeclCallback = llvm::function_ref; /// Iterate over the auxiliary declarations for this declaration, @@ -1098,15 +1129,14 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi std::optional getIntroducedOSVersion(PlatformKind Kind) const; - /// Returns the OS version in which the decl became ABI as specified by the - /// `@backDeployed` attribute. - std::optional - getBackDeployedBeforeOSVersion(ASTContext &Ctx, - bool forTargetVariant = false) const; + /// Returns the active `@backDeployed` attribute and the `AvailabilityRange` + /// in which the decl is available as ABI. + std::optional> + getBackDeployedAttrAndRange(ASTContext &Ctx, + bool forTargetVariant = false) const; - /// Returns true if the decl has an active `@backDeployed` attribute for the - /// given context. - bool isBackDeployed(ASTContext &Ctx) const; + /// Returns true if the decl has a valid and active `@backDeployed` attribute. + bool isBackDeployed() const; /// Returns the starting location of the entire declaration. SourceLoc getStartLoc() const { return getSourceRange().Start; } @@ -1149,6 +1179,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi /// constructed from a serialized module. bool isInMacroExpansionInContext() const; + /// Whether this declaration is within a macro expansion relative to + /// its decl context, and the macro was attached to a node imported from clang. + bool isInMacroExpansionFromClangHeader() const; + /// Returns the appropriate kind of entry point to generate for this class, /// based on its attributes. /// @@ -1443,6 +1477,27 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi std::optional getAvailableAttrForPlatformIntroduction(bool checkExtension = true) const; + /// Returns true if `decl` has any active `@available` attribute attached to + /// it. + bool hasAnyActiveAvailableAttr() const { + return hasAnyMatchingActiveAvailableAttr( + [](SemanticAvailableAttr attr) -> bool { return true; }); + } + + /// Returns true if `predicate` returns true for any active availability + /// attribute attached to `decl`. The predicate function should accept a + /// `SemanticAvailableAttr`. + template + bool hasAnyMatchingActiveAvailableAttr(F predicate) const { + auto &ctx = getASTContext(); + auto decl = getAbstractSyntaxDeclForAttributes(); + for (auto attr : decl->getSemanticAvailableAttrs()) { + if (attr.isActive(ctx) && predicate(attr)) + return true; + } + return false; + } + /// Returns true if the declaration is deprecated at the current deployment /// target. bool isDeprecated() const { return getDeprecatedAttr().has_value(); } @@ -1500,6 +1555,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi /// compatibility mode. bool requiresUnavailableDeclABICompatibilityStubs() const; + /// Returns the decl that should be considered the parent decl when looking + /// for inherited availability annotations. + const Decl *parentDeclForAvailability() const; + // List the SPI groups declared with @_spi or inherited by this decl. // // SPI groups are inherited from the parent contexts only if the local decl @@ -1718,8 +1777,8 @@ class ImportDecl final : public Decl, /// path will include 'Foo'. This return value is always owned by \c ImportDecl /// (which is owned by the AST context), so it can be persisted. ImportPath getImportPath() const { - return ImportPath({ getTrailingObjects(), - static_cast(Bits.ImportDecl.NumPathElements) }); + return ImportPath(getTrailingObjects( + static_cast(Bits.ImportDecl.NumPathElements))); } /// Retrieves the import path, replacing any module aliases with real names. @@ -1996,6 +2055,7 @@ class ExtensionDecl final : public GenericContext, public Decl, std::pair takeConformanceLoaderSlow(); friend class ExtendedNominalRequest; + friend class BindExtensionsRequest; friend class Decl; public: using Decl::getASTContext; @@ -2033,14 +2093,16 @@ class ExtensionDecl final : public GenericContext, public Decl, Type getExtendedType() const; /// Retrieve the nominal type declaration that is being extended. - /// Will trip an assertion if the declaration has not already been computed. + /// Will trip an assertion if the declaration has not already been computed. /// In order to fail fast when type checking work is attempted /// before extension binding has taken place. - NominalTypeDecl *getExtendedNominal() const; - /// Compute the nominal type declaration that is being extended. - NominalTypeDecl *computeExtendedNominal() const; + /// Compute the nominal type declaration that is being extended. The result + /// is not cached, this should only be invoked by extension binding itself. + /// FIXME: Make this private once lldb has been migrated off it. + NominalTypeDecl *computeExtendedNominal( + bool excludeMacroExpansions=false) const; /// \c hasBeenBound means nothing if this extension can never been bound /// because it is not at the top level. @@ -2110,7 +2172,10 @@ class ExtensionDecl final : public GenericContext, public Decl, /// Determine whether this extension context is in the same defining module as /// the original nominal type context. - bool isInSameDefiningModule() const; + /// + /// \param RespectOriginallyDefinedIn Whether to respect + /// \c @_originallyDefinedIn attributes or the actual location of the decls. + bool isInSameDefiningModule(bool RespectOriginallyDefinedIn = true) const; /// Determine whether this extension is equivalent to one that requires at /// at least some constraints to be written in the source. @@ -2287,6 +2352,7 @@ class PatternBindingEntry { // Flags::Checked. friend class PatternBindingEntryRequest; friend class PatternBindingCheckedAndContextualizedInitRequest; + friend class PatternBindingCaptureInfoRequest; bool isFullyValidated() const { return InitContextAndFlags.getInt().contains( @@ -2435,8 +2501,20 @@ class PatternBindingEntry { /// from the source range. SourceRange getSourceRange(bool omitAccessors = false) const; - CaptureInfo getCaptureInfo() const { return Captures; } - void setCaptureInfo(CaptureInfo captures) { Captures = captures; } + /// Retrieve the computed capture info, or \c nullopt if it hasn't been + /// computed yet. + std::optional getCachedCaptureInfo() const { + if (!Captures.hasBeenComputed()) + return std::nullopt; + + return Captures; + } + + void setCaptureInfo(CaptureInfo captures) { + ASSERT(!Captures.hasBeenComputed()); + ASSERT(captures.hasBeenComputed()); + Captures = captures; + } private: SourceLoc getLastAccessorEndLoc() const; @@ -2460,6 +2538,7 @@ class PatternBindingDecl final : public Decl, friend class Decl; friend class PatternBindingEntryRequest; friend class PatternBindingCheckedAndContextualizedInitRequest; + friend class PatternBindingCaptureInfoRequest; SourceLoc StaticLoc; ///< Location of the 'static/class' keyword, if present. SourceLoc VarLoc; ///< Location of the 'var' keyword. @@ -2503,9 +2582,7 @@ class PatternBindingDecl final : public Decl, Pattern *Pat, Expr *E, DeclContext *Parent); - SourceLoc getStartLoc() const { - return StaticLoc.isValid() ? StaticLoc : VarLoc; - } + SourceLoc getStartLoc() const; SourceRange getSourceRange() const; unsigned getNumPatternEntries() const { @@ -2639,13 +2716,9 @@ class PatternBindingDecl final : public Decl, getMutablePatternList()[i].setInitContext(init); } - CaptureInfo getCaptureInfo(unsigned i) const { - return getPatternList()[i].getCaptureInfo(); - } - - void setCaptureInfo(unsigned i, CaptureInfo captures) { - getMutablePatternList()[i].setCaptureInfo(captures); - } + /// Retrieve the capture info for the initializer at the given index, + /// computing if needed. + CaptureInfo getCaptureInfo(unsigned i) const; /// Given that this PBD is the parent pattern for the specified VarDecl, /// return the entry of the VarDecl in our PatternList. For example, in: @@ -2751,7 +2824,7 @@ class PatternBindingDecl final : public Decl, private: MutableArrayRef getMutablePatternList() { // Pattern entries are tail allocated. - return {getTrailingObjects(), getNumPatternEntries()}; + return getTrailingObjects(getNumPatternEntries()); } }; @@ -2851,6 +2924,10 @@ class ValueDecl : public Decl { /// a null pointer. unsigned noOpaqueResultType : 1; + /// Whether the ClangUSRGenerationRequest request was evaluated and produced + /// a std::nullopt. + unsigned noClangUSR : 1; + /// Whether the "isFinal" bit has been computed yet. unsigned isFinalComputed : 1; @@ -2867,6 +2944,10 @@ class ValueDecl : public Decl { /// Whether we've evaluated the ApplyAccessNoteRequest. unsigned accessNoteApplied : 1; + + /// Whether the AvailabilityDomainForDeclRequest request was evaluated and + /// yielded no availability domain. + unsigned noAvailabilityDomain : 1; } LazySemanticInfo = { }; friend class DynamicallyReplacedDeclRequest; @@ -2880,6 +2961,8 @@ class ValueDecl : public Decl { friend class ActorIsolationRequest; friend class OpaqueResultTypeRequest; friend class ApplyAccessNoteRequest; + friend class AvailabilityDomainForDeclRequest; + friend class ClangUSRGenerationRequest; friend class Decl; SourceLoc getLocFromSource() const { return NameLoc; } @@ -3011,6 +3094,10 @@ class ValueDecl : public Decl { /// \c \@usableFromInline, \c \@inlinalbe, and \c \@_alwaysEmitIntoClient bool isUsableFromInline() const; + // Returns \c true if this value decl is marked with an attribute that implies + // \c \@inlinable semantics: either \c \@inlinable or \c \@inline(always) + bool hasAttributeWithInlinableSemantics() const; + /// Returns \c true if this declaration is *not* intended to be used directly /// by application developers despite the visibility. bool shouldHideFromEditor() const; @@ -3206,6 +3293,9 @@ class ValueDecl : public Decl { /// `AbstractStorageDecl`, returns `false`. bool isAsync() const; + /// Returns whether this function represents a defer body. + bool isDeferBody() const; + private: bool isObjCDynamic() const { return isObjC() && isDynamic(); @@ -3590,10 +3680,7 @@ class OpaqueTypeDecl final : /// Retrieve the buffer containing the opaque return type /// representations that correspond to the opaque generic parameters. ArrayRef getOpaqueReturnTypeReprs() const { - return { - getTrailingObjects(), - getNumOpaqueReturnTypeReprs() - }; + return getTrailingObjects(getNumOpaqueReturnTypeReprs()); } /// Should the underlying type be visible to clients outside of the module? @@ -3601,7 +3688,8 @@ class OpaqueTypeDecl final : /// The substitutions that map the generic parameters of the opaque type to /// the unique underlying types, when that information is known. - std::optional getUniqueUnderlyingTypeSubstitutions() const; + std::optional getUniqueUnderlyingTypeSubstitutions( + bool typeCheckFunctionBodies=true) const; void setUniqueUnderlyingTypeSubstitutions(SubstitutionMap subs) { assert(!UniqueUnderlyingType.has_value() && "resetting underlying type?!"); @@ -3640,40 +3728,36 @@ class OpaqueTypeDecl final : return false; } - using AvailabilityCondition = std::pair; - class ConditionallyAvailableSubstitutions final - : private llvm::TrailingObjects< - ConditionallyAvailableSubstitutions, - AvailabilityCondition> { + : private llvm::TrailingObjects { friend TrailingObjects; - unsigned NumAvailabilityConditions; + unsigned NumAvailabilityQueries; SubstitutionMap Substitutions; /// A type with limited availability described by the provided set /// of availability conditions (with `and` relationship). ConditionallyAvailableSubstitutions( - ArrayRef availabilityContext, + ArrayRef availabilityQueries, SubstitutionMap substitutions) - : NumAvailabilityConditions(availabilityContext.size()), + : NumAvailabilityQueries(availabilityQueries.size()), Substitutions(substitutions) { - assert(!availabilityContext.empty()); - std::uninitialized_copy(availabilityContext.begin(), - availabilityContext.end(), - getTrailingObjects()); + assert(!availabilityQueries.empty()); + std::uninitialized_copy(availabilityQueries.begin(), + availabilityQueries.end(), getTrailingObjects()); } public: - ArrayRef getAvailability() const { - return {getTrailingObjects(), NumAvailabilityConditions}; + ArrayRef getAvailabilityQueries() const { + return getTrailingObjects(NumAvailabilityQueries); } SubstitutionMap getSubstitutions() const { return Substitutions; } static ConditionallyAvailableSubstitutions * - get(ASTContext &ctx, ArrayRef availabilityContext, + get(ASTContext &ctx, ArrayRef availabilityContext, SubstitutionMap substitutions); }; }; @@ -4819,6 +4903,16 @@ class EnumDecl final : public NominalTypeDecl { return getAttrs().hasAttribute(); } + /// True if the enum is marked with `@c`. + bool isCDeclEnum() const { + return getAttrs().hasAttribute(); + } + + /// True if the enum is marked with `@c` or `@objc`. + bool isCCompatibleEnum() const { + return isCDeclEnum() || isObjC(); + } + /// True if the enum can be exhaustively switched within \p useDC. /// /// Note that this property is \e not necessarily true for all children of @@ -5851,7 +5945,7 @@ class AbstractStorageDecl : public ValueDecl { inline AccessorDecl *getAccessor(AccessorKind kind) const; ArrayRef getAllAccessors() const { - return { getTrailingObjects(), NumAccessors }; + return getTrailingObjects(NumAccessors); } void addOpaqueAccessor(AccessorDecl *accessor); @@ -5860,7 +5954,7 @@ class AbstractStorageDecl : public ValueDecl { private: MutableArrayRef getAccessorsBuffer() { - return { getTrailingObjects(), NumAccessors }; + return getTrailingObjects(NumAccessors); } bool registerAccessor(AccessorDecl *accessor, AccessorIndex index); @@ -6163,7 +6257,7 @@ class AbstractStorageDecl : public ValueDecl { bool requiresOpaqueRead2Coroutine() const; /// Does this storage require a 'set' accessor in its opaque-accessors set? - bool requiresOpaqueSetter() const { return supportsMutation(); } + bool requiresOpaqueSetter() const; /// Does this storage require a '_modify' accessor in its opaque-accessors /// set? @@ -6232,10 +6326,16 @@ class AbstractStorageDecl : public ValueDecl { bool forConformance=false) const; /// Determine how this storage declaration should actually be accessed. - AccessStrategy getAccessStrategy(AccessSemantics semantics, - AccessKind accessKind, ModuleDecl *module, - ResilienceExpansion expansion, - bool useOldABI) const; + AccessStrategy getAccessStrategy( + AccessSemantics semantics, AccessKind accessKind, ModuleDecl *module, + ResilienceExpansion expansion, + std::optional> location, + bool useOldABI) const; + + /// Whether access is via physical storage. + bool isAccessedViaPhysicalStorage(AccessSemantics semantics, + AccessKind accessKind, ModuleDecl *module, + ResilienceExpansion expansion) const; /// Do we need to use resilient access patterns outside of this /// property's resilience domain? @@ -6256,9 +6356,12 @@ class AbstractStorageDecl : public ValueDecl { /// Otherwise, its override must be referenced. bool isValidKeyPathComponent() const; - /// True if the storage exports a property descriptor for key paths in - /// other modules. - bool exportsPropertyDescriptor() const; + /// If the storage exports a property descriptor for key paths in other + /// modules, this returns the generic signature in which its member methods + /// are emitted. If the storage does not export a property descriptor, + /// returns `std::nullopt`. + std::optional + getPropertyDescriptorGenericSignature() const; /// True if any of the accessors to the storage is private or fileprivate. bool hasPrivateAccessor() const; @@ -6895,6 +6998,9 @@ class ParamDecl : public VarDecl { /// Whether or not this parameter is 'sending'. IsSending = 1 << 4, + + /// Whether or not this parameter is isolated to a caller. + IsCallerIsolated = 1 << 5, }; /// The type repr and 3 bits used for flags. @@ -6972,6 +7078,10 @@ class ParamDecl : public VarDecl { /// Create a an identical copy of this ParamDecl. static ParamDecl *clone(const ASTContext &Ctx, ParamDecl *PD); + static ParamDecl *cloneAccessor(const ASTContext &Ctx, + ParamDecl const *subscriptParam, + DeclContext *Parent); + static ParamDecl * createImplicit(ASTContext &Context, SourceLoc specifierLoc, SourceLoc argumentNameLoc, Identifier argumentName, @@ -6991,6 +7101,14 @@ class ParamDecl : public VarDecl { Identifier parameterName, Expr *defaultValue, DefaultArgumentInitializer *defaultValueInitContext, DeclContext *dc); + SourceLoc getLocFromSource() const { + // If we have a name loc, use it, otherwise fallback to the start loc for + // e.g enum elements without parameter names. + if (auto nameLoc = getNameLoc()) + return nameLoc; + return getStartLoc(); + } + /// Retrieve the argument (API) name for this function parameter. Identifier getArgumentName() const { return ArgumentNameAndFlags.getPointer(); @@ -7183,6 +7301,18 @@ class ParamDecl : public VarDecl { removeFlag(Flag::IsSending); } + /// Whether or not this parameter is marked with 'nonisolated(nonsending)'. + bool isCallerIsolated() const { + return getOptions().contains(Flag::IsCallerIsolated); + } + + void setCallerIsolated(bool value = true) { + if (value) + addFlag(Flag::IsCallerIsolated); + else + removeFlag(Flag::IsCallerIsolated); + } + /// Whether or not this parameter is marked with '@_addressable'. bool isAddressable() const { return getOptions().contains(Flag::IsAddressable); @@ -8106,6 +8236,11 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// instance method. bool isObjCInstanceMethod() const; + /// Get the foreign language targeted by a @c-style attribute, if any. + /// Used to abstract away the change in meaning of @c vs @_cdecl while + /// formalizing the attribute. + std::optional getCDeclKind() const; + /// Determine whether the name of an argument is an API name by default /// depending on the function context. bool argumentNameIsAPIByDefault() const; @@ -8139,8 +8274,6 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { return cast_or_null(ValueDecl::getOverriddenDecl()); } - std::optional getExecutionBehavior() const; - /// Whether the declaration is later overridden in the module /// /// Overrides are resolved during type checking; only query this field after @@ -8198,10 +8331,6 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Asserts if not in type context. Type getMethodInterfaceType() const; - /// Tests if this is a function returning a DynamicSelfType, or a - /// constructor. - bool hasDynamicSelfResult() const; - /// The async function marked as the alternative to this function, if any. AbstractFunctionDecl *getAsyncAlternative() const; @@ -8373,9 +8502,8 @@ class FuncDecl : public AbstractFunctionDecl { SourceLoc getStaticLoc() const { return StaticLoc; } SourceLoc getFuncLoc() const { return FuncLoc; } - SourceLoc getStartLoc() const { - return StaticLoc.isValid() ? StaticLoc : FuncLoc; - } + SourceLoc getStartLoc() const; + SourceLoc getEndLoc() const; SourceRange getSourceRange() const; TypeRepr *getResultTypeRepr() const { return FnRetType.getTypeRepr(); } @@ -8569,7 +8697,7 @@ class AccessorDecl final : public FuncDecl { switch (getAccessorKind()) { #define OBSERVING_ACCESSOR(ID, KEYWORD) \ case AccessorKind::ID: return true; -#define ACCESSOR(ID) \ +#define ACCESSOR(ID, KEYWORD) \ case AccessorKind::ID: return false; #include "swift/AST/AccessorKinds.def" } @@ -8592,7 +8720,7 @@ class AccessorDecl final : public FuncDecl { switch (getAccessorKind()) { #define COROUTINE_ACCESSOR(ID, KEYWORD) \ case AccessorKind::ID: return true; -#define ACCESSOR(ID) \ +#define ACCESSOR(ID, KEYWORD) \ case AccessorKind::ID: return false; #include "swift/AST/AccessorKinds.def" } @@ -8608,6 +8736,13 @@ class AccessorDecl final : public FuncDecl { /// an explicit parameter in its parameter list. bool isSimpleDidSet() const; + bool isBorrowAccessor() const { + return getAccessorKind() == AccessorKind::Borrow; + } + bool isMutateAccessor() const { + return getAccessorKind() == AccessorKind::Mutate; + } + void setIsTransparent(bool transparent) { Bits.AccessorDecl.IsTransparent = transparent; Bits.AccessorDecl.IsTransparentComputed = 1; @@ -8672,7 +8807,7 @@ class EnumCaseDecl final : public Decl, { Bits.EnumCaseDecl.NumElements = Elements.size(); std::uninitialized_copy(Elements.begin(), Elements.end(), - getTrailingObjects()); + getTrailingObjects()); } SourceLoc getLocFromSource() const { return CaseLoc; } @@ -8683,8 +8818,8 @@ class EnumCaseDecl final : public Decl, /// Get the list of elements declared in this case. ArrayRef getElements() const { - return {getTrailingObjects(), - static_cast(Bits.EnumCaseDecl.NumElements)}; + return getTrailingObjects( + static_cast(Bits.EnumCaseDecl.NumElements)); } SourceRange getSourceRange() const; @@ -8719,7 +8854,8 @@ class EnumCaseDecl final : public Decl, /// EnumCaseDecl. class EnumElementDecl : public DeclContext, public ValueDecl { friend class EnumRawValuesRequest; - + friend class LifetimeDependenceInfoRequest; + /// This is the type specified with the enum element, for /// example 'Int' in 'case Y(Int)'. This is null if there is no type /// associated with this element, as in 'case Z' or in all elements of enum @@ -8731,6 +8867,11 @@ class EnumElementDecl : public DeclContext, public ValueDecl { /// The raw value literal for the enum element, or null. LiteralExpr *RawValueExpr; +protected: + struct { + unsigned NoLifetimeDependenceInfo : 1; + } LazySemanticInfo = {}; + public: EnumElementDecl(SourceLoc IdentifierLoc, DeclName Name, ParameterList *Params, @@ -9693,6 +9834,34 @@ class MacroExpansionDecl : public Decl, public FreestandingMacroExpansion { } }; +/// UsingDecl - This represents a single `using` declaration, e.g.: +/// using @MainActor +class UsingDecl : public Decl { + friend class Decl; + +private: + SourceLoc UsingLoc, SpecifierLoc; + + UsingDecl(SourceLoc usingLoc, SourceLoc specifierLoc, + UsingSpecifier specifier, DeclContext *parent); + +public: + UsingSpecifier getSpecifier() const { + return static_cast(Bits.UsingDecl.Specifier); + } + + std::string getSpecifierName() const; + + SourceLoc getLocFromSource() const { return UsingLoc; } + SourceRange getSourceRange() const { return {UsingLoc, SpecifierLoc}; } + + static UsingDecl *create(ASTContext &ctx, SourceLoc usingLoc, + SourceLoc specifierLoc, UsingSpecifier specifier, + DeclContext *parent); + + static bool classof(const Decl *D) { return D->getKind() == DeclKind::Using; } +}; + inline void AbstractStorageDecl::overwriteSetterAccess(AccessLevel accessLevel) { Accessors.setInt(accessLevel); diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index 654d9cf221688..632cdebe58d95 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -143,7 +143,7 @@ SIMPLE_DECL_ATTR(NSManaged, NSManaged, CONTEXTUAL_SIMPLE_DECL_ATTR(lazy, Lazy, OnVar, - DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, + DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 16) SIMPLE_DECL_ATTR(LLDBDebuggerFunction, LLDBDebuggerFunction, @@ -163,7 +163,7 @@ SIMPLE_DECL_ATTR(unsafe_no_objc_tagged_pointer, UnsafeNoObjCTaggedPointer, DECL_ATTR(inline, Inline, OnVar | OnSubscript | OnAbstractFunction, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 20) DECL_ATTR(_semantics, Semantics, @@ -193,7 +193,7 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(postfix, Postfix, SIMPLE_DECL_ATTR(_transparent, Transparent, OnFunc | OnAccessor | OnConstructor | OnVar | OnDestructor, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 26) SIMPLE_DECL_ATTR(requires_stored_property_inits, RequiresStoredPropertyInits, @@ -201,7 +201,11 @@ SIMPLE_DECL_ATTR(requires_stored_property_inits, RequiresStoredPropertyInits, ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 27) -// Unused '28' +SIMPLE_DECL_ATTR(_neverEmitIntoClient, NeverEmitIntoClient, + OnVar | OnSubscript | OnAbstractFunction, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, + 28) + // Unused '29' SIMPLE_DECL_ATTR(nonobjc, NonObjC, @@ -216,7 +220,7 @@ SIMPLE_DECL_ATTR(_fixed_layout, FixedLayout, SIMPLE_DECL_ATTR(inlinable, Inlinable, OnVar | OnSubscript | OnAbstractFunction, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 32) DECL_ATTR(_specialize, Specialize, @@ -364,10 +368,11 @@ SIMPLE_DECL_ATTR(_show_in_interface, ShowInInterface, UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 62) -DECL_ATTR(_cdecl, CDecl, - OnFunc | OnAccessor, +DECL_ATTR(c, CDecl, + OnFunc | OnAccessor | OnEnum, LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 63) +DECL_ATTR_ALIAS(_cdecl, CDecl) SIMPLE_DECL_ATTR(usableFromInline, UsableFromInline, OnAbstractFunction | OnVar | OnSubscript | OnNominalType | OnTypeAlias, @@ -455,7 +460,7 @@ DECL_ATTR(_dynamicReplacement, DynamicReplacement, SIMPLE_DECL_ATTR(_borrowed, Borrowed, OnVar | OnSubscript, - UserInaccessible | NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, + UserInaccessible | NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 81) DECL_ATTR(_private, PrivateImport, @@ -465,7 +470,7 @@ DECL_ATTR(_private, PrivateImport, SIMPLE_DECL_ATTR(_alwaysEmitIntoClient, AlwaysEmitIntoClient, OnVar | OnSubscript | OnAbstractFunction, - UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, + UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 83) SIMPLE_DECL_ATTR(_implementationOnly, ImplementationOnly, @@ -619,9 +624,10 @@ SIMPLE_DECL_ATTR(_implicitSelfCapture, ImplicitSelfCapture, UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 115) -SIMPLE_DECL_ATTR(_inheritActorContext, InheritActorContext, +DECL_ATTR(_inheritActorContext, InheritActorContext, OnParam, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove | ForbiddenInABIAttr, + // since the _inheritActorContext(always) forces an actor capture, it changes ABI of the closure this applies to + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 116) SIMPLE_DECL_ATTR(_eagerMove, EagerMove, @@ -809,7 +815,10 @@ SIMPLE_DECL_ATTR(_noObjCBridging, NoObjCBridging, UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 155) -// Unused '156': Used to be `_distributedThunkTarget` but completed implementation in Swift 6.0 does not need it after all +SIMPLE_DECL_ATTR(_manualOwnership, ManualOwnership, + OnAbstractFunction | OnSubscript, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, + 156) DECL_ATTR(_allowFeatureSuppression, AllowFeatureSuppression, OnAnyDecl, @@ -832,7 +841,7 @@ SIMPLE_DECL_ATTR(sensitive, Sensitive, 159) SIMPLE_DECL_ATTR(unsafe, Unsafe, - OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType | OnExtension | OnTypeAlias | OnEnumElement, + OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType | OnExtension | OnTypeAlias | OnEnumElement | OnImport, UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 160) @@ -840,6 +849,7 @@ DECL_ATTR(lifetime, Lifetime, OnAccessor | OnConstructor | OnFunc | OnSubscript, LongAttribute | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | AllowMultipleAttributes | EquivalentInABIAttr, 161) +DECL_ATTR_ALIAS(_lifetime, Lifetime) SIMPLE_DECL_ATTR(_addressableSelf, AddressableSelf, OnAccessor | OnConstructor | OnFunc | OnSubscript, @@ -860,13 +870,8 @@ DECL_ATTR(abi, ABI, OnConstructor | OnFunc | OnSubscript | OnVar, LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 165) -DECL_ATTR_FEATURE_REQUIREMENT(ABI, ABIAttribute) -DECL_ATTR(execution, Execution, - OnFunc | OnConstructor | OnSubscript | OnVar, - ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, - 166) -DECL_ATTR_FEATURE_REQUIREMENT(Execution, ExecutionAttribute) +// Unused '166': Used to be `@execution(caller | concurrent)` replaced with `@concurrent` and `nonisolated(nonsending)` SIMPLE_DECL_ATTR(const, ConstVal, OnParam | OnVar | OnFunc, @@ -879,7 +884,30 @@ SIMPLE_DECL_ATTR(constInitialized, ConstInitialized, 168) DECL_ATTR_FEATURE_REQUIREMENT(ConstInitialized, CompileTimeValues) -LAST_DECL_ATTR(ConstInitialized) +DECL_ATTR(nonexhaustive, Nonexhaustive, + OnEnum, + ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | ForbiddenInABIAttr, + 169) +DECL_ATTR_FEATURE_REQUIREMENT(Nonexhaustive, NonexhaustiveAttribute) + +SIMPLE_DECL_ATTR(concurrent, Concurrent, + OnFunc | OnConstructor | OnSubscript | OnVar, + ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, + 170) + +// Unused '171': Used to be `@preEnumExtensibility` + +DECL_ATTR(specialized, Specialized, + OnConstructor | OnFunc | OnAccessor, + AllowMultipleAttributes | LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, + 172) + +SIMPLE_DECL_ATTR(_unsafeSelfDependentResult, UnsafeSelfDependentResult, + OnAccessor, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove | EquivalentInABIAttr, + 173) + +LAST_DECL_ATTR(UnsafeSelfDependentResult) #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index 72d7192cfc280..b064f05cfd95d 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -114,21 +114,20 @@ enum class DeclContextKind : unsigned { /// Describes the kind of a particular conformance. /// /// The following code involves conformances of the three different kinds: -/// \code -/// protocol P { } -/// protocol P2 : P { } /// -/// class Super : P2 { } -/// class Sub : Super { } -/// \endcode +/// protocol P { } +/// protocol P2 : P { } /// -/// \c Super conforms to \c P2 via an explicit conformance, +/// class Super : P2 { } +/// class Sub : Super { } +/// +/// `Super` conforms to `P2` via an explicit conformance, /// specified on the class declaration itself. /// -/// \c Super conforms to \c P via an implied conformance, whose -/// origin is the explicit conformance to \c P2. +/// `Super` conforms to `P` via an implied conformance, whose +/// origin is the explicit conformance to `P2`. /// -/// \c Sub conforms to \c P2 and \c P via inherited conformances, +/// `Sub` conforms to `P2` and `P` via inherited conformances, /// which link back to the conformances described above. /// /// The enumerators are ordered in terms of decreasing preference: @@ -137,22 +136,28 @@ enum class DeclContextKind : unsigned { /// conformance kinds supersede later conformance kinds, possibly with a /// diagnostic (e.g., if an inherited conformance supersedes an /// explicit conformance). +/// +/// - Important: The raw values of these cases are relied upon by +/// `declared_protocol_conformance_here`. +/// - If you add a new case, give it an explicit raw value. +/// - If you change the cases, make sure they remain in sync with the +/// aforementioned diagnostic. enum class ConformanceEntryKind : unsigned { /// Inherited from a superclass conformance. - Inherited, + Inherited = 0, /// Explicitly specified. - Explicit, + Explicit = 1, /// The conformance is generated by a macro that has not been /// expanded yet. - PreMacroExpansion, + PreMacroExpansion = 2, /// Implicitly synthesized. - Synthesized, + Synthesized = 3, /// Implied by an explicitly-specified conformance. - Implied, + Implied = 4, Last_Kind = Implied }; diff --git a/include/swift/AST/DeclExportabilityVisitor.h b/include/swift/AST/DeclExportabilityVisitor.h index 6eb9c46c928d9..732113d55a056 100644 --- a/include/swift/AST/DeclExportabilityVisitor.h +++ b/include/swift/AST/DeclExportabilityVisitor.h @@ -158,6 +158,7 @@ class DeclExportabilityVisitor UNREACHABLE(MissingMember); UNREACHABLE(GenericTypeParam); UNREACHABLE(Param); + UNREACHABLE(Using); #undef UNREACHABLE diff --git a/include/swift/AST/DeclNameExtractor.h b/include/swift/AST/DeclNameExtractor.h new file mode 100644 index 0000000000000..92be7502b87b1 --- /dev/null +++ b/include/swift/AST/DeclNameExtractor.h @@ -0,0 +1,58 @@ +//===--- DeclNameExtractor.h - Decl Name Demangling -------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 DeclNameExtractor utility. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ASTContext.h" +#include "swift/AST/Identifier.h" + +namespace swift { +namespace Demangle { + +class Node; + +class DeclNameExtractor { +private: + ASTContext &Ctx; + +public: + DeclNameExtractor(ASTContext &ctx) : Ctx(ctx) {} + + /// Extract a DeclName from a demangling node + /// \returns true if a \c DeclName is found, false otherwise. + bool extractDeclName(Node *node, DeclName &name, + Identifier &privateDiscriminator); + +private: + bool extractIdentifierName(Node *node, DeclName &declName, + Identifier &privateDiscriminator); + bool extractFunctionLikeName(Node *node, DeclName &declName, + Identifier &privateDiscriminator); + void extractArgLabelsFromLabelList(Node *LabelList, + SmallVectorImpl &ArgLabels); + void extractArgLabelsFromType(Node *Type, + SmallVectorImpl &ArgLabels); + DeclBaseName extractOperatorName(Node *node); +}; + +/// Returns an identifier with the given name, automatically removing any +/// surrounding backticks that are present for raw identifiers. +Identifier getIdentifier(ASTContext &Ctx, StringRef name); + +bool extractNameNodeInfo(ASTContext &Ctx, Node *node, StringRef &name, + StringRef &relatedEntityKind, + Identifier &privateDiscriminator); + +} // namespace Demangle +} // namespace swift diff --git a/include/swift/AST/DeclNameLoc.h b/include/swift/AST/DeclNameLoc.h index a4a65ef50be99..494a4ae721faf 100644 --- a/include/swift/AST/DeclNameLoc.h +++ b/include/swift/AST/DeclNameLoc.h @@ -20,6 +20,8 @@ #include "swift/Basic/LLVM.h" #include "swift/Basic/SourceLoc.h" +#include "llvm/ADT/ArrayRef.h" + class BridgedDeclNameLoc; namespace swift { @@ -71,6 +73,10 @@ class DeclNameLoc { explicit DeclNameLoc(SourceLoc baseNameLoc) : DeclNameLoc(baseNameLoc.getOpaquePointerValue(), 0) {} + explicit DeclNameLoc(ASTContext &ctx, SourceLoc moduleSelectorLoc, + SourceLoc baseNameLoc) + : DeclNameLoc(baseNameLoc) { } + /// Create declaration name location information for a compound /// name. DeclNameLoc(ASTContext &ctx, SourceLoc baseNameLoc, @@ -78,6 +84,13 @@ class DeclNameLoc { ArrayRef argumentLabelLocs, SourceLoc rParenLoc); + DeclNameLoc(ASTContext &ctx, SourceLoc moduleSelectorLoc, + SourceLoc baseNameLoc, + SourceLoc lParenLoc, + ArrayRef argumentLabelLocs, + SourceLoc rParenLoc) + : DeclNameLoc(ctx, baseNameLoc, lParenLoc, argumentLabelLocs, rParenLoc) { } + /// Whether the location information is valid. bool isValid() const { return getBaseNameLoc().isValid(); } @@ -111,6 +124,10 @@ class DeclNameLoc { return getSourceLocs()[FirstArgumentLabelIndex + index]; } + SourceLoc getModuleSelectorLoc() const { + return SourceLoc(); + } + SourceLoc getStartLoc() const { return getBaseNameLoc(); } diff --git a/include/swift/AST/DeclNodes.def b/include/swift/AST/DeclNodes.def index 2f02c4bf5aac8..ef83c8a92777f 100644 --- a/include/swift/AST/DeclNodes.def +++ b/include/swift/AST/DeclNodes.def @@ -190,6 +190,7 @@ DECL(Missing, Decl) DECL(MissingMember, Decl) DECL(PatternBinding, Decl) DECL(EnumCase, Decl) +DECL(Using, Decl) ABSTRACT_DECL(Operator, Decl) OPERATOR_DECL(InfixOperator, OperatorDecl) diff --git a/include/swift/AST/DiagnosticArgument.h b/include/swift/AST/DiagnosticArgument.h new file mode 100644 index 0000000000000..9f8652db72eaf --- /dev/null +++ b/include/swift/AST/DiagnosticArgument.h @@ -0,0 +1,213 @@ +//===--- DiagnosticArgument.h -----------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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_AST_DIAGNOSTIC_ARGUMENT_H +#define SWIFT_AST_DIAGNOSTIC_ARGUMENT_H + +#include "swift/AST/ActorIsolation.h" +#include "swift/AST/AvailabilityDomain.h" +#include "swift/AST/DiagnosticConsumer.h" +#include "swift/AST/Identifier.h" +#include "swift/AST/LayoutConstraint.h" +#include "swift/AST/TypeLoc.h" +#include "swift/Basic/Version.h" +#include "llvm/Support/VersionTuple.h" + +namespace clang { +class NamedDecl; +class Type; +} // namespace clang + +namespace swift { + +class Decl; +class DeclAttribute; +class TypeAttribute; +class TypeRepr; + +enum class DescriptivePatternKind : uint8_t; +enum class DescriptiveDeclKind : uint8_t; +enum class ReferenceOwnership : uint8_t; +enum class SelfAccessKind : uint8_t; +enum class StaticSpellingKind : uint8_t; +enum class StmtKind; + +/// A family of wrapper types for compiler data types that forces its +/// underlying data to be formatted with full qualification. +/// +/// So far, this is only useful for \c Type, hence the SFINAE'ing. +template +struct FullyQualified {}; + +template +struct FullyQualified< + T, typename std::enable_if::value>::type> { + Type t; + +public: + FullyQualified(T t) : t(t) {}; + + Type getType() const { return t; } +}; + +struct WitnessType { + Type t; + + WitnessType(Type t) : t(t) {} + + Type getType() { return t; } +}; + +/// Describes the kind of diagnostic argument we're storing. +enum class DiagnosticArgumentKind { + String, + Integer, + Unsigned, + Identifier, + ObjCSelector, + Decl, + Type, + TypeRepr, + FullyQualifiedType, + WitnessType, + DescriptivePatternKind, + SelfAccessKind, + ReferenceOwnership, + StaticSpellingKind, + DescriptiveDeclKind, + DescriptiveStmtKind, + DeclAttribute, + TypeAttribute, + AvailabilityDomain, + AvailabilityRange, + VersionTuple, + LayoutConstraint, + ActorIsolation, + IsolationSource, + Diagnostic, + ClangDecl, + ClangType, +}; + +/// Variant type that holds a single diagnostic argument of a known +/// type. +/// +/// All diagnostic arguments are converted to an instance of this class. +class DiagnosticArgument { + DiagnosticArgumentKind Kind; + union { + int IntegerVal; + unsigned UnsignedVal; + StringRef StringVal; + DeclNameRef IdentifierVal; + ObjCSelector ObjCSelectorVal; + const Decl *TheDecl; + Type TypeVal; + TypeRepr *TyR; + FullyQualified FullyQualifiedTypeVal; + WitnessType WitnessTypeVal; + DescriptivePatternKind DescriptivePatternKindVal; + SelfAccessKind SelfAccessKindVal; + ReferenceOwnership ReferenceOwnershipVal; + StaticSpellingKind StaticSpellingKindVal; + DescriptiveDeclKind DescriptiveDeclKindVal; + StmtKind DescriptiveStmtKindVal; + const DeclAttribute *DeclAttributeVal; + const TypeAttribute *TypeAttributeVal; + AvailabilityDomain AvailabilityDomainVal; + AvailabilityRange AvailabilityRangeVal; + llvm::VersionTuple VersionVal; + LayoutConstraint LayoutConstraintVal; + ActorIsolation ActorIsolationVal; + IsolationSource IsolationSourceVal; + DiagnosticInfo *DiagnosticVal; + const clang::NamedDecl *ClangDecl; + const clang::Type *ClangType; + }; + +public: + DiagnosticArgument(StringRef S); + DiagnosticArgument(int I); + DiagnosticArgument(unsigned I); + DiagnosticArgument(DeclNameRef R); + DiagnosticArgument(DeclName D); + DiagnosticArgument(DeclBaseName D); + DiagnosticArgument(Identifier I); + DiagnosticArgument(ObjCSelector S); + DiagnosticArgument(const Decl *VD); + DiagnosticArgument(Type T); + DiagnosticArgument(TypeRepr *T); + DiagnosticArgument(FullyQualified FQT); + DiagnosticArgument(WitnessType WT); + DiagnosticArgument(const TypeLoc &TL); + DiagnosticArgument(DescriptivePatternKind DPK); + DiagnosticArgument(ReferenceOwnership RO); + DiagnosticArgument(SelfAccessKind SAK); + DiagnosticArgument(StaticSpellingKind SSK); + DiagnosticArgument(DescriptiveDeclKind DDK); + DiagnosticArgument(StmtKind SK); + DiagnosticArgument(const DeclAttribute *attr); + DiagnosticArgument(const TypeAttribute *attr); + DiagnosticArgument(const AvailabilityDomain domain); + DiagnosticArgument(const AvailabilityRange &range); + DiagnosticArgument(llvm::VersionTuple version); + DiagnosticArgument(LayoutConstraint L); + DiagnosticArgument(ActorIsolation AI); + DiagnosticArgument(IsolationSource IS); + DiagnosticArgument(DiagnosticInfo *D); + DiagnosticArgument(const clang::NamedDecl *ND); + DiagnosticArgument(const clang::Type *Ty); + + /// Initializes a diagnostic argument using the underlying type of the + /// given enum. + template < + typename EnumType, + typename std::enable_if::value>::type * = nullptr> + DiagnosticArgument(EnumType value) + : DiagnosticArgument( + static_cast::type>(value)) { + } + + DiagnosticArgumentKind getKind() const; + + StringRef getAsString() const; + int getAsInteger() const; + unsigned getAsUnsigned() const; + DeclNameRef getAsIdentifier() const; + ObjCSelector getAsObjCSelector() const; + const Decl *getAsDecl() const; + Type getAsType() const; + TypeRepr *getAsTypeRepr() const; + FullyQualified getAsFullyQualifiedType() const; + WitnessType getAsWitnessType() const; + DescriptivePatternKind getAsDescriptivePatternKind() const; + ReferenceOwnership getAsReferenceOwnership() const; + SelfAccessKind getAsSelfAccessKind() const; + StaticSpellingKind getAsStaticSpellingKind() const; + DescriptiveDeclKind getAsDescriptiveDeclKind() const; + StmtKind getAsDescriptiveStmtKind() const; + const DeclAttribute *getAsDeclAttribute() const; + const TypeAttribute *getAsTypeAttribute() const; + const AvailabilityDomain getAsAvailabilityDomain() const; + const AvailabilityRange getAsAvailabilityRange() const; + llvm::VersionTuple getAsVersionTuple() const; + LayoutConstraint getAsLayoutConstraint() const; + ActorIsolation getAsActorIsolation() const; + IsolationSource getAsIsolationSource() const; + DiagnosticInfo *getAsDiagnostic() const; + const clang::NamedDecl *getAsClangDecl() const; + const clang::Type *getAsClangType() const; +}; + +} // namespace swift + +#endif /* SWIFT_AST_DIAGNOSTIC_ARGUMENT_H */ diff --git a/include/swift/AST/DiagnosticBridge.h b/include/swift/AST/DiagnosticBridge.h index b110f530e9b6b..81a161aa3a549 100644 --- a/include/swift/AST/DiagnosticBridge.h +++ b/include/swift/AST/DiagnosticBridge.h @@ -46,6 +46,10 @@ class DiagnosticBridge { void enqueueDiagnostic(SourceManager &SM, const DiagnosticInfo &Info, unsigned innermostBufferID); + /// Emit a single diagnostic without location information. + void emitDiagnosticWithoutLocation( + const DiagnosticInfo &Info, llvm::raw_ostream &out, bool forceColors); + /// Flush all enqueued diagnostics. void flush(llvm::raw_ostream &OS, bool includeTrailingBreak, bool forceColors); diff --git a/include/swift/AST/DiagnosticConsumer.h b/include/swift/AST/DiagnosticConsumer.h index 51730e0202fc1..0ca6cb99c8c45 100644 --- a/include/swift/AST/DiagnosticConsumer.h +++ b/include/swift/AST/DiagnosticConsumer.h @@ -19,6 +19,7 @@ #ifndef SWIFT_BASIC_DIAGNOSTICCONSUMER_H #define SWIFT_BASIC_DIAGNOSTICCONSUMER_H +#include "swift/AST/DiagnosticKind.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/SourceLoc.h" #include "llvm/Support/SourceMgr.h" @@ -30,15 +31,6 @@ namespace swift { class SourceManager; enum class DiagID : uint32_t; -/// Describes the kind of diagnostic. -/// -enum class DiagnosticKind : uint8_t { - Error, - Warning, - Remark, - Note -}; - /// Information about a diagnostic passed to DiagnosticConsumers. struct DiagnosticInfo { DiagID ID = DiagID(0); diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index 56c9868b0593a..5cb88a4fd822a 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -18,9 +18,8 @@ #ifndef SWIFT_BASIC_DIAGNOSTICENGINE_H #define SWIFT_BASIC_DIAGNOSTICENGINE_H -#include "swift/AST/ActorIsolation.h" -#include "swift/AST/AvailabilityDomain.h" #include "swift/AST/DeclNameLoc.h" +#include "swift/AST/DiagnosticArgument.h" #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/TypeLoc.h" #include "swift/Basic/PrintDiagnosticNamesMode.h" @@ -35,7 +34,6 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/SaveAndRestore.h" -#include "llvm/Support/VersionTuple.h" namespace clang { class NamedDecl; @@ -43,27 +41,15 @@ class Type; } namespace swift { - class ConstructorDecl; class ClosureExpr; class Decl; class DeclAttribute; class DiagnosticEngine; class FuncDecl; - class GeneratedSourceInfo; class SourceManager; - class TypeAliasDecl; - class ValueDecl; class SourceFile; - - enum class CXXStdlibKind : uint8_t; - enum class DescriptivePatternKind : uint8_t; - enum class DiagGroupID : uint16_t; - enum class SelfAccessKind : uint8_t; - enum class ReferenceOwnership : uint8_t; - enum class StaticSpellingKind : uint8_t; - enum class DescriptiveDeclKind : uint8_t; - enum class DeclAttrKind : unsigned; - enum class StmtKind; + class ParamDecl; + class AnyPattern; /// Enumeration describing all of possible diagnostics. /// @@ -101,371 +87,10 @@ namespace swift { using DiagArgTuple = std::tuple::type...>; - /// A family of wrapper types for compiler data types that forces its - /// underlying data to be formatted with full qualification. - /// - /// So far, this is only useful for \c Type, hence the SFINAE'ing. - template struct FullyQualified {}; - - template - struct FullyQualified< - T, typename std::enable_if::value>::type> { - Type t; - - public: - FullyQualified(T t) : t(t){}; - - Type getType() const { return t; } - }; - - struct WitnessType { - Type t; - - WitnessType(Type t) : t(t) {} - - Type getType() { return t; } - }; - - /// Describes the kind of diagnostic argument we're storing. - /// - enum class DiagnosticArgumentKind { - String, - Integer, - Unsigned, - Identifier, - ObjCSelector, - Decl, - Type, - TypeRepr, - FullyQualifiedType, - WitnessType, - DescriptivePatternKind, - SelfAccessKind, - ReferenceOwnership, - StaticSpellingKind, - DescriptiveDeclKind, - DescriptiveStmtKind, - DeclAttribute, - AvailabilityDomain, - AvailabilityRange, - VersionTuple, - LayoutConstraint, - ActorIsolation, - IsolationSource, - Diagnostic, - ClangDecl, - ClangType, - }; - namespace diag { enum class RequirementKind : uint8_t; } - /// Variant type that holds a single diagnostic argument of a known - /// type. - /// - /// All diagnostic arguments are converted to an instance of this class. - class DiagnosticArgument { - DiagnosticArgumentKind Kind; - union { - int IntegerVal; - unsigned UnsignedVal; - StringRef StringVal; - DeclNameRef IdentifierVal; - ObjCSelector ObjCSelectorVal; - const Decl *TheDecl; - Type TypeVal; - TypeRepr *TyR; - FullyQualified FullyQualifiedTypeVal; - WitnessType WitnessTypeVal; - DescriptivePatternKind DescriptivePatternKindVal; - SelfAccessKind SelfAccessKindVal; - ReferenceOwnership ReferenceOwnershipVal; - StaticSpellingKind StaticSpellingKindVal; - DescriptiveDeclKind DescriptiveDeclKindVal; - StmtKind DescriptiveStmtKindVal; - const DeclAttribute *DeclAttributeVal; - AvailabilityDomain AvailabilityDomainVal; - AvailabilityRange AvailabilityRangeVal; - llvm::VersionTuple VersionVal; - LayoutConstraint LayoutConstraintVal; - ActorIsolation ActorIsolationVal; - IsolationSource IsolationSourceVal; - DiagnosticInfo *DiagnosticVal; - const clang::NamedDecl *ClangDecl; - const clang::Type *ClangType; - }; - - public: - DiagnosticArgument(StringRef S) - : Kind(DiagnosticArgumentKind::String), StringVal(S) { - } - - DiagnosticArgument(int I) - : Kind(DiagnosticArgumentKind::Integer), IntegerVal(I) { - } - - DiagnosticArgument(unsigned I) - : Kind(DiagnosticArgumentKind::Unsigned), UnsignedVal(I) { - } - - DiagnosticArgument(DeclNameRef R) - : Kind(DiagnosticArgumentKind::Identifier), IdentifierVal(R) {} - - DiagnosticArgument(DeclName D) - : Kind(DiagnosticArgumentKind::Identifier), - IdentifierVal(DeclNameRef(D)) {} - - DiagnosticArgument(DeclBaseName D) - : Kind(DiagnosticArgumentKind::Identifier), - IdentifierVal(DeclNameRef(D)) {} - - DiagnosticArgument(Identifier I) - : Kind(DiagnosticArgumentKind::Identifier), - IdentifierVal(DeclNameRef(I)) { - } - - DiagnosticArgument(ObjCSelector S) - : Kind(DiagnosticArgumentKind::ObjCSelector), ObjCSelectorVal(S) { - } - - DiagnosticArgument(const Decl *VD) - : Kind(DiagnosticArgumentKind::Decl), TheDecl(VD) { - } - - DiagnosticArgument(Type T) - : Kind(DiagnosticArgumentKind::Type), TypeVal(T) { - } - - DiagnosticArgument(TypeRepr *T) - : Kind(DiagnosticArgumentKind::TypeRepr), TyR(T) { - } - - DiagnosticArgument(FullyQualified FQT) - : Kind(DiagnosticArgumentKind::FullyQualifiedType), - FullyQualifiedTypeVal(FQT) {} - - DiagnosticArgument(WitnessType WT) - : Kind(DiagnosticArgumentKind::WitnessType), - WitnessTypeVal(WT) {} - - DiagnosticArgument(const TypeLoc &TL) { - if (TypeRepr *tyR = TL.getTypeRepr()) { - Kind = DiagnosticArgumentKind::TypeRepr; - TyR = tyR; - } else { - Kind = DiagnosticArgumentKind::Type; - TypeVal = TL.getType(); - } - } - - DiagnosticArgument(DescriptivePatternKind DPK) - : Kind(DiagnosticArgumentKind::DescriptivePatternKind), - DescriptivePatternKindVal(DPK) {} - - DiagnosticArgument(ReferenceOwnership RO) - : Kind(DiagnosticArgumentKind::ReferenceOwnership), - ReferenceOwnershipVal(RO) {} - - DiagnosticArgument(SelfAccessKind SAK) - : Kind(DiagnosticArgumentKind::SelfAccessKind), - SelfAccessKindVal(SAK) {} - - DiagnosticArgument(StaticSpellingKind SSK) - : Kind(DiagnosticArgumentKind::StaticSpellingKind), - StaticSpellingKindVal(SSK) {} - - DiagnosticArgument(DescriptiveDeclKind DDK) - : Kind(DiagnosticArgumentKind::DescriptiveDeclKind), - DescriptiveDeclKindVal(DDK) {} - - DiagnosticArgument(StmtKind SK) - : Kind(DiagnosticArgumentKind::DescriptiveStmtKind), - DescriptiveStmtKindVal(SK) {} - - DiagnosticArgument(const DeclAttribute *attr) - : Kind(DiagnosticArgumentKind::DeclAttribute), - DeclAttributeVal(attr) {} - - DiagnosticArgument(const AvailabilityDomain domain) - : Kind(DiagnosticArgumentKind::AvailabilityDomain), - AvailabilityDomainVal(domain) {} - - DiagnosticArgument(const AvailabilityRange &range) - : Kind(DiagnosticArgumentKind::AvailabilityRange), - AvailabilityRangeVal(range) {} - - DiagnosticArgument(llvm::VersionTuple version) - : Kind(DiagnosticArgumentKind::VersionTuple), - VersionVal(version) { } - - DiagnosticArgument(LayoutConstraint L) - : Kind(DiagnosticArgumentKind::LayoutConstraint), LayoutConstraintVal(L) { - } - - DiagnosticArgument(ActorIsolation AI) - : Kind(DiagnosticArgumentKind::ActorIsolation), - ActorIsolationVal(AI) { - } - - DiagnosticArgument(IsolationSource IS) - : Kind(DiagnosticArgumentKind::IsolationSource), - IsolationSourceVal(IS){ - } - - DiagnosticArgument(DiagnosticInfo *D) - : Kind(DiagnosticArgumentKind::Diagnostic), - DiagnosticVal(D) { - } - - DiagnosticArgument(const clang::NamedDecl *ND) - : Kind(DiagnosticArgumentKind::ClangDecl), ClangDecl(ND) {} - - DiagnosticArgument(const clang::Type *Ty) - : Kind(DiagnosticArgumentKind::ClangType), ClangType(Ty) {} - - /// Initializes a diagnostic argument using the underlying type of the - /// given enum. - template< - typename EnumType, - typename std::enable_if::value>::type* = nullptr> - DiagnosticArgument(EnumType value) - : DiagnosticArgument( - static_cast::type>(value)) {} - - DiagnosticArgumentKind getKind() const { return Kind; } - - StringRef getAsString() const { - assert(Kind == DiagnosticArgumentKind::String); - return StringVal; - } - - int getAsInteger() const { - assert(Kind == DiagnosticArgumentKind::Integer); - return IntegerVal; - } - - unsigned getAsUnsigned() const { - assert(Kind == DiagnosticArgumentKind::Unsigned); - return UnsignedVal; - } - - DeclNameRef getAsIdentifier() const { - assert(Kind == DiagnosticArgumentKind::Identifier); - return IdentifierVal; - } - - ObjCSelector getAsObjCSelector() const { - assert(Kind == DiagnosticArgumentKind::ObjCSelector); - return ObjCSelectorVal; - } - - const Decl *getAsDecl() const { - assert(Kind == DiagnosticArgumentKind::Decl); - return TheDecl; - } - - Type getAsType() const { - assert(Kind == DiagnosticArgumentKind::Type); - return TypeVal; - } - - TypeRepr *getAsTypeRepr() const { - assert(Kind == DiagnosticArgumentKind::TypeRepr); - return TyR; - } - - FullyQualified getAsFullyQualifiedType() const { - assert(Kind == DiagnosticArgumentKind::FullyQualifiedType); - return FullyQualifiedTypeVal; - } - - WitnessType getAsWitnessType() const { - assert(Kind == DiagnosticArgumentKind::WitnessType); - return WitnessTypeVal; - } - - DescriptivePatternKind getAsDescriptivePatternKind() const { - assert(Kind == DiagnosticArgumentKind::DescriptivePatternKind); - return DescriptivePatternKindVal; - } - - ReferenceOwnership getAsReferenceOwnership() const { - assert(Kind == DiagnosticArgumentKind::ReferenceOwnership); - return ReferenceOwnershipVal; - } - - SelfAccessKind getAsSelfAccessKind() const { - assert(Kind == DiagnosticArgumentKind::SelfAccessKind); - return SelfAccessKindVal; - } - - StaticSpellingKind getAsStaticSpellingKind() const { - assert(Kind == DiagnosticArgumentKind::StaticSpellingKind); - return StaticSpellingKindVal; - } - - DescriptiveDeclKind getAsDescriptiveDeclKind() const { - assert(Kind == DiagnosticArgumentKind::DescriptiveDeclKind); - return DescriptiveDeclKindVal; - } - - StmtKind getAsDescriptiveStmtKind() const { - assert(Kind == DiagnosticArgumentKind::DescriptiveStmtKind); - return DescriptiveStmtKindVal; - } - - const DeclAttribute *getAsDeclAttribute() const { - assert(Kind == DiagnosticArgumentKind::DeclAttribute); - return DeclAttributeVal; - } - - const AvailabilityDomain getAsAvailabilityDomain() const { - assert(Kind == DiagnosticArgumentKind::AvailabilityDomain); - return AvailabilityDomainVal; - } - - const AvailabilityRange getAsAvailabilityRange() const { - assert(Kind == DiagnosticArgumentKind::AvailabilityRange); - return AvailabilityRangeVal; - } - - llvm::VersionTuple getAsVersionTuple() const { - assert(Kind == DiagnosticArgumentKind::VersionTuple); - return VersionVal; - } - - LayoutConstraint getAsLayoutConstraint() const { - assert(Kind == DiagnosticArgumentKind::LayoutConstraint); - return LayoutConstraintVal; - } - - ActorIsolation getAsActorIsolation() const { - assert(Kind == DiagnosticArgumentKind::ActorIsolation); - return ActorIsolationVal; - } - - IsolationSource getAsIsolationSource() const { - assert(Kind == DiagnosticArgumentKind::IsolationSource); - return IsolationSourceVal; - } - - DiagnosticInfo *getAsDiagnostic() const { - assert(Kind == DiagnosticArgumentKind::Diagnostic); - return DiagnosticVal; - } - - const clang::NamedDecl *getAsClangDecl() const { - assert(Kind == DiagnosticArgumentKind::ClangDecl); - return ClangDecl; - } - - const clang::Type *getAsClangType() const { - assert(Kind == DiagnosticArgumentKind::ClangType); - return ClangType; - } - }; - /// Describes the current behavior to take with a diagnostic. /// Ordered from most severe to least. struct DiagnosticBehavior { @@ -594,6 +219,9 @@ namespace swift { const class Decl *getDecl() const { return Decl; } DiagnosticBehavior getBehaviorLimit() const { return BehaviorLimit; } + /// Retrieve the stored SourceLoc, or the location of the stored Decl. + SourceLoc getLocOrDeclLoc() const; + void setLoc(SourceLoc loc) { Loc = loc; } void setIsChildNote(bool isChildNote) { IsChildNote = isChildNote; } void setDecl(const class Decl *decl) { Decl = decl; } @@ -656,6 +284,19 @@ namespace swift { } }; + namespace detail { + /// Stores information for an active diagnostic that hasn't been emitted yet. + /// This includes both "in-flight" diagnostics as well as diagnostics queued + /// for a transaction. + struct ActiveDiagnostic { + Diagnostic Diag; + SmallVector WrappedDiagnostics; + SmallVector, 4> WrappedDiagnosticArgs; + + ActiveDiagnostic(Diagnostic diag) : Diag(std::move(diag)) {} + }; + } // namespace detail + /// A diagnostic that has no input arguments, so it is trivially-destructable. using ZeroArgDiagnostic = Diag<>; @@ -670,33 +311,40 @@ namespace swift { friend class DiagnosticEngine; DiagnosticEngine *Engine; + unsigned Idx; bool IsActive; /// Create a new in-flight diagnostic. /// /// This constructor is only available to the DiagnosticEngine. - InFlightDiagnostic(DiagnosticEngine &Engine) - : Engine(&Engine), IsActive(true) { } - + InFlightDiagnostic(DiagnosticEngine &Engine, unsigned idx) + : Engine(&Engine), Idx(idx), IsActive(true) {} + InFlightDiagnostic(const InFlightDiagnostic &) = delete; InFlightDiagnostic &operator=(const InFlightDiagnostic &) = delete; InFlightDiagnostic &operator=(InFlightDiagnostic &&) = delete; + /// Retrieve the underlying active diagnostic information. + detail::ActiveDiagnostic &getActiveDiag() const; + + /// Retrieve the underlying diagnostic. + Diagnostic &getDiag() const { return getActiveDiag().Diag; } + public: /// Create an active but unattached in-flight diagnostic. /// /// The resulting diagnostic can be used as a dummy, accepting the /// syntax to add additional information to a diagnostic without /// actually emitting a diagnostic. - InFlightDiagnostic() : Engine(0), IsActive(true) { } - + InFlightDiagnostic() : Engine(0), Idx(0), IsActive(true) {} + /// Transfer an in-flight diagnostic to a new object, which is /// typically used when returning in-flight diagnostics. InFlightDiagnostic(InFlightDiagnostic &&Other) - : Engine(Other.Engine), IsActive(Other.IsActive) { + : Engine(Other.Engine), Idx(Other.Idx), IsActive(Other.IsActive) { Other.IsActive = false; } - + ~InFlightDiagnostic() { if (IsActive) flush(); @@ -767,6 +415,23 @@ namespace swift { return limitBehaviorUntilSwiftVersion(limit, languageMode); } + /// Limit the diagnostic behavior to warning until the next future + /// language mode. + /// + /// This should be preferred over passing the next major version to + /// `warnUntilSwiftVersion` to make it easier to find and update clients + /// when a new language mode is introduced. + /// + /// This helps stage in fixes for stricter diagnostics as warnings + /// until the next major language version. + InFlightDiagnostic &warnUntilFutureSwiftVersion(); + + InFlightDiagnostic &warnUntilFutureSwiftVersionIf(bool shouldLimit) { + if (!shouldLimit) + return *this; + return warnUntilFutureSwiftVersion(); + } + /// Limit the diagnostic behavior to warning until the specified version. /// /// This helps stage in fixes for stricter diagnostics as warnings @@ -841,9 +506,6 @@ namespace swift { static const char *fixItStringFor(const FixItID id); - /// Get the best location where an 'import' fixit might be offered. - SourceLoc getBestAddImportFixItLoc(const Decl *Member) const; - /// Add a token-based replacement fix-it to the currently-active /// diagnostic. template @@ -959,6 +621,9 @@ namespace swift { /// Don't emit any warnings bool suppressWarnings = false; + + /// Don't emit any notes + bool suppressNotes = false; /// Don't emit any remarks bool suppressRemarks = false; @@ -987,7 +652,10 @@ namespace swift { /// Figure out the Behavior for the given diagnostic, taking current /// state such as fatality into account. - DiagnosticBehavior determineBehavior(const Diagnostic &diag); + DiagnosticBehavior determineBehavior(const Diagnostic &diag) const; + + /// Updates the diagnostic state for a diagnostic to emit. + void updateFor(DiagnosticBehavior behavior); bool hadAnyError() const { return anyErrorOccurred; } bool hasFatalErrorOccurred() const { return fatalErrorOccurred; } @@ -1003,6 +671,10 @@ namespace swift { void setSuppressWarnings(bool val) { suppressWarnings = val; } bool getSuppressWarnings() const { return suppressWarnings; } + /// Whether to skip emitting notes + void setSuppressNotes(bool val) { suppressNotes = val; } + bool getSuppressNotes() const { return suppressNotes; } + /// Whether to skip emitting remarks void setSuppressRemarks(bool val) { suppressRemarks = val; } bool getSuppressRemarks() const { return suppressRemarks; } @@ -1015,7 +687,7 @@ namespace swift { /// Returns a Boolean value indicating whether warnings belonging to the /// diagnostic group identified by `id` should be escalated to errors. - bool getWarningsAsErrorsForDiagGroupID(DiagGroupID id) { + bool getWarningsAsErrorsForDiagGroupID(DiagGroupID id) const { return warningsAsErrors[(unsigned)id]; } @@ -1041,9 +713,14 @@ namespace swift { ignoredDiagnostics[(unsigned)id] = ignored; } + bool isIgnoredDiagnostic(DiagID id) const { + return ignoredDiagnostics[(unsigned)id]; + } + void swap(DiagnosticState &other) { std::swap(showDiagnosticsAfterFatalError, other.showDiagnosticsAfterFatalError); std::swap(suppressWarnings, other.suppressWarnings); + std::swap(suppressNotes, other.suppressNotes); std::swap(suppressRemarks, other.suppressRemarks); std::swap(warningsAsErrors, other.warningsAsErrors); std::swap(fatalErrorOccurred, other.fatalErrorOccurred); @@ -1144,16 +821,12 @@ namespace swift { /// Tracks diagnostic behaviors and state DiagnosticState state; - /// The currently active diagnostic, if there is one. - std::optional ActiveDiagnostic; - - /// Diagnostics wrapped by ActiveDiagnostic, if any. - SmallVector WrappedDiagnostics; - SmallVector, 4> WrappedDiagnosticArgs; + /// The currently active diagnostics. + SmallVector ActiveDiagnostics; /// All diagnostics that have are no longer active but have not yet /// been emitted due to an open transaction. - SmallVector TentativeDiagnostics; + SmallVector TentativeDiagnostics; llvm::BumpPtrAllocator TransactionAllocator; /// A set of all strings involved in current transactional chain. @@ -1176,6 +849,9 @@ namespace swift { /// emitted once all transactions have closed. unsigned TransactionCount = 0; + /// The number of currently in-flight diagnostics. + unsigned NumActiveDiags = 0; + /// For batch mode, use this to know where to output a diagnostic from a /// non-primary file. It's any location in the buffer of the current primary /// input being compiled. @@ -1211,7 +887,7 @@ namespace swift { public: explicit DiagnosticEngine(SourceManager &SourceMgr) - : SourceMgr(SourceMgr), ActiveDiagnostic(), + : SourceMgr(SourceMgr), ActiveDiagnostics(), TransactionStrings(TransactionAllocator), DiagnosticStringsSaver(DiagnosticStringsAllocator) {} @@ -1230,6 +906,7 @@ namespace swift { } void flushConsumers() { + ASSERT(NumActiveDiags == 0 && "Expected in-flight diags to be flushed"); for (auto consumer : Consumers) consumer->flush(); } @@ -1240,6 +917,12 @@ namespace swift { return state.getSuppressWarnings(); } + /// Whether to skip emitting notes + void setSuppressNotes(bool val) { state.setSuppressNotes(val); } + bool getSuppressNotes() const { + return state.getSuppressNotes(); + } + /// Whether to skip emitting remarks void setSuppressRemarks(bool val) { state.setSuppressRemarks(val); } bool getSuppressRemarks() const { @@ -1287,6 +970,10 @@ namespace swift { state.setIgnoredDiagnostic(id, true); } + bool isIgnoredDiagnostic(DiagID id) const { + return state.isIgnoredDiagnostic(id); + } + void resetHadAnyError() { state.resetHadAnyError(); } @@ -1361,10 +1048,9 @@ namespace swift { /// \returns An in-flight diagnostic, to which additional information can /// be attached. InFlightDiagnostic diagnose(SourceLoc Loc, const Diagnostic &D) { - assert(!ActiveDiagnostic && "Already have an active diagnostic"); - ActiveDiagnostic = D; - ActiveDiagnostic->setLoc(Loc); - return InFlightDiagnostic(*this); + auto IFD = beginDiagnostic(D); + getActiveDiagnostic(IFD).Diag.setLoc(Loc); + return IFD; } /// Emit a diagnostic with the given set of diagnostic arguments. @@ -1440,10 +1126,9 @@ namespace swift { /// \returns An in-flight diagnostic, to which additional information can /// be attached. InFlightDiagnostic diagnose(const Decl *decl, const Diagnostic &diag) { - assert(!ActiveDiagnostic && "Already have an active diagnostic"); - ActiveDiagnostic = diag; - ActiveDiagnostic->setDecl(decl); - return InFlightDiagnostic(*this); + auto IFD = beginDiagnostic(diag); + getActiveDiagnostic(IFD).Diag.setDecl(decl); + return IFD; } /// Emit a diagnostic with the given set of diagnostic arguments. @@ -1497,16 +1182,21 @@ namespace swift { DiagnosticFormatOptions FormatOpts = DiagnosticFormatOptions()); private: + /// Begins a new in-flight diagnostic. + InFlightDiagnostic beginDiagnostic(const Diagnostic &D); + + /// Ends an in-flight diagnostic. Once all in-flight diagnostics have ended, + /// they will either be emitted, or captured by an open transaction. + void endDiagnostic(const InFlightDiagnostic &D); + /// Called when tentative diagnostic is about to be flushed, /// to apply any required transformations e.g. copy string arguments /// to extend their lifetime. void onTentativeDiagnosticFlush(Diagnostic &diagnostic); - /// Flush the active diagnostic. - void flushActiveDiagnostic(); - - /// Retrieve the active diagnostic. - Diagnostic &getActiveDiagnostic() { return *ActiveDiagnostic; } + /// Retrieve the stored active diagnostic for a given InFlightDiagnostic. + detail::ActiveDiagnostic & + getActiveDiagnostic(const InFlightDiagnostic &diag); /// Generate DiagnosticInfo for a Diagnostic to be passed to consumers. std::optional @@ -1522,7 +1212,7 @@ namespace swift { /// Handle a new diagnostic, which will either be emitted, or added to an /// active transaction. - void handleDiagnostic(Diagnostic &&diag); + void handleDiagnostic(detail::ActiveDiagnostic &&diag); /// Clear any tentative diagnostics. void clearTentativeDiagnostics(); @@ -1561,11 +1251,7 @@ namespace swift { SourceLoc getDefaultDiagnosticLoc() const { return bufferIndirectlyCausingDiagnostic; } - SourceLoc getBestAddImportFixItLoc(const Decl *Member, - SourceFile *sourceFile) const; - SourceLoc getBestAddImportFixItLoc(const Decl *Member) const { - return getBestAddImportFixItLoc(Member, nullptr); - } + SourceLoc getBestAddImportFixItLoc(SourceFile *sf) const; }; inline SourceManager &InFlightDiagnostic::getSourceManager() { @@ -1655,12 +1341,12 @@ namespace swift { } bool hasErrors() const { - ArrayRef diagnostics(Engine.TentativeDiagnostics.begin() + - PrevDiagnostics, - Engine.TentativeDiagnostics.end()); + ArrayRef diagnostics( + Engine.TentativeDiagnostics.begin() + PrevDiagnostics, + Engine.TentativeDiagnostics.end()); for (auto &diagnostic : diagnostics) { - auto behavior = Engine.state.determineBehavior(diagnostic); + auto behavior = Engine.state.determineBehavior(diagnostic.Diag); if (behavior == DiagnosticBehavior::Fatal || behavior == DiagnosticBehavior::Error) return true; @@ -1725,14 +1411,14 @@ namespace swift { // The first diagnostic is assumed to be the parent. If this is not an // error or warning, we'll assert later when trying to add children. - Diagnostic &parent = Engine.TentativeDiagnostics[PrevDiagnostics]; + Diagnostic &parent = Engine.TentativeDiagnostics[PrevDiagnostics].Diag; // Associate the children with the parent. for (auto diag = Engine.TentativeDiagnostics.begin() + PrevDiagnostics + 1; diag != Engine.TentativeDiagnostics.end(); ++diag) { - diag->setIsChildNote(true); - parent.addChildNote(std::move(*diag)); + diag->Diag.setIsChildNote(true); + parent.addChildNote(std::move(diag->Diag)); } // Erase the children, they'll be emitted alongside their parent. @@ -1792,6 +1478,13 @@ namespace swift { /// Retrieve the underlying engine which will receive the diagnostics. DiagnosticEngine &getUnderlyingDiags() const { return UnderlyingEngine; } + /// Iterates over each captured diagnostic, running a lambda with it. + void forEach(llvm::function_ref body) const; + + /// Filters the queued diagnostics, dropping any where the predicate + /// returns \c false. + void filter(llvm::function_ref predicate); + /// Clear this queue and erase all diagnostics recorded. void clear() { assert(QueueEngine.TransactionCount == 1 && diff --git a/include/swift/AST/DiagnosticGroups.def b/include/swift/AST/DiagnosticGroups.def index 0b93805b8f417..4f55336d1ed97 100644 --- a/include/swift/AST/DiagnosticGroups.def +++ b/include/swift/AST/DiagnosticGroups.def @@ -12,6 +12,24 @@ // // This file defines diagnostic groups and links between them. // +// The GROUP(Name, Filename) macro is used to define each diagnostic group. +// These groups are used to associate documentation with particular set of +// diagnostics and also to provide more control over whether the warnings +// in the group are escalated to errors (via the -Wwarning and -Werror flags.) +// A given warning or error can be associated with a diagnostic group by using +// GROUPED_WARNING or GROUPED_ERROR, respectively. New warnings and errors +// should be introduced along with groups to improve documentation. +// +// - Name: the name of the group that will be shown in diagnostic messages +// (e.g., StrictMemorySafety) and used in command-line options like +// -Wwarning and -Werror. Once assigned, the group name for a particular +// diagnostic should not be changed, because it will affect existing users +// of the command-line flags. +// - Filename: the name of the file that provides documentation for this +// diagnostic group. There should be a corresponding Markdown file in +// userdocs/diagnostics providing short-form documentation that helps +// explain the diagnostic and how to resolve the problem. +// //===----------------------------------------------------------------------===// #define DEFINE_DIAGNOSTIC_GROUPS_MACROS @@ -22,31 +40,50 @@ GROUP(no_group, "") -GROUP(ActorIsolatedCall, "actor-isolated-call.md") -GROUP(AsyncCallerExecution, "async-caller-execution.md") -GROUP(ConformanceIsolation, "conformance-isolation.md") -GROUP(DeprecatedDeclaration, "deprecated-declaration.md") -GROUP(DynamicCallable, "dynamic-callable-requirements.md") -GROUP(ErrorInFutureSwiftVersion, "error-in-future-swift-version.md") -GROUP(ExistentialAny, "existential-any.md") -GROUP(ExistentialMemberAccess, "existential-member-access-limitations.md") -GROUP(IsolatedConformances, "isolated-conformances.md") -GROUP(MultipleInheritance, "multiple-inheritance.md") -GROUP(MutableGlobalVariable, "mutable-global-variable.md") -GROUP(NominalTypes, "nominal-types.md") -GROUP(OpaqueTypeInference, "opaque-type-inference.md") -GROUP(PreconcurrencyImport, "preconcurrency-import.md") -GROUP(PropertyWrappers, "property-wrapper-requirements.md") -GROUP(ProtocolTypeNonConformance, "protocol-type-non-conformance.md") -GROUP(ResultBuilderMethods, "result-builder-methods.md") -GROUP(SendableClosureCaptures, "sendable-closure-captures.md") -GROUP(SendingRisksDataRace, "sending-risks-data-race.md") -GROUP(StrictLanguageFeatures, "strict-language-features.md") -GROUP(StrictMemorySafety, "strict-memory-safety.md") -GROUP(StringInterpolationConformance, "string-interpolation-conformance.md") -GROUP(TemporaryPointers, "temporary-pointers.md") -GROUP(TrailingClosureMatching, "trailing-closure-matching.md") -GROUP(UnknownWarningGroup, "unknown-warning-group.md") +GROUP(ActorIsolatedCall, "actor-isolated-call") +GROUP(AlwaysAvailableDomain, "always-available-domain") +GROUP(AvailabilityUnrecognizedName, "availability-unrecognized-name") +GROUP(ClangDeclarationImport, "clang-declaration-import") +GROUP(ConformanceIsolation, "conformance-isolation") +GROUP(DeprecatedDeclaration, "deprecated-declaration") +GROUP(DynamicCallable, "dynamic-callable-requirements") +GROUP(EmbeddedRestrictions, "embedded-restrictions") +GROUP(ErrorInFutureSwiftVersion, "error-in-future-swift-version") +GROUP(ExclusivityViolation, "exclusivity-violation") +GROUP(ExistentialAny, "existential-any") +GROUP(ExistentialMemberAccess, "existential-member-access-limitations") +GROUP(ImplementationOnlyDeprecated, "implementation-only-deprecated") +GROUP(IsolatedConformances, "isolated-conformances") +GROUP(MemberImportVisibility, "member-import-visibility") +GROUP(MissingModuleOnKnownPaths, "missing-module-on-known-paths") +GROUP(ModuleNotTestable, "module-not-testable") +GROUP(ModuleVersionMissing, "module-version-missing") +GROUP(MultipleInheritance, "multiple-inheritance") +GROUP(MutableGlobalVariable, "mutable-global-variable") +GROUP(NominalTypes, "nominal-types") +GROUP(NonisolatedNonsendingByDefault, "nonisolated-nonsending-by-default") +GROUP(OpaqueTypeInference, "opaque-type-inference") +GROUP(PreconcurrencyImport, "preconcurrency-import") +GROUP(PropertyWrappers, "property-wrapper-requirements") +GROUP(ProtocolTypeNonConformance, "protocol-type-non-conformance") +GROUP(ResultBuilderMethods, "result-builder-methods") +GROUP(SendableClosureCaptures, "sendable-closure-captures") +GROUP(SendableMetatypes, "sendable-metatypes") +GROUP(SendingRisksDataRace, "sending-risks-data-race") +GROUP(StrictLanguageFeatures, "strict-language-features") +GROUP(StrictMemorySafety, "strict-memory-safety") +GROUP(StringInterpolationConformance, "string-interpolation-conformance") +GROUP(TemporaryPointers, "temporary-pointers") +GROUP(TrailingClosureMatching, "trailing-closure-matching") +GROUP(UnknownWarningGroup, "unknown-warning-group") +GROUP(CompilationCaching, "compilation-caching") +GROUP(WeakMutability, "weak-mutability") + +GROUP(PerformanceHints, "performance-hints") +GROUP(ReturnTypeImplicitCopy, "return-type-implicit-copy") +GROUP_LINK(PerformanceHints, ReturnTypeImplicitCopy) +GROUP(ExistentialType, "existential-type") +GROUP_LINK(PerformanceHints, ExistentialType) #define UNDEFINE_DIAGNOSTIC_GROUPS_MACROS #include "swift/AST/DefineDiagnosticGroupsMacros.h" diff --git a/include/swift/AST/DiagnosticKind.h b/include/swift/AST/DiagnosticKind.h new file mode 100644 index 0000000000000..f973a6ea7d8e8 --- /dev/null +++ b/include/swift/AST/DiagnosticKind.h @@ -0,0 +1,35 @@ +//===-- AST/DiagnosticKind.h ------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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_AST_DIAGNOSTIC_KIND_H +#define SWIFT_AST_DIAGNOSTIC_KIND_H + +/// `DiagnosticKind.h` is imported into Swift. Be *very* careful with what you +/// include here and keep these includes minimal! +/// +/// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" +#include + +namespace swift { + +/// Describes the kind of diagnostic. +enum class ENUM_EXTENSIBILITY_ATTR(open) DiagnosticKind : uint8_t { + Error SWIFT_NAME("error"), + Warning SWIFT_NAME("warning"), + Remark SWIFT_NAME("remark"), + Note SWIFT_NAME("note") +}; + +} // namespace swift + +#endif // SWIFT_AST_DIAGNOSTIC_KIND_H diff --git a/include/swift/AST/DiagnosticList.h b/include/swift/AST/DiagnosticList.h index a360593df617d..06d64ce6278ad 100644 --- a/include/swift/AST/DiagnosticList.h +++ b/include/swift/AST/DiagnosticList.h @@ -17,8 +17,12 @@ #ifndef SWIFT_DIAGNOSTICLIST_H #define SWIFT_DIAGNOSTICLIST_H -#include -#include +/// `DiagnosticList.h` is imported into Swift. Be *very* careful with what you +/// include here and keep these includes minimal! +/// +/// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" +#include namespace swift { @@ -26,7 +30,7 @@ namespace swift { /// /// Each of the diagnostics described in Diagnostics.def has an entry in /// this enumeration type that uniquely identifies it. -enum class DiagID : uint32_t { +enum class ENUM_EXTENSIBILITY_ATTR(open) DiagID : uint32_t { #define DIAG(KIND, ID, Group, Options, Text, Signature) ID, #include "swift/AST/DiagnosticsAll.def" NumDiagsHandle @@ -34,10 +38,9 @@ enum class DiagID : uint32_t { static_assert(static_cast(swift::DiagID::invalid_diagnostic) == 0, "0 is not the invalid diagnostic ID"); -constexpr auto NumDiagIDs = - static_cast>(DiagID::NumDiagsHandle); +constexpr auto NumDiagIDs = static_cast(DiagID::NumDiagsHandle); -enum class FixItID : uint32_t { +enum class ENUM_EXTENSIBILITY_ATTR(open) FixItID : uint32_t { #define DIAG(KIND, ID, Group, Options, Text, Signature) #define FIXIT(ID, Text, Signature) ID, #include "swift/AST/DiagnosticsAll.def" diff --git a/include/swift/AST/DiagnosticsClangImporter.def b/include/swift/AST/DiagnosticsClangImporter.def index d6f27a807cbcb..2cf1d185f14a9 100644 --- a/include/swift/AST/DiagnosticsClangImporter.def +++ b/include/swift/AST/DiagnosticsClangImporter.def @@ -51,10 +51,10 @@ ERROR(emit_pcm_error,Fatal, ERROR(dump_pcm_error,Fatal, "failed to dump precompiled module '%0'", (StringRef)) -WARNING(invalid_swift_name_method,none, +GROUPED_WARNING(invalid_swift_name_method, ClangDeclarationImport, none, "too %select{few|many}0 parameters in swift_name attribute (expected %1; " "got %2)", (bool, unsigned, unsigned)) -WARNING(invalid_swift_name_for_decl,none, +GROUPED_WARNING(invalid_swift_name_for_decl, ClangDeclarationImport, none, "custom Swift name '%0' ignored because it is not valid for %kindonly1; " "imported as %1 instead", (StringRef, ValueDecl *)) @@ -67,20 +67,25 @@ ERROR(swift_name_protocol_static, none, "swift_name cannot be used to define " ERROR(swift_name_no_prototype, none, "swift_name cannot be used on a non-prototyped function declaration", ()) -WARNING(inconsistent_swift_name,none, +GROUPED_WARNING(inconsistent_swift_name, ClangDeclarationImport, none, "inconsistent Swift name for Objective-C %select{method|property}0 " "'%1' in '%2' (%3 in '%4' vs. %5 in '%6')", (bool, StringRef, StringRef, DeclName, StringRef, DeclName, StringRef)) -WARNING(swift_name_circular_context_import,none, +WARNING(swift_name_attr_ignored, none, + "ignoring swift_name attribute %0; swift_name attributes have no " + "effect on method overrides", + (StringRef)) + +GROUPED_WARNING(swift_name_circular_context_import, ClangDeclarationImport, none, "cycle detected while resolving '%0' in swift_name attribute for '%1'", (StringRef, StringRef)) NOTE(swift_name_circular_context_import_other,none, "while resolving '%0' in swift_name attribute for '%1'", (StringRef, StringRef)) -WARNING(unresolvable_clang_decl,none, +GROUPED_WARNING(unresolvable_clang_decl, ClangDeclarationImport, none, "imported declaration '%0' could not be mapped to '%1'", (StringRef, StringRef)) @@ -88,38 +93,41 @@ NOTE(unresolvable_clang_decl_is_a_framework_bug,none, "please report this issue to the owners of '%0'", (StringRef)) -WARNING(clang_swift_attr_unhandled,none, +GROUPED_WARNING(clang_swift_attr_unhandled, ClangDeclarationImport, none, "ignoring unknown Swift attribute or modifier '%0'", (StringRef)) -WARNING(clang_error_code_must_be_sendable,none, - "cannot make error code type '%0' non-sendable because Swift errors " +GROUPED_WARNING(clang_error_code_must_be_sendable, ClangDeclarationImport, none, + "cannot make error code type '%0' non-Sendable because Swift errors " "are always sendable", (StringRef)) -WARNING(clang_ignored_sendable_attr,none, +GROUPED_WARNING(clang_ignored_sendable_attr, ClangDeclarationImport, none, "cannot make type %0 sendable because '@Sendable' and '& Sendable' " "cannot be added to it", (Type)) -NOTE(clang_param_should_be_implicitly_sendable,none, - "parameter should be implicitly 'Sendable' because it is a completion " - "handler", ()) + +GROUPED_WARNING(warn_clang_ignored_bounds_on_self, ClangDeclarationImport, none, + "bounds attribute '%0' ignored on parameter mapped to 'self'", + (StringRef)) +NOTE(note_swift_name_instance_method, none, + "swift_name maps free function to instance method here", ()) WARNING(implicit_bridging_header_imported_from_module,none, "implicit import of bridging header '%0' via module %1 " "is deprecated and will be removed in a later version of Swift", (StringRef, Identifier)) -WARNING(import_multiple_mainactor_attr,none, +GROUPED_WARNING(import_multiple_mainactor_attr, ClangDeclarationImport, none, "this attribute for global actor '%0' is invalid; the declaration already has attribute for global actor '%1'", (StringRef, StringRef)) -WARNING(contradicting_mutation_attrs,none, +GROUPED_WARNING(contradicting_mutation_attrs, ClangDeclarationImport, none, "attribute '%0' is ignored when combined with attribute '%1'", (StringRef, StringRef)) -WARNING(nonmutating_without_const,none, +GROUPED_WARNING(nonmutating_without_const, ClangDeclarationImport, none, "attribute 'nonmutating' has no effect on non-const method", ()) -WARNING(nonmutating_without_mutable_fields,none, +GROUPED_WARNING(nonmutating_without_mutable_fields, ClangDeclarationImport, none, "attribute 'nonmutating' has no effect without any mutable fields", ()) ERROR(module_map_not_found, none, "module map file '%0' not found", (StringRef)) @@ -135,7 +143,7 @@ WARNING(libstdcxx_modulemap_not_found, none, "module map for libstdc++ not found for '%0'; C++ stdlib may be unavailable", (StringRef)) -WARNING(api_pattern_attr_ignored, none, +GROUPED_WARNING(api_pattern_attr_ignored, ClangDeclarationImport, none, "'%0' Swift attribute ignored on type '%1': type is not copyable or destructible", (StringRef, StringRef)) @@ -226,7 +234,7 @@ ERROR(private_fileid_attr_repeated, none, NOTE(private_fileid_attr_here, none, "SWIFT_PRIVATE_FILEID annotation found here", ()) - WARNING(private_fileid_attr_format_invalid, none, + GROUPED_WARNING(private_fileid_attr_format_invalid, ClangDeclarationImport, none, "SWIFT_PRIVATE_FILEID annotation on '%0' does not have a valid file ID", (StringRef)) REMARK(private_fileid_attr_format_specification, none, @@ -259,15 +267,18 @@ ERROR(foreign_reference_types_invalid_retain_release, none, ERROR(foreign_reference_types_retain_non_void_or_self_return_type, none, "specified retain function '%0' is invalid; " - "retain function must have 'void' or parameter return type", + "retain function must either return have 'void', the reference count as an integer, or the parameter type", (StringRef)) ERROR(foreign_reference_types_release_non_void_return_type, none, "specified release function '%0' is invalid; " - "release function must have 'void' return type", + "release function must either return 'void' or the reference count as an integer", (StringRef)) ERROR(foreign_reference_types_retain_release_not_a_function_decl, none, "specified %select{retain|release}0 function '%1' is not a function", (bool, StringRef)) +ERROR(foreign_reference_types_retain_release_not_an_instance_function, none, + "specified %select{retain|release}0 function '%1' is a static function; expected an instance function", + (bool, StringRef)) ERROR(conforms_to_missing_dot, none, "expected module name and protocol name separated by '.' in protocol " "conformance; '%0' is invalid", @@ -282,7 +293,8 @@ ERROR(conforms_to_ambiguous,none, "ambiguous reference to protocol '%0' in specified protocol conformance; module '%1' contains multiple protocols named '%0'", (StringRef, StringRef)) ERROR(conforms_to_not_protocol,none, - "%0 %1 referenced in protocol conformance '%2' is not a protocol", (DescriptiveDeclKind, ValueDecl *, StringRef)) + "%kind0 referenced in protocol conformance '%1' is not a protocol", + (const ValueDecl *, StringRef)) ERROR(failed_base_method_call_synthesis,none, "failed to synthesize call to the base method %0 of type %0", @@ -293,21 +305,16 @@ ERROR(both_returns_retained_returns_unretained, none, "SWIFT_RETURNS_UNRETAINED", (const clang::NamedDecl *)) -ERROR(returns_retained_or_returns_unretained_for_non_cxx_frt_values, none, - "%0 cannot be annotated with either SWIFT_RETURNS_RETAINED or " +ERROR(redundant_conformance_protocol,none, + "redundant conformance of %0 to protocol '%1'", (Type, StringRef)) + +WARNING(returns_retained_or_returns_unretained_for_non_cxx_frt_values, none, + "%0 should not be annotated with SWIFT_RETURNS_RETAINED or " "SWIFT_RETURNS_UNRETAINED because it is not returning " "a SWIFT_SHARED_REFERENCE type", (const clang::NamedDecl *)) -// TODO: In the next C++ interop version, convert this warning into an error and -// stop importing unannotated C++ APIs that return SWIFT_SHARED_REFERENCE. -// rdar://138806722 -WARNING(no_returns_retained_returns_unretained, none, - "%0 should be annotated with either SWIFT_RETURNS_RETAINED or " - "SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE", - (const clang::NamedDecl *)) - -WARNING(returns_retained_returns_unretained_on_overloaded_operator, none, +GROUPED_WARNING(returns_retained_returns_unretained_on_overloaded_operator, ClangDeclarationImport, none, "SWIFT_RETURNS_RETAINED and SWIFT_RETURNS_UNRETAINED is not supported " "yet for overloaded C++ %0. Overloaded C++ operators always " "return " @@ -318,7 +325,7 @@ WARNING(returns_retained_returns_unretained_on_overloaded_operator, none, // stop importing C++ types that inherit from SWIFT_SHARED_REFERENCE if the // Swift compiler cannot find unique retain/release functions. // rdar://145194375 -WARNING(cant_infer_frt_in_cxx_inheritance, none, +GROUPED_WARNING(cant_infer_frt_in_cxx_inheritance, ClangDeclarationImport, none, "unable to infer SWIFT_SHARED_REFERENCE for %0, although one of its " "transitive base types is marked as SWIFT_SHARED_REFERENCE", (const clang::NamedDecl *)) @@ -351,8 +358,12 @@ NOTE(forward_declared_protocol_clashes_with_imported_objc_Swift_protocol, none, "its name conflicts with a %1 in module %2", (const clang::NamedDecl*, StringRef, StringRef)) -WARNING(return_escapable_with_lifetimebound, none, "the returned type '%0' is annotated as escapable; it cannot have lifetime dependencies", (StringRef)) -WARNING(return_nonescapable_without_lifetimebound, none, "the returned type '%0' is annotated as non-escapable; its lifetime dependencies must be annotated", (StringRef)) +GROUPED_WARNING(return_escapable_with_lifetimebound, ClangDeclarationImport, none, + "the returned type '%0' is annotated as escapable; it cannot have lifetime dependencies", + (StringRef)) +GROUPED_WARNING(return_nonescapable_without_lifetimebound, ClangDeclarationImport, none, + "the returned type '%0' is annotated as non-escapable; its lifetime dependencies must be annotated", + (StringRef)) ERROR(unknown_template_parameter,none, "template parameter '%0' does not exist", (StringRef)) @@ -373,5 +384,9 @@ NOTE(ptr_to_nonescapable,none, "pointer to non-escapable type %0 cannot be imported", (const clang::Type*)) +NOTE(nonescapable_field_of_escapable, none, + "escapable record %0 cannot have non-escapable field '%1'", + (const clang::NamedDecl *, StringRef)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index 14c2e9b76cc31..ffa723d4d2c10 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -23,6 +23,13 @@ ERROR(invalid_diagnostic,none, "INTERNAL ERROR: this diagnostic should not be produced", ()) +NOTE(kind_declname_declared_here,none, + "%0 %1 declared here", (DescriptiveDeclKind, DeclName)) +NOTE(decl_declared_here_with_kind,none, + "%kind0 declared here", (const Decl *)) +NOTE(through_decl_declared_here_with_kind,none, + "through %kind0 declared here", (const Decl *)) + ERROR(not_implemented,none, "INTERNAL ERROR: feature not implemented: %0", (StringRef)) @@ -87,7 +94,7 @@ ERROR(require_literal_initializer_for_literal,none, "_const let should be initialized with a literal value", ()) ERROR(require_const_initializer_for_const,none, - "@const value should be initialized with a compile-time value", ()) + "'@const' value should be initialized with a compile-time value", ()) ERROR(require_const_arg_for_parameter,none, "expected a compile-time value argument for a '@const' parameter", ()) @@ -176,9 +183,6 @@ ERROR(circular_enum_inheritance,none, ERROR(circular_protocol_def,none, "protocol %0 refines itself", (Identifier)) -NOTE(kind_declname_declared_here,none, - "%0 %1 declared here", (DescriptiveDeclKind, DeclName)) - WARNING(warn_property_wrapper_module_scope,none, "ignoring associated type %0 in favor of module-scoped property " "wrapper %0; please qualify the reference with %1", @@ -212,6 +216,9 @@ ERROR(scanner_find_cycle, none, NOTE(scanner_find_cycle_swift_overlay_path, none, "Swift Overlay dependency of '%0' on '%1' via Clang module dependency: '%2'", (StringRef, StringRef, StringRef)) +NOTE(scanner_cycle_source_target_shadow_module, none, + "source target '%0' shadowing a%select{ |n SDK }2Swift module with the same name at: '%1'", (StringRef, StringRef, bool)) + ERROR(scanner_arguments_invalid, none, "dependencies scanner cannot be configured with arguments: '%0'", (StringRef)) @@ -251,8 +258,10 @@ REMARK(remark_scanner_invalidate_missing_cas, none, // MARK: custom attribute diagnostics //------------------------------------------------------------------------------ -ERROR(unknown_attribute,none, +ERROR(unknown_attr_name,none, "unknown attribute '%0'", (StringRef)) +ERROR(unknown_type_attr,none, + "unknown %kind0", (const TypeAttribute *)) //------------------------------------------------------------------------------ // MARK: macro diagnostics @@ -270,6 +279,13 @@ ERROR(macro_experimental,none, ERROR(lookup_outputs_dont_match,none, "Unqualified lookup output from ASTScope and SwiftLexicalLookup don't match", ()) +//------------------------------------------------------------------------------ +// MARK: Accessor diagnostics +//------------------------------------------------------------------------------ + +ERROR(accessor_not_supported_in_decl,none, + "%0 is supported only on a struct or enum", (StringRef)) + //------------------------------------------------------------------------------ // MARK: bridged diagnostics //------------------------------------------------------------------------------ diff --git a/include/swift/AST/DiagnosticsCommon.h b/include/swift/AST/DiagnosticsCommon.h index 80d0c725ffe46..afde6bf6820f3 100644 --- a/include/swift/AST/DiagnosticsCommon.h +++ b/include/swift/AST/DiagnosticsCommon.h @@ -24,6 +24,13 @@ #include "swift/Config.h" namespace swift { + class AccessorDecl; + class ConstructorDecl; + class MacroDecl; + class SubscriptDecl; + class SwitchStmt; + class TypeAliasDecl; + template struct Diag; @@ -48,6 +55,7 @@ namespace swift { } // end namespace detail enum class StaticSpellingKind : uint8_t; + enum class ForeignLanguage : uint8_t; namespace diag { diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index c98ad2af6939c..957a626fac49b 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -114,9 +114,6 @@ WARNING(warn_opt_remark_disabled, none, "requires a single compiler invocation: consider enabling the " "-whole-module-optimization flag", ()) -WARNING(warn_ignoring_batch_mode,none, -"ignoring '-enable-batch-mode' because '%0' was also specified", (StringRef)) - WARNING(warn_ignoring_wmo, none, "ignoring '-wmo' because '-dump-ast' was also specified", ()) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 41736350078fa..c2ebf1b73e9bb 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -59,9 +59,9 @@ GROUPED_WARNING(cannot_disable_feature_with_mode, StrictLanguageFeatures, none, "'%0' argument '%1' cannot specify a mode", (StringRef, StringRef)) -GROUPED_WARNING(feature_does_not_support_adoption_mode, StrictLanguageFeatures, +GROUPED_WARNING(feature_does_not_support_migration_mode, StrictLanguageFeatures, none, - "feature '%0' does not support adoption mode", + "feature '%0' does not support migration mode", (StringRef)) ERROR(error_unknown_library_level, none, @@ -126,8 +126,6 @@ WARNING(warning_locale_path_not_found,none, "translation is disabled", (StringRef)) WARNING(warning_cannot_find_locale_file,none, "cannot find translations for '%0' at '%1': no such file", (StringRef, StringRef)) -WARNING(warning_cannot_multithread_batch_mode,none, - "ignoring -num-threads argument; cannot multithread batch mode", ()) ERROR(error_cannot_explicit_interface_build_in_mode,none, "'-explicit-interface-module-build' only supported when building a module from interface ('-compile-module-from-interface' or '-typecheck-module-from-interface')'", ()) ERROR(error_unsupported_option_argument,none, @@ -158,6 +156,9 @@ ERROR(error_load_resolved_plugin,none, NOTE(note_valid_swift_versions, none, "valid arguments to '-swift-version' are %0", (StringRef)) +ERROR(error_module_interface_requires_language_mode,none, + "emitting module interface files requires '-language-mode'", ()) + ERROR(error_mode_cannot_emit_dependencies,none, "this mode does not support emitting dependency files", ()) ERROR(error_mode_cannot_emit_reference_dependencies,none, @@ -193,10 +194,12 @@ ERROR(cannot_emit_ir_skipping_function_bodies,none, WARNING(emit_reference_dependencies_without_primary_file,none, "ignoring -emit-reference-dependencies (requires -primary-file)", ()) +WARNING(ignoring_option_obsolete,none, + "ignoring '%0'; this option is obsolete", (StringRef)) WARNING(ignoring_option_requires_option,none, "ignoring %0 (requires %1)", (StringRef, StringRef)) -WARNING(warn_ignore_option_overriden_by,none, - "ignoring %0 (overriden by %1)", (StringRef, StringRef)) +WARNING(warn_ignore_option_overridden_by,none, + "ignoring %0 (overridden by %1)", (StringRef, StringRef)) WARNING(warn_implicit_concurrency_import_failed,none, "unable to perform implicit import of \"_Concurrency\" module: no such module found", ()) @@ -211,6 +214,9 @@ ERROR(error_stdlib_module_name,none, "module name \"%0\" is reserved for the standard library" "%select{|; use -module-name flag to specify an alternate name}1", (StringRef, bool)) +WARNING(warn_multiple_module_inputs_same_name,none, + "multiple Swift module file inputs with identifier \"%0\": replacing '%1' with '%2'", + (StringRef, StringRef, StringRef)) ERROR(error_bad_export_as_name,none, "export-as name \"%0\" is not a valid identifier", @@ -357,16 +363,8 @@ ERROR(explicit_swift_module_map_missing,none, (StringRef)) ERROR(explicit_swift_module_map_corrupted,none, - "explicit Swift module map from %0 is malformed", - (StringRef)) - -ERROR(placeholder_dependency_module_map_missing,none, - "cannot open Swift placeholder dependency module map from %0", - (StringRef)) - -ERROR(placeholder_dependency_module_map_corrupted,none, - "Swift placeholder dependency module map from %0 is malformed", - (StringRef)) + "explicit Swift module map from %0 is malformed: %1", + (StringRef, StringRef)) ERROR(const_extract_protocol_list_input_file_missing,none, "cannot open constant extraction protocol list input file from %0", @@ -517,23 +515,35 @@ REMARK(matching_output_produced,none, "produced matching output file '%0' for deterministic check: hash '%1'", (StringRef, StringRef)) // Caching related diagnostics -ERROR(error_caching_no_cas_fs, none, - "caching is enabled without -cas-fs option, input is not immutable", ()) +GROUPED_ERROR(error_caching_no_cas_fs, CompilationCaching, none, + "caching is enabled without CAS file-system options, input is not immutable", ()) ERROR(error_prefix_mapping, none, "cannot create scanner prefix mapping: '%0'", (StringRef)) REMARK(replay_output, none, "replay output file '%0': key '%1'", (StringRef, StringRef)) REMARK(output_cache_miss, none, "cache miss for input file '%0': key '%1'", (StringRef, StringRef)) // CAS related diagnostics -ERROR(error_cas, none, "CAS error encountered: %0", (StringRef)) -WARNING(cache_replay_failed, none, "cache replay failed: %0", (StringRef)) +GROUPED_ERROR(error_invalid_cas_id, CompilationCaching, none, "CAS cannot parse id '%0': %1", (StringRef, StringRef)) +GROUPED_ERROR(error_cas, CompilationCaching, none, "CAS error encountered during %0: %1", (StringRef, StringRef)) +GROUPED_ERROR(error_cas_fs_creation, CompilationCaching, none, "cannot create CAS filesystem: %0", (StringRef)) +GROUPED_ERROR(error_cache_key_creation, CompilationCaching, none, "cannot create cache key for compilation %0: %1", (StringRef, StringRef)) +GROUPED_ERROR(error_cas_file_ref, CompilationCaching, none, "cannot load file %0 from CAS filesystem", (StringRef)) +GROUPED_ERROR(error_cas_conflict_options, CompilationCaching, none, "cannot setup CAS due to conflicting '-cas-*' options", ()) +GROUPED_ERROR(error_cas_initialization, CompilationCaching, none, "CAS cannot be initialized from the specified '-cas-*' options: %0", (StringRef)) +GROUPED_ERROR(error_cas_malformed_input, CompilationCaching, none, "CAS input '%0' is malformed: %1", (StringRef, StringRef)) +GROUPED_WARNING(cache_replay_failed, CompilationCaching, none, "cache replay failed: %0", (StringRef)) + +GROUPED_ERROR(error_failed_cached_diag, CompilationCaching, none, "failed to serialize cached diagnostics: %0", (StringRef)) +GROUPED_ERROR(error_replay_cached_diag, CompilationCaching, none, "failed to replay cached diagnostics: %0", (StringRef)) + +GROUPED_ERROR(error_load_input_from_cas, CompilationCaching, none, "failed to load input '%0' from CAS", (StringRef)) -ERROR(error_failed_cached_diag, none, "failed to serialize cached diagnostics: %0", (StringRef)) -ERROR(error_replay_cached_diag, none, "failed to replay cached diagnostics: %0", (StringRef)) +GROUPED_ERROR(error_wrong_input_num_for_input_file_key, CompilationCaching, none, "-input-file-key only support one input file", ()) -ERROR(error_load_input_from_cas, none, "failed to load input '%0' from CAS", (StringRef)) +ERROR(error_gen_reproducer_not_caching, none, "-gen-reproducer only supports swift caching (-cache-compile-job)", ()) +ERROR(error_cannot_create_reproducer_dir, none, "failed to create reproducer director '%0': %1", (StringRef, StringRef)) -ERROR(error_wrong_input_num_for_input_file_key, none, "-input-file-key only support one input file", ()) +NOTE(note_reproducer, none, "reproducer is available at: %0", (StringRef)) // Dependency Verifier Diagnostics ERROR(missing_member_dependency,none, @@ -558,9 +568,6 @@ WARNING(module_incompatible_with_skip_function_bodies,none, "-experimental-skip-*-function-bodies* flags; they have " "been automatically disabled", (StringRef)) -WARNING(access_notes_file_io_error,none, - "ignored access notes file at '%0' because it cannot be read: %1", - (StringRef, StringRef)) WARNING(error_in_access_notes_file,none, "ignored access notes file because it cannot be parsed: %0", (StringRef)) @@ -594,12 +601,17 @@ ERROR(objc_with_embedded,none, "Objective-C interoperability cannot be enabled with embedded Swift.", ()) ERROR(no_allocations_without_embedded,none, "-no-allocations is only applicable with embedded Swift.", ()) +ERROR(min_ptr_value_without_embedded,none, + "-min-valid-pointer-value is only applicable with embedded Swift.", ()) ERROR(no_swift_sources_with_embedded,none, "embedded swift cannot be enabled in a compiler built without Swift sources", ()) ERROR(package_cmo_requires_library_evolution, none, "Library evolution must be enabled for Package CMO", ()) +WARNING(internal_bridging_header_without_library_evolution,none, + "using internal bridging headers without library evolution can cause instability", ()) + ERROR(experimental_not_supported_in_production,none, "experimental feature '%0' cannot be enabled in production compiler", (StringRef)) @@ -618,5 +630,17 @@ ERROR(ast_format_requires_dump_ast,none, ERROR(unknown_dump_ast_format,none, "unknown format '%0' requested with '-dump-ast-format'", (StringRef)) +ERROR(dependency_scan_unexpected_variant, none, + "unexpected variant during dependency scanning on module '%0'", (StringRef)) +NOTE(dependency_scan_unexpected_variant_context_hash_note, none, + "first module context hash: '%0', second module context hash: '%1'", (StringRef, StringRef)) +NOTE(dependency_scan_unexpected_variant_module_map_note, none, + "first module module map: '%0', second module module map: '%1'", (StringRef, StringRef)) +NOTE(dependency_scan_unexpected_variant_extra_arg_note, none, + "%select{first|second}0 module command-line has extra argument: '%1'", (bool, StringRef)) + +ERROR(bridging_header_and_pch_internal_mismatch,none, + "bridging header and precompiled header options mismatch on internal vs. public import", ()) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsIRGen.def b/include/swift/AST/DiagnosticsIRGen.def index ad85f2f9df6e7..e01cd868d5827 100644 --- a/include/swift/AST/DiagnosticsIRGen.def +++ b/include/swift/AST/DiagnosticsIRGen.def @@ -42,12 +42,12 @@ ERROR(no_input_files_for_mt,none, "no swift input files for multi-threaded compilation", ()) ERROR(alignment_dynamic_type_layout_unsupported,none, - "@_alignment is not supported on types with dynamic layout", ()) + "'@_alignment' is not supported on types with dynamic layout", ()) ERROR(alignment_less_than_natural,none, - "@_alignment cannot decrease alignment below natural alignment of %0", + "'@_alignment' cannot decrease alignment below natural alignment of %0", (unsigned)) ERROR(alignment_more_than_maximum,none, - "@_alignment cannot increase alignment above maximum alignment of %0", + "'@_alignment' cannot increase alignment above maximum alignment of %0", (unsigned)) ERROR(temporary_allocation_size_negative,none, @@ -80,5 +80,37 @@ ERROR(attr_objc_implementation_resilient_property_deployment_target, none, ERROR(unable_to_load_pass_plugin,none, "unable to load plugin '%0': '%1'", (StringRef, StringRef)) +ERROR(failed_emit_copy, none, + "failed to copy %0; did you mean to import %0 as ~Copyable?", + (const clang::NamedDecl *)) + +NOTE(use_requires_expression, none, + "use 'requires' (since C++20) to specify the constraints under which the " + "copy " + "constructor is available", + ()) + +NOTE(annotate_copyable_if, none, + "annotate a type with 'SWIFT_COPYABLE_IF()' in C++ to specify " + "that the type is Copyable if is Copyable", + ()) + +NOTE(annotate_non_copyable, none, + "annotate a type with 'SWIFT_NONCOPYABLE' in C++ to import it as " + "~Copyable", + ()) + +NOTE(maybe_missing_annotation, none, + "one of the types that %0 depends on may need a 'requires' clause (since " + "C++20) in the copy constructor, a 'SWIFT_COPYABLE_IF' annotation or a " + "'SWIFT_NONCOPYABLE' annotation'", + (const clang::NamedDecl *)) + +NOTE(maybe_missing_parameter, none, + "the %select{'requires' clause on the copy constructor " + "of|'SWIFT_COPYABLE_IF' annotation on}0 %1 may be missing a " + "%select{constraint|parameter}0", + (bool, const clang::NamedDecl *)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsModuleDiffer.def b/include/swift/AST/DiagnosticsModuleDiffer.def index 7e295ba80e805..d74b6d349c201 100644 --- a/include/swift/AST/DiagnosticsModuleDiffer.def +++ b/include/swift/AST/DiagnosticsModuleDiffer.def @@ -66,7 +66,7 @@ ERROR(optional_req_changed,APIDigesterBreakage,"%0 is %select{now|no longer}1 an ERROR(no_longer_open,APIDigesterBreakage,"%0 is no longer open for subclassing", (StringRef)) -ERROR(func_type_escaping_changed,APIDigesterBreakage,"%0 has %select{removed|added}2 @escaping in %1", (StringRef, StringRef, bool)) +ERROR(func_type_escaping_changed,APIDigesterBreakage,"%0 has %select{removed|added}2 '@escaping' in %1", (StringRef, StringRef, bool)) ERROR(func_self_access_change,APIDigesterBreakage,"%0 has self access kind changing from %1 to %2", (StringRef, StringRef, StringRef)) @@ -78,7 +78,7 @@ ERROR(decl_new_witness_table_entry,APIDigesterBreakage,"%0 now requires%select{| ERROR(class_member_moved_to_extension,APIDigesterBreakage,"Non-final class member %0 is moved to extension", (StringRef)) -WARNING(new_decl_without_intro,APIDigesterBreakage,"%0 is a new API without @available attribute", (StringRef)) +WARNING(new_decl_without_intro,APIDigesterBreakage,"%0 is a new API without '@available'", (StringRef)) ERROR(objc_name_change,APIDigesterBreakage,"%0 has ObjC name change from %1 to %2", (StringRef, StringRef, StringRef)) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 6d3f9b400f73b..d6379e0fe37df 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -62,7 +62,7 @@ ERROR(pound_diagnostic_expected_string,none, "expected string literal in %select{#warning|#error}0 directive",(bool)) ERROR(pound_diagnostic_expected,none, "expected '%0' in %select{#warning|#error}1 directive",(StringRef,bool)) -ERROR(pound_diagnostic_expected_parens,none, +ERROR(pound_diagnostic_expected_parens,PointsToFirstBadToken, "%select{#warning|#error}0 directive requires parentheses",(bool)) ERROR(pound_diagnostic_interpolation,none, "string interpolation is not allowed in %select{#warning|#error}0 directives",(bool)) @@ -229,8 +229,8 @@ ERROR(disallowed_var_multiple_getset,none, "'var' declarations with multiple variables cannot have explicit" " getters/setters", ()) ERROR(stub_decl_cannot_have_body,none, - "stub %0 cannot have body", - (DescriptiveDeclKind)) + "stub %kindonly0 cannot have body", + (const Decl *)) ERROR(disallowed_init,none, "initial value is not allowed here", ()) @@ -303,6 +303,10 @@ ERROR(missing_reading_accessor,none, "%select{variable|subscript}0 with %1 must also have " "a getter, addressor, or 'read' accessor", (unsigned, StringRef)) +ERROR(missing_borrow_accessor,none, + "%select{variable|subscript}0 with %1 must also have " + "a 'borrow' accessor, getter, addressor or 'read' accessor", + (unsigned, StringRef)) ERROR(observing_accessor_conflicts_with_accessor,none, "%select{'willSet'|'didSet'}0 cannot be provided together with %1", (unsigned, StringRef)) @@ -313,6 +317,9 @@ ERROR(getset_cannot_be_implied,none, ERROR(accessor_requires_coroutine_accessors,none, "%0 is only valid when experimental feature coroutine accessors is enabled", (StringRef)) +ERROR(accessor_requires_borrow_and_mutate_accessors,none, + "%0 requires '-enable-experimental-feature BorrowAndMutateAccessors'", + (StringRef)) // Import ERROR(decl_expected_module_name,none, @@ -497,7 +504,7 @@ ERROR(expected_precedencegroup_relation,none, ERROR(expected_sil_keyword,none, "expected SIL keyword", ()) ERROR(inout_not_attribute, none, - "@inout is no longer an attribute", ()) + "'inout' is no longer an attribute", ()) ERROR(only_allowed_in_sil,none, "'%0' only allowed in SIL modules", (StringRef)) ERROR(expected_sil_type,none, @@ -520,7 +527,7 @@ ERROR(silfunc_and_silarg_have_incompatible_sil_value_ownership,none, "Function type specifies: '@%0'. SIL argument specifies: '@%1'.", (StringRef, StringRef)) ERROR(sil_arg_both_lexical_and_eagerMove,none, - "Function argument is annotated both @_eagerMove and @_noEagerMove, " + "Function argument is annotated both '@_eagerMove' and '@_noEagerMove', " "but these are incompatible alternatives", ()) ERROR(expected_sil_colon,none, "expected ':' before %0", (StringRef)) @@ -1484,9 +1491,12 @@ ERROR(multiple_access_level_modifiers,none, "multiple incompatible access-level modifiers specified", ()) NOTE(previous_access_level_modifier,none, "previous modifier specified here", ()) -ERROR(mutually_exclusive_attrs,none, +ERROR(mutually_exclusive_decl_attrs,none, "%0 contradicts previous %select{attribute|modifier}2 %1", (DeclAttribute, DeclAttribute, bool)) +ERROR(mutually_exclusive_type_attrs,none, + "%0 contradicts previous %1", + (const TypeAttribute *, const TypeAttribute *)) ERROR(mutually_exclusive_attr_names,none, "'%0' contradicts previous %select{attribute|modifier}2 '%1'", (StringRef, StringRef, bool)) @@ -1529,6 +1539,9 @@ ERROR(attr_expected_comma,none, ERROR(attr_expected_string_literal,none, "expected string literal in '%0' attribute", (StringRef)) +ERROR(attr_expected_cname,none, + "expected C identifier in '%0' attribute", (StringRef)) + ERROR(attr_expected_option_such_as,none, "expected '%0' option such as '%1'", (StringRef, StringRef)) @@ -1568,6 +1581,11 @@ ERROR(attr_unsupported_on_target, none, ERROR(attr_name_unsupported_on_target, none, "attribute '%0' is unsupported on target '%1'", (StringRef, StringRef)) +// abi attribute +ERROR(attr_abi_incompatible_kind,none, + "cannot use %0 in '@abi'", + (DescriptiveDeclKind)) + // availability ERROR(attr_availability_platform,none, "expected platform name or '*' for '%0' attribute", (StringRef)) @@ -1624,14 +1642,14 @@ ERROR(attr_availability_duplicate,none, // originallyDefinedIn ERROR(originally_defined_in_missing_rparen,none, - "expected ')' in @_originallyDefinedIn argument list", ()) + "expected ')' in '@_originallyDefinedIn' argument list", ()) ERROR(originally_defined_in_need_original_module_name,none, "expected 'module: \"original\"' in the first argument to " - "@_originallyDefinedIn", ()) + "'@_originallyDefinedIn'", ()) ERROR(originally_defined_in_need_nonempty_module_name,none, - "original module name cannot be empty in @_originallyDefinedIn", ()) + "original module name cannot be empty in '@_originallyDefinedIn'", ()) // backDeploy ERROR(attr_back_deploy_expected_before_label,none, @@ -1662,25 +1680,25 @@ ERROR(convention_attribute_witness_method_expected_protocol,none, // objc ERROR(attr_objc_missing_colon,none, - "missing ':' after selector piece in @objc attribute", ()) + "missing ':' after selector piece in '@objc'", ()) ERROR(attr_objc_expected_rparen,none, - "expected ')' after name for @objc", ()) + "expected ')' after name for '@objc'", ()) ERROR(attr_objc_empty_name,none, - "expected name within parentheses of @objc attribute", ()) + "expected name within parentheses of '@objc'", ()) ERROR(attr_dynamic_replacement_expected_rparen,none, - "expected ')' after function name for @_dynamicReplacement", ()) + "expected ')' after function name for '@_dynamicReplacement'", ()) ERROR(attr_dynamic_replacement_expected_function,none, - "expected a function name in @_dynamicReplacement(for:)", ()) + "expected a function name in '@_dynamicReplacement(for:)'", ()) ERROR(attr_dynamic_replacement_expected_for,none, "expected 'for' in '_dynamicReplacement' attribute", ()) ERROR(attr_dynamic_replacement_expected_colon,none, - "expected ':' after @_dynamicReplacement(for", ()) + "expected ':' after '@_dynamicReplacement(for'", ()) ERROR(attr_type_eraser_expected_type_name,none, - "expected a type name in @_typeEraser()", ()) + "expected a type name in '@_typeEraser()'", ()) ERROR(attr_type_eraser_expected_rparen,none, - "expected ')' after type name for @_typeEraser", ()) + "expected ')' after type name for '@_typeEraser'", ()) ERROR(expected_thrown_error_type,none, "expected thrown error type after 'throws('", ()) @@ -1697,22 +1715,14 @@ ERROR(attr_isolated_expected_rparen,none, ERROR(attr_isolated_expected_kind,none, "expected 'any' as the isolation kind", ()) -ERROR(attr_execution_expected_lparen,none, - "expected '(' after '@execution'", - ()) -ERROR(attr_execution_expected_rparen,none, - "expected ')' after execution behavior", ()) -ERROR(attr_execution_expected_kind,none, - "expected 'concurrent' or 'caller' as the execution behavior", ()) - ERROR(attr_private_import_expected_rparen,none, - "expected ')' after function name for @_private", ()) + "expected ')' after function name for '@_private'", ()) ERROR(attr_private_import_expected_sourcefile, none, "expected 'sourceFile' in '_private' attribute", ()) ERROR(attr_private_import_expected_sourcefile_name,none, - "expected a source file name in @_private(sourceFile:)", ()) + "expected a source file name in '@_private(sourceFile:)'", ()) ERROR(attr_private_import_expected_colon,none, - "expected ':' after @_private(sourceFile", ()) + "expected ':' after '@_private(sourceFile'", ()) // opened ERROR(opened_attribute_expected_lparen,none, @@ -1746,22 +1756,26 @@ ERROR(attr_warn_unused_result_expected_rparen,none, // _specialize ERROR(attr_specialize_missing_colon,none, - "missing ':' after %0 in '_specialize' attribute", (StringRef)) + "missing ':' after %0 in %select{'_specialize'|'specialized'}1 attribute", (StringRef, bool)) ERROR(attr_specialize_missing_comma,none, - "missing ',' in '_specialize' attribute", ()) + "missing ',' in %select{'_specialize'|'specialized'}0 attribute", (bool)) ERROR(attr_specialize_unknown_parameter_name,none, - "unknown parameter %0 in '_specialize attribute'", (StringRef)) + "unknown parameter %0 in %select{'_specialize'|'specialized'}1 attribute", (StringRef, bool)) +ERROR(attr_specialize_unsupported_parameter_name,none, + "unsupported parameter %0 in 'specialized attribute'", (StringRef)) ERROR(attr_specialize_expected_bool_value,none, - "expected a boolean true or false value in '_specialize' attribute", ()) + "expected a boolean true or false value in %select{'_specialize'|'specialized'}0 attribute", (bool)) ERROR(attr_specialize_missing_parameter_label_or_where_clause,none, "expected a parameter label or a where clause in '_specialize' attribute", ()) +ERROR(attr_specialized_missing_where_clause,none, + "expected a where clause in 'specialized' attribute", ()) ERROR(attr_specialize_parameter_already_defined,none, - "parameter '%0' was already defined in '_specialize' attribute", (StringRef)) + "parameter '%0' was already defined in %select{'_specialize'|'specialized'}1 attribute", (StringRef, bool)) ERROR(attr_specialize_expected_partial_or_full,none, "expected 'partial' or 'full' as values of the 'kind' parameter in '_specialize' attribute", ()) @@ -1833,18 +1847,18 @@ ERROR(sil_inst_autodiff_expected_differentiability_witness_kind,PointsToFirstBad "'[vjp]', or '[transpose]'", ()) WARNING(warn_attr_unsafe_removed,none, - "'%0' attribute has been removed in favor of @preconcurrency", + "'%0' attribute has been removed in favor of '@preconcurrency'", (StringRef)) // _documentation ERROR(documentation_attr_expected_argument,none, - "@_documentation attribute expected 'visibility' or 'metadata' argument", + "'_documentation' attribute expected 'visibility' or 'metadata' argument", ()) ERROR(documentation_attr_unknown_argument,none, "unknown argument '%0', expected 'visibility' or 'metadata'", (StringRef)) ERROR(documentation_attr_expected_access_level,none, - "@_documentation attribute's 'visibility' argument expected an access level", + "'_documentation' attribute's 'visibility' argument expected an access level", ()) ERROR(documentation_attr_unknown_access_level,none, "unknown visibility '%0', expected an access level keyword", @@ -1852,26 +1866,26 @@ ERROR(documentation_attr_unknown_access_level,none, ERROR(documentation_attr_duplicate_visibility,none, "cannot give more than one visibility to the same item", ()) ERROR(documentation_attr_metadata_expected_text,none, - "@_documentation attribute's 'metadata' argument expected an identifier or " + "'_documentation' attribute's 'metadata' argument expected an identifier or " "quoted string", ()) ERROR(documentation_attr_duplicate_metadata,none, "cannot give more than one metadata argument to the same item", ()) ERROR(attr_rawlayout_expected_label,none, - "expected %0 argument to @_rawLayout attribute", (StringRef)) + "expected %0 argument to '@_rawLayout'", (StringRef)) ERROR(attr_rawlayout_expected_integer_size,none, - "expected integer literal size in @_rawLayout attribute", ()) + "expected integer literal size in '@_rawLayout'", ()) ERROR(attr_rawlayout_expected_integer_alignment,none, - "expected integer literal alignment in @_rawLayout attribute", ()) + "expected integer literal alignment in '@_rawLayout'", ()) ERROR(attr_rawlayout_expected_params,none, - "expected %1 argument after %0 argument in @_rawLayout attribute", (StringRef, StringRef)) + "expected %1 argument after %0 argument in '@_rawLayout'", (StringRef, StringRef)) ERROR(attr_extern_expected_label,none, - "expected %0 argument to @_extern attribute", (StringRef)) + "expected %0 argument to '@_extern'", (StringRef)) ERROR(attr_expected_feature_name,none, - "expected feature name in @%0 attribute", (StringRef)) + "expected feature name in '@%0'", (StringRef)) //------------------------------------------------------------------------------ // MARK: Generics parsing diagnostics @@ -2064,13 +2078,13 @@ ERROR(macro_role_syntax_mismatch,PointsToFirstBadToken, "%select{a freestanding|an attached}0 macro cannot have the %1 role", (bool, Identifier)) ERROR(macro_attribute_unknown_label,PointsToFirstBadToken, - "@%select{freestanding|attached}0 has no argument with label %1", + "'@%select{freestanding|attached}0' has no argument with label %1", (bool, Identifier)) ERROR(macro_attribute_duplicate_label,PointsToFirstBadToken, - "@%select{freestanding|attached}0 already has an argument with " + "'@%select{freestanding|attached}0' already has an argument with " "label %1", (bool, StringRef)) ERROR(macro_attribute_missing_label,none, - "@%select{freestanding|attached}0 argument is missing label '%1'", + "'@%select{freestanding|attached}0' argument is missing label '%1'", (bool, StringRef)) ERROR(macro_attribute_unknown_name_kind,PointsToFirstBadToken, "unknown introduced name kind %0", (Identifier)) @@ -2081,7 +2095,8 @@ ERROR(macro_attribute_introduced_name_requires_argument,PointsToFirstBadToken, ERROR(macro_attribute_introduced_name_requires_no_argument,PointsToFirstBadToken, "introduced name kind %0 must not have an argument", (Identifier)) WARNING(macro_expression_attribute_removed,PointsToFirstBadToken, - "@expression has been removed in favor of @freestanding(expression)", ()) + "'@expression' has been removed in favor of '@freestanding(expression)'", + ()) ERROR(unexpected_attribute_expansion,PointsToFirstBadToken, "unexpected token '%0' in expanded attribute list", @@ -2117,16 +2132,16 @@ ERROR(init_accessor_is_not_in_the_primary_declaration,none, ()) ERROR(missing_storage_restrictions_attr_label,none, - "missing label in @storageRestrictions attribute", ()) + "missing label in '@storageRestrictions'", ()) ERROR(invalid_storage_restrictions_attr_label,none, - "unexpected label %0 in @storageRestrictions attribute", (Identifier)) + "unexpected label %0 in '@storageRestrictions'", (Identifier)) ERROR(duplicate_storage_restrictions_attr_label,none, - "duplicate label %0 in @storageRestrictions attribute", (Identifier)) + "duplicate label %0 in '@storageRestrictions'", (Identifier)) ERROR(storage_restrictions_attr_expected_name,none, - "expected property name in @storageRestrictions list", ()) + "expected property name in '@storageRestrictions' list", ()) ERROR(requires_experimental_feature, none, "'%0' %select{attribute|parameter specifier}1 is only valid when experimental feature " @@ -2142,7 +2157,7 @@ ERROR(expected_lparen_after_lifetime_dependence, PointsToFirstBadToken, ERROR(expected_identifier_or_index_or_self_after_lifetime_dependence, PointsToFirstBadToken, - "expected identifier, index or self in lifetime dependence specifier", + "expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier", ()) ERROR(expected_rparen_after_lifetime_dependence, PointsToFirstBadToken, @@ -2169,5 +2184,27 @@ ERROR(sil_thunkinst_failed_to_parse_kind,none, ERROR(sil_failed_to_parse_sil_optional,none, "Expected SIL optional value of the form '[' NAME ']'", ()) +//------------------------------------------------------------------------------ +// MARK: nonisolated(nonsending) +//------------------------------------------------------------------------------ + +ERROR(nonisolated_nonsending_expected_lparen,PointsToFirstBadToken, + "expected '(' following 'nonisolated'", ()) +ERROR(nonisolated_nonsending_incorrect_modifier,PointsToFirstBadToken, + "expected 'nonsending' in modifier", ()) +ERROR(nonisolated_nonsending_expected_rparen,PointsToFirstBadToken, + "expected ')' after 'nonisolated' modifier", ()) +ERROR(nonisolated_nonsending_repeated,none, + "parameter may have at most one 'nonisolated(nonsending)' specifier", ()) + +//------------------------------------------------------------------------------ +// MARK: using @ or using +//------------------------------------------------------------------------------ +ERROR(using_decl_invalid_specifier,PointsToFirstBadToken, + "default isolation can only be set to '@MainActor' or 'nonisolated'", + ()) +ERROR(experimental_using_decl_disabled,PointsToFirstBadToken, + "'using' is an experimental feature that is currently disabled", ()) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 8237db1f11b8c..0b744c0debd19 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -84,7 +84,7 @@ ERROR(unimplemented_generator_witnesses,none, "protocol conformance emission for generator coroutines is unimplemented", ()) -ERROR(exclusivity_access_required,none, +GROUPED_ERROR(exclusivity_access_required,ExclusivityViolation,none, "overlapping accesses to %0, but " "%select{initialization|read|modification|deinitialization}1 requires " "exclusive access; " @@ -92,18 +92,18 @@ ERROR(exclusivity_access_required,none, "consider calling MutableCollection.swapAt(_:_:)}2", (StringRef, unsigned, bool)) -ERROR(exclusivity_access_required_unknown_decl,none, +GROUPED_ERROR(exclusivity_access_required_unknown_decl,ExclusivityViolation,none, "overlapping accesses, but " "%select{initialization|read|modification|deinitialization}0 requires " "exclusive access; consider copying to a local variable", (unsigned)) -ERROR(exclusivity_access_required_moveonly,none, +GROUPED_ERROR(exclusivity_access_required_moveonly,ExclusivityViolation,none, "overlapping accesses to %0, but " "%select{initialization|read|modification|deinitialization}1 requires " "exclusive access", (StringRef, unsigned)) -ERROR(exclusivity_access_required_unknown_decl_moveonly,none, +GROUPED_ERROR(exclusivity_access_required_unknown_decl_moveonly,ExclusivityViolation,none, "overlapping accesses, but " "%select{initialization|read|modification|deinitialization}0 requires " "exclusive access", (unsigned)) @@ -143,7 +143,7 @@ ERROR(deserialize_function_type_mismatch,Fatal, (StringRef, Type, Type)) ERROR(without_actually_escaping_on_isolated_any,none, - "withoutActuallyEscaping is currently unimplemented for @isolated(any) " + "withoutActuallyEscaping is currently unimplemented for '@isolated(any)' " "function values", ()) // Capture before declaration diagnostics. @@ -315,12 +315,12 @@ ERROR(missing_never_call_closure,none, (Type)) ERROR(missing_return_decl,none, - "missing return in %1 expected to return %0", - (Type, DescriptiveDeclKind)) + "missing return in %kindonly1 expected to return %0", + (Type, const AbstractFunctionDecl *)) ERROR(missing_never_call_decl,none, - "%1 with uninhabited return type %0 is missing " + "%kindonly1 with uninhabited return type %0 is missing " "call to another never-returning function on all paths", - (Type, DescriptiveDeclKind)) + (Type, const AbstractFunctionDecl *)) NOTE(missing_return_last_expr_note,none, "did you mean to return the last expression?", ()) @@ -378,40 +378,69 @@ ERROR(bad_attr_on_non_const_global,none, "global variable must be a compile-time constant to use %0 attribute", (StringRef)) ERROR(global_must_be_compile_time_const,none, "global variable must be a compile-time constant", ()) -ERROR(non_final_generic_class_function,none, - "classes cannot have non-final generic functions in embedded Swift", ()) -ERROR(cannot_specialize_witness_method,none, - "an existential type cannot contain a generic method %0 in embedded Swift", (DeclName)) -ERROR(cannot_specialize_class,none, +ERROR(perf_diag_existential_type,none, + "cannot use a value of protocol type %0 in '@_noExistential' function", (Type)) +ERROR(perf_diag_existential,none, + "cannot use a value of protocol type in '@_noExistential' function", ()) + +// Embedded Swift diagnostics +GROUPED_ERROR(embedded_cannot_specialize_class_method,EmbeddedRestrictions,none, + "classes cannot have a non-final, generic method %0 in embedded Swift", (DeclName)) +GROUPED_ERROR(embedded_cannot_specialize_witness_method,EmbeddedRestrictions,none, + "a protocol type cannot contain a generic method %0 in embedded Swift", (DeclName)) +GROUPED_ERROR(cannot_specialize_class,EmbeddedRestrictions,none, "cannot specialize %0 because class definition is not available (make sure to build with -wmo)", (Type)) -ERROR(embedded_swift_existential_type,none, +GROUPED_ERROR(embedded_swift_existential_type,EmbeddedRestrictions,none, "cannot use a value of protocol type %0 in embedded Swift", (Type)) -ERROR(embedded_swift_existential,none, +GROUPED_ERROR(embedded_swift_existential_protocol,EmbeddedRestrictions,none, + "cannot use a value of protocol type 'any %0' in embedded Swift", (StringRef)) +GROUPED_ERROR(embedded_swift_existential,EmbeddedRestrictions,none, "cannot use a value of protocol type in embedded Swift", ()) -ERROR(perf_diag_existential_type,none, - "cannot use a value of protocol type %0 in @_noExistential function", (Type)) -ERROR(perf_diag_existential,none, - "cannot use a value of protocol type in @_noExistential function", ()) -ERROR(embedded_swift_value_deinit,none, +GROUPED_ERROR(embedded_swift_value_deinit,EmbeddedRestrictions,none, "cannot de-virtualize deinit of type %0", (Type)) -ERROR(embedded_swift_metatype_type,none, +GROUPED_ERROR(embedded_swift_metatype_type,EmbeddedRestrictions,none, "cannot use metatype of type %0 in embedded Swift", (Type)) -ERROR(embedded_swift_metatype,none, +GROUPED_ERROR(embedded_swift_metatype,EmbeddedRestrictions,none, "cannot use metatype in embedded Swift", ()) -ERROR(embedded_swift_keypath,none, +GROUPED_ERROR(embedded_swift_keypath,EmbeddedRestrictions,none, "cannot use key path in embedded Swift", ()) -ERROR(embedded_swift_dynamic_cast,none, +GROUPED_ERROR(embedded_swift_dynamic_cast,EmbeddedRestrictions,none, "cannot do dynamic casting in embedded Swift", ()) +GROUPED_ERROR(embedded_capture_of_generic_value_with_deinit,EmbeddedRestrictions,none, + "capturing generic non-copyable type with deinit in escaping closure not supported in embedded Swift", ()) +GROUPED_ERROR(embedded_call_generic_function,EmbeddedRestrictions,none, + "cannot specialize generic function or default protocol method in this context", ()) +GROUPED_ERROR(embedded_call_generic_function_with_dynamic_self,EmbeddedRestrictions,none, + "cannot call an initializer or static method, which is defined as default protocol method, from a class method or initializer", ()) +NOTE(embedded_specialization_called_from,none, + "generic specialization called here", ()) +NOTE(embedded_existential_created,none, + "protocol type value created here", ()) +NOTE(embedded_constructor_called,none, + "instance of type created here", ()) + +// no-allocations diagnostics ERROR(embedded_swift_allocating_type,none, "cannot use allocating type %0 in -no-allocations mode", (Type)) +ERROR(embedded_swift_allocating_coroutine,none, + "cannot use co-routines (like accessors) in -no-allocations mode", ()) +ERROR(embedded_swift_allocating_closure,none, + "cannot use escaping closures in -no-allocations mode", ()) ERROR(embedded_swift_allocating,none, "cannot use allocating operation in -no-allocations mode", ()) -ERROR(embedded_capture_of_generic_value_with_deinit,none, - "capturing generic non-copyable type with deinit in escaping closure not supported in embedded Swift", ()) + ERROR(wrong_linkage_for_serialized_function,none, "function has wrong linkage to be called from %0", (StringRef)) NOTE(performance_called_from,none, "called from here", ()) +ERROR(manualownership_copy,none, + "explicit 'copy' required here; please report this vague diagnostic as a bug", ()) +ERROR(manualownership_copy_happened,none, + "accessing %0 may produce a copy; write 'copy' to acknowledge or 'consume' to elide", (Identifier)) +ERROR(manualownership_copy_demanded,none, + "independent copy of %0 is required here; write 'copy' to acknowledge or 'consume' to elide", (Identifier)) +ERROR(manualownership_copy_captured,none, + "closure capture of '%0' requires independent copy of it; write [%0 = copy %0] in the closure's capture list to acknowledge", (StringRef)) // 'transparent' diagnostics ERROR(circular_transparent,none, @@ -419,6 +448,10 @@ ERROR(circular_transparent,none, NOTE(note_while_inlining,none, "while inlining here", ()) +// '@inline(always)' diagnostics +ERROR(circular_inlineAlways,none, + "inlining '@inline(always)' functions forms circular loop", ()) + // Pre-specializations ERROR(cannot_prespecialize,none, "Cannot pre-specialize %0", (StringRef)) @@ -701,8 +734,9 @@ WARNING(warning_int_to_fp_inexact, none, // Flow-isolation diagnostics ERROR(isolated_after_nonisolated, none, - "cannot access %1 %2 here in %select{nonisolated initializer|deinitializer}0", - (bool, DescriptiveDeclKind, DeclName)) + "cannot access %kind1 here in " + "%select{nonisolated initializer|deinitializer}0", + (bool, const ValueDecl *)) NOTE(nonisolated_blame, none, "after %1%2 %3, " "only nonisolated properties of 'self' can be accessed from " "%select{this init|a deinit}0", (bool, StringRef, StringRef, DeclName)) @@ -809,7 +843,7 @@ NOTE(capturepromotion_variable_defined_here,none, // noimplicitcopy on generic or existential binding ERROR(noimplicitcopy_used_on_generic_or_existential, none, - "@_noImplicitCopy can not be used on a generic or existential typed " + "'@_noImplicitCopy' can not be used on a generic or existential typed " "binding or a nominal type containing such typed things", ()) // discard statement @@ -840,7 +874,8 @@ ERROR(sil_movechecking_borrowed_parameter_captured_by_closure, none, "parameter", (StringRef)) ERROR(sil_movechecking_capture_consumed, none, - "noncopyable '%0' cannot be consumed when captured by an escaping closure", (StringRef)) + "noncopyable '%0' cannot be consumed when captured by an escaping closure or borrowed by a non-Escapable type", + (StringRef)) ERROR(sil_movechecking_not_reinitialized_before_end_of_function, none, "missing reinitialization of %select{inout parameter|closure capture}1 '%0' " "after consume", (StringRef, bool)) @@ -864,7 +899,8 @@ ERROR(sil_movechecking_cannot_destructure_imported_nonfrozen, none, "cannot partially consume '%0' of non-frozen type %1 imported from %2", (StringRef, Type, const ModuleDecl*)) ERROR(sil_movechecking_cannot_destructure_exported_usableFromInline_alwaysEmitIntoClient, none, - "cannot partially consume '%0' of non-frozen usableFromInline type %1 within a function annotated @_alwaysEmitIntoClient", + "cannot partially consume '%0' of non-frozen usableFromInline type %1 " + "within a function annotated '@_alwaysEmitIntoClient'", (StringRef, Type)) ERROR(sil_movechecking_cannot_destructure, none, "cannot partially consume '%0'", @@ -876,7 +912,8 @@ ERROR(sil_movechecking_cannot_partially_reinit_nonfrozen, none, "cannot partially reinitialize '%0' of non-frozen type %1 imported from %2; only full reinitialization is allowed", (StringRef, Type, const ModuleDecl*)) ERROR(sil_movechecking_cannot_partially_reinit_exported_usableFromInline_alwaysEmitIntoClient, none, - "cannot partially reinitialize '%0' of non-frozen usableFromInline type %1 within a function annotated @_alwaysEmitIntoClient", + "cannot partially reinitialize '%0' of non-frozen usableFromInline type " + "%1 within a function annotated '@_alwaysEmitIntoClient'", (StringRef, Type)) ERROR(sil_movechecking_cannot_partially_reinit, none, "cannot partially reinitialize '%0' after it has been consumed; only full reinitialization is allowed", @@ -947,10 +984,10 @@ NOTE(sil_referencebinding_inout_binding_here, none, ()) //===----------------------------------------------------------------------===// -// MARK: Region Based Isolation Diagnostics +// MARK: Region-Based Isolation Diagnostics //===----------------------------------------------------------------------===// -// READ THIS: Please when adding diagnostics for region based isolation, keep +// READ THIS: Please when adding diagnostics for region-based isolation, keep // them ordered in sections depending on which emitter uses it. It makes it // easier for people unfamiliar with the code to quickly see which diagnostics // are used by which of the SendNonSendable emitters use. @@ -959,7 +996,7 @@ NOTE(sil_referencebinding_inout_binding_here, none, // Misc ERROR(regionbasedisolation_unknown_pattern, none, - "pattern that the region based isolation checker does not understand how to check. Please file a bug", + "pattern that the region-based isolation checker does not understand how to check. Please file a bug", ()) NOTE(regionbasedisolation_maybe_race, none, "access can happen concurrently", ()) @@ -979,23 +1016,23 @@ GROUPED_ERROR(regionbasedisolation_type_send_yields_race, SendingRisksDataRace, (Type)) NOTE(regionbasedisolation_type_use_after_send, none, "sending value of non-Sendable type %0 to %1 callee risks causing data races between %1 and local %2 uses", - (Type, ActorIsolation, ActorIsolation)) + (Type, StringRef, StringRef)) NOTE(regionbasedisolation_type_use_after_send_callee, none, - "sending value of non-Sendable type %0 to %1 %2 %3 risks causing data " - "races between %1 and local %4 uses", - (Type, ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation)) + "sending value of non-Sendable type %0 to %1 %kind2 risks causing data " + "races between %1 and local %3 uses", + (Type, StringRef, const ValueDecl *, StringRef)) NOTE(regionbasedisolation_named_info_send_yields_race, none, - "sending %1%0 to %2 callee risks causing data races between %2 and local %3 uses", - (Identifier, StringRef, ActorIsolation, ActorIsolation)) + "sending %select{%2 |}0%1 to %3 callee risks causing data races between %3 and local %4 uses", + (bool, Identifier, StringRef, StringRef, StringRef)) NOTE(regionbasedisolation_named_info_send_yields_race_callee, none, - "sending %1%0 to %2 %3 %4 risks causing data races between %2 and local %5 uses", - (Identifier, StringRef, ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation)) + "sending %select{%2 |}0%1 to %3 %kind4 risks causing data races between %3 and local %5 uses", + (bool, Identifier, StringRef, StringRef, const ValueDecl *, StringRef)) // Use after send closure. NOTE(regionbasedisolation_type_isolated_capture_yields_race, none, "sending value of non-Sendable type %0 to %1 closure due to closure capture risks causing races in between %1 and %2 uses", - (Type, ActorIsolation, ActorIsolation)) + (Type, StringRef, StringRef)) // Value captured in async let and reused. NOTE(regionbasedisolation_named_nonisolated_asynclet_name, none, @@ -1006,33 +1043,33 @@ NOTE(regionbasedisolation_named_value_used_after_explicit_sending, none, "%0 used after being passed as a 'sending' parameter; Later uses could race", (Identifier)) NOTE(regionbasedisolation_named_isolated_closure_yields_race, none, - "%0%1 is captured by a %2 closure. %2 uses in closure may race against later %3 uses", - (StringRef, Identifier, ActorIsolation, ActorIsolation)) + "%select{%1 |}0%2 is captured by a %3 closure. %3 uses in closure may race against later %4 uses", + (bool, StringRef, Identifier, StringRef, StringRef)) NOTE(regionbasedisolation_typed_use_after_sending, none, "Passing value of non-Sendable type %0 as a 'sending' argument risks causing races in between local and caller code", (Type)) NOTE(regionbasedisolation_typed_use_after_sending_callee, none, - "Passing value of non-Sendable type %0 as a 'sending' argument to %1 %2 risks causing races in between local and caller code", - (Type, DescriptiveDeclKind, DeclName)) + "Passing value of non-Sendable type %0 as a 'sending' argument to %kind1 risks causing races in between local and caller code", + (Type, const ValueDecl *)) //=== // Sending Never Sendable Emitter NOTE(regionbasedisolation_named_send_never_sendable, none, - "sending %1%0 to %2 callee risks causing data races between %2 and %3 uses", - (Identifier, StringRef, ActorIsolation, StringRef)) + "sending %select{%2 |}0%1 to %3 callee risks causing data races between %3 and %4 uses", + (bool, Identifier, StringRef, StringRef, StringRef)) NOTE(regionbasedisolation_named_send_never_sendable_callee, none, - "sending %1%0 to %2 %3 %4 risks causing data races between %2 and %5 uses", - (Identifier, StringRef, ActorIsolation, DescriptiveDeclKind, DeclName, StringRef)) + "sending %select{%2 |}0%1 to %3 %kind4 risks causing data races between %3 and %5 uses", + (bool, Identifier, StringRef, StringRef, const ValueDecl *, StringRef)) NOTE(regionbasedisolation_named_send_into_sending_param, none, - "%0%1 is passed as a 'sending' parameter; Uses in callee may race with " - "later %0uses", - (StringRef, Identifier)) + "%select{%1 |}0%2 is passed as a 'sending' parameter; Uses in callee may race with " + "later %1 uses", + (bool, StringRef, Identifier)) NOTE(regionbasedisolation_named_nosend_send_into_result, none, - "%0%1 cannot be a 'sending' result. %2 uses may race with caller uses", - (StringRef, Identifier, StringRef)) + "%select{%1 |}0%2 cannot be a 'sending' result. %3 uses may race with caller uses", + (bool, StringRef, Identifier, StringRef)) NOTE(regionbasedisolation_typed_tns_passed_to_sending, none, "Passing %0 value of non-Sendable type %1 as a 'sending' parameter risks " "causing races inbetween %0 uses and uses reachable from the callee", @@ -1048,8 +1085,8 @@ NOTE(regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_value_ "closure captures %0 which is accessible to code in the current task", (DeclName)) NOTE(regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_boxed_value_task_isolated, none, - "closure captures reference to mutable %1 %0 which is accessible to code in the current task", - (DeclName, DescriptiveDeclKind)) + "closure captures reference to mutable %kind0 which is accessible to code in the current task", + (const ValueDecl *)) NOTE(regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_value_region, none, "closure captures %1 which is accessible to %0 code", (StringRef, DeclName)) @@ -1065,36 +1102,66 @@ NOTE(regionbasedisolation_closure_captures_actor, none, (DeclName, StringRef)) NOTE(regionbasedisolation_typed_tns_passed_to_sending_callee, none, - "Passing %0 value of non-Sendable type %1 as a 'sending' parameter to %2 %3 risks causing races inbetween %0 uses and uses reachable from %3", - (StringRef, Type, DescriptiveDeclKind, DeclName)) + "Passing %0 value of non-Sendable type %1 as a 'sending' parameter to %kind2 risks causing races inbetween %0 uses and uses reachable from %2", + (StringRef, Type, const ValueDecl *)) NOTE(regionbasedisolation_named_send_nt_asynclet_capture, none, "sending %1 %0 into async let risks causing data races between nonisolated and %1 uses", (Identifier, StringRef)) NOTE(regionbasedisolation_typed_sendneversendable_via_arg, none, "sending %0 value of non-Sendable type %1 to %2 callee risks causing races in between %0 and %2 uses", - (StringRef, Type, ActorIsolation)) + (StringRef, Type, StringRef)) NOTE(regionbasedisolation_typed_sendneversendable_via_arg_callee, none, - "sending %0 value of non-Sendable type %1 to %2 %3 %4 risks causing races in between %0 and %2 uses", - (StringRef, Type, ActorIsolation, DescriptiveDeclKind, DeclName)) + "sending %0 value of non-Sendable type %1 to %2 %kind3 risks causing races in between %0 and %2 uses", + (StringRef, Type, StringRef, const ValueDecl *)) + +NOTE(regionbasedisolation_isolated_conformance_introduced, none, + "isolated conformance to %kind0 can be introduced here", + (const ValueDecl *)) // Error that is only used when the send non sendable emitter cannot discover any // information to give a better diagnostic. ERROR(regionbasedisolation_task_or_actor_isolated_sent, none, - "task or actor isolated value cannot be sent", ()) + "task or actor-isolated value cannot be sent", ()) //=== // InOut Sending Emitter NOTE(regionbasedisolation_inout_sending_must_be_reinitialized, none, - "'inout sending' parameter must be reinitialized before function exit with a non-actor isolated value", + "'inout sending' parameter must be reinitialized before function exit with a non-actor-isolated value", ()) ERROR(regionbasedisolation_inout_sending_cannot_be_actor_isolated, none, - "'inout sending' parameter %0 cannot be %1at end of function", + "'inout sending' parameter %0 cannot be %1 at end of function", (Identifier, StringRef)) NOTE(regionbasedisolation_inout_sending_cannot_be_actor_isolated_note, none, - "%1%0 risks causing races in between %1uses and caller uses since caller assumes value is not actor isolated", + "%1 %0 risks causing races in between %1 uses and caller uses since caller assumes value is not actor isolated", (Identifier, StringRef)) +ERROR(regionbasedisolation_inout_sending_cannot_be_returned_param, none, + "'inout sending' parameter %0 cannot be returned", + (Identifier)) +ERROR(regionbasedisolation_inout_sending_cannot_be_returned_value, none, + "%0 cannot be returned", + (Identifier)) +ERROR(regionbasedisolation_inout_sending_cannot_be_returned_value_result, none, + "result of %kind0 cannot be returned", + (const ValueDecl *)) +NOTE(regionbasedisolation_inout_sending_cannot_be_returned_note_param, none, + "returning 'inout sending' parameter %0 risks concurrent access as caller assumes %0 and result can be sent to different isolation domains", + (Identifier)) +NOTE(regionbasedisolation_inout_sending_cannot_be_returned_note_value, none, + "returning %0 risks concurrent access to 'inout sending' parameter %1 as caller assumes %1 and result can be sent to different isolation domains", + (Identifier, Identifier)) +NOTE(regionbasedisolation_inout_sending_cannot_be_returned_note_return_value, none, + "returning result of %kind0 risks concurrent access to 'inout sending' parameter %1 as caller assumes %1 and result can be sent to different isolation domains", + (const ValueDecl *, Identifier)) +NOTE(regionbasedisolation_inout_sending_cannot_be_returned_note_actor_param, none, + "returning %0 risks concurrent access as caller assumes %0 is not actor-isolated and result is %1", (Identifier, StringRef)) +NOTE(regionbasedisolation_inout_sending_cannot_be_returned_note_actor_value, none, + "returning %0 risks concurrent access to 'inout sending' parameter %1 as caller assumes %1 is not actor-isolated and result is %2", + (Identifier, Identifier, StringRef)) +NOTE(regionbasedisolation_inout_sending_cannot_be_returned_note_actor_return_value, none, + "returning result of %kind0 risks concurrent access to 'inout sending' parameter %1 as caller assumes %1 is not actor-isolated and result is %2", + (const ValueDecl *, Identifier, StringRef)) //=== // Out Sending @@ -1118,10 +1185,10 @@ NOTE(regionbasedisolation_out_sending_cannot_be_actor_isolated_note_named, none, // Example: returning main-actor isolated result to a custom-actor isolated context risks causing data races ERROR(rbi_isolation_crossing_result, none, "non-Sendable %0-typed result can not be returned from %1 %kind2 to %3 context", - (Type, ActorIsolation, const ValueDecl *, ActorIsolation)) + (Type, StringRef, const ValueDecl *, StringRef)) ERROR(rbi_isolation_crossing_result_no_decl, none, "non-Sendable %0-typed result can not be returned from %1 function to %2 context", - (Type, ActorIsolation, ActorIsolation)) + (Type, StringRef, StringRef)) NOTE(rbi_non_sendable_nominal,none, "%kind0 does not conform to the 'Sendable' protocol", (const ValueDecl *)) @@ -1136,12 +1203,9 @@ NOTE(rbi_add_generic_parameter_sendable_conformance,none, // Concurrency related diagnostics ERROR(cannot_find_executor_factory_type, none, - "the specified executor factory '%0' could not be found", (StringRef)) + "the DefaultExecutorFactory type could not be found", ()) ERROR(executor_factory_must_conform, none, - "the executor factory '%0' does not conform to 'ExecutorFactory'", - (StringRef)) -ERROR(executor_factory_not_supported, none, - "deployment target too low for executor factory specification", ()) + "the DefaultExecutorFactory does not conform to 'ExecutorFactory'", ()) //===----------------------------------------------------------------------===// // MARK: Misc Diagnostics @@ -1155,21 +1219,43 @@ ERROR(lifetime_variable_outside_scope, none, "lifetime-dependent variable '%0' escapes its scope", (Identifier)) ERROR(lifetime_value_outside_scope, none, "lifetime-dependent value escapes its scope", ()) +ERROR(lifetime_value_outside_accessor, none, + "lifetime-dependent value returned by generated accessor '%0'", (StringRef)) +ERROR(lifetime_value_outside_thunk, none, + "lifetime-dependent value returned by generated %select{function|thunk}0 '%1'", (bool, StringRef)) NOTE(lifetime_outside_scope_argument, none, "it depends on the lifetime of argument '%0'", (Identifier)) +NOTE(lifetime_outside_scope_synthesized_argument, none, + "it depends on the lifetime of an argument of '%0'", (Identifier)) NOTE(lifetime_outside_scope_access, none, "it depends on this scoped access to variable '%0'", (Identifier)) NOTE(lifetime_outside_scope_variable, none, "it depends on the lifetime of variable '%0'", (Identifier)) NOTE(lifetime_outside_scope_value, none, "it depends on the lifetime of this parent value", ()) +NOTE(lifetime_outside_scope_capture, none, + "it depends on a closure capture; this is not yet supported", ()) NOTE(lifetime_outside_scope_use, none, "this use of the lifetime-dependent value is out of scope", ()) NOTE(lifetime_outside_scope_escape, none, "this use causes the lifetime-dependent value to escape", ()) +NOTE(implicit_function_note, none, "error in compiler-generated '%0'", (StringRef)) + ERROR(noncopyable_shared_case_block_unimplemented, none, "matching a non-'Copyable' value using a case label that has multiple patterns is not implemented", ()) + +/// Borrow and mutate accessors +ERROR(invalid_borrow_accessor_return, none, + "invalid return value from borrow accessor", ()) +NOTE(borrow_accessor_not_a_projection_note, none, + "borrow accessors can return either literals, stored properties or computed " + "properties that have borrow accessors", + ()) +ERROR(invalid_multiple_return_borrow_accessor, none, + "multiple return statements in borrow accessors are not yet supported", + ()) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 2e9f8b78c95f8..dbee278386036 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -42,6 +42,8 @@ NOTE(opaque_return_type_declared_here,none, "opaque return type declared here", ()) NOTE(default_value_declared_here,none, "default value declared here", ()) +NOTE(requirement_declared_here,none, + "requirement %0 declared here", (const ValueDecl *)) //------------------------------------------------------------------------------ // MARK: Constraint solver diagnostics @@ -165,11 +167,20 @@ ERROR(init_candidate_inaccessible,none, "'%select{private|fileprivate|internal|package|@_spi|@_spi}1' protection level", (Type, AccessLevel)) -ERROR(candidate_from_missing_import,none, - "%0 %1 is not available due to missing import of defining module %2", - (DescriptiveDeclKind, DeclName, ModuleDecl *)) +GROUPED_ERROR(member_from_missing_import,MemberImportVisibility,none, + "%kind0 is not available due to missing import of defining module %1", + (const ValueDecl *, const ModuleDecl *)) +GROUPED_ERROR(member_from_missing_imports_2_or_more,MemberImportVisibility,none, + "%kind0 is not available due to missing imports of defining modules " + "%2%select{ and|, }1 %3%select{|, and others}1", + (const ValueDecl *, bool, const ModuleDecl *, const ModuleDecl *)) NOTE(candidate_add_import,none, - "add import of module %0", (ModuleDecl *)) + "add import of module %0", (const ModuleDecl *)) + +GROUPED_WARNING(add_required_import_for_member,MemberImportVisibility,none, + "import of module %0 is required", (const ModuleDecl *)) +NOTE(decl_from_module_used_here,none, + "%kind0 from %1 used here", (const ValueDecl *, const ModuleDecl *)) ERROR(cannot_pass_rvalue_mutating_subelement,none, "cannot use mutating member on immutable value: %0", @@ -243,13 +254,6 @@ ERROR(cannot_apply_lvalue_binop_to_subelement,none, ERROR(cannot_apply_lvalue_binop_to_rvalue,none, "left side of mutating operator has immutable type %0", (Type)) -ERROR(cannot_subscript_base,none, - "cannot subscript a value of type %0", - (Type)) - -ERROR(cannot_subscript_ambiguous_base,none, - "cannot subscript a value of incorrect or ambiguous type", ()) - ERROR(cannot_subscript_nil_literal,none, "cannot subscript a nil literal value", ()) ERROR(conditional_cast_from_nil,none, @@ -310,13 +314,13 @@ ERROR(incorrect_explicit_closure_result_vs_return_type,none, (Type, Type)) ERROR(addressable_not_enabled,none, - "@_addressable is an experimental feature", ()) + "'@_addressable' is an experimental feature", ()) ERROR(addressableSelf_not_on_method,none, - "@_addressableSelf cannot be applied to non-member declarations", ()) + "'@_addressableSelf' cannot be applied to non-member declarations", ()) ERROR(addressable_types_not_enabled,none, - "@_addressableForDependencies is an experimental feature", ()) + "'@_addressableForDependencies' is an experimental feature", ()) ERROR(class_cannot_be_addressable_for_dependencies,none, - "a class cannot be @_addressableForDependencies", ()) + "a class cannot be '@_addressableForDependencies'", ()) ERROR(unsupported_closure_attr,none, "%select{attribute |}0%1 is not supported on a closure", @@ -355,6 +359,11 @@ ERROR(cannot_convert_return_type_to_anyobject,none, ERROR(cannot_convert_to_return_type_nil,none, "'nil' is incompatible with return type %0", (Type)) +ERROR(cannot_convert_metatype_to_non_metatype,none, + "cannot convert metatype %0 to non-metatype %1", (Type, Type)) +ERROR(cannot_convert_typeof_to_non_metatype,none, + "cannot convert 'type(of:)' metatype to non-metatype %0", (Type)) + ERROR(cannot_convert_thrown_type,none, "thrown expression type %0 %select{cannot be converted to error type %1|" "does not conform to 'Error'}2", @@ -594,8 +603,8 @@ ERROR(cannot_convert_assign,none, "cannot assign value of type %0 to type %1", (Type,Type)) NOTE(assign_protocol_conformance_fix_it,none, - "add missing conformance to %0 to %1 %2", - (Type, DescriptiveDeclKind, Type)) + "add missing conformance to %0 to %kind1", + (Type, const NominalTypeDecl *)) ERROR(cannot_convert_assign_protocol,none, "value of type %0 does not conform to %1 in assignment", (Type, Type)) @@ -670,6 +679,9 @@ ERROR(expr_keypath_nonescapable_type,none, ERROR(expr_keypath_not_property,none, "%select{key path|dynamic key path member lookup}1 cannot refer to %kind0", (const ValueDecl *, bool)) +ERROR(expr_keypath_type_ref,none, + "%select{key path|dynamic key path member lookup}1 cannot refer to type %0", + (const ValueDecl *, bool)) ERROR(expr_keypath_mutating_getter,none, "%select{key path|dynamic key path member lookup}1 cannot refer to %0, " "which has a mutating getter", @@ -783,11 +795,11 @@ ERROR(expr_selector_not_objc,none, "Objective-C", (const ValueDecl *)) NOTE(make_decl_objc,none, - "add '@objc' to expose this %0 to Objective-C", - (DescriptiveDeclKind)) + "add '@objc' to expose this %kindonly0 to Objective-C", + (const ValueDecl *)) NOTE(make_decl_objc_for_implementation,none, - "add '@objc' to implement an Objective-C %0", - (DescriptiveDeclKind)) + "add '@objc' to implement an Objective-C %kindonly0", + (const Decl *)) // Selectors-as-string-literals. WARNING(selector_literal_invalid,none, @@ -810,9 +822,34 @@ ERROR(cannot_return_value_from_void_func,none, NOTE(add_return_type_note,none, "did you mean to add a return type?", ()) -ERROR(expect_compile_time_const,none, +ERROR(expect_compile_time_literal,none, "expect a compile-time constant literal", ()) +ERROR(const_unsupported_enum_associated_value,none, + "enums with associated values not supported in a '@const' expression", ()) +ERROR(const_unsupported_operator,none, + "unsupported operator in a '@const' expression", ()) +ERROR(const_unsupported_type,none, + "unsupported type in a '@const' expression", ()) +ERROR(const_unsupported_type_expr,none, + "type expressions not supported in a '@const' expression", ()) +ERROR(const_unsupported_closure,none, + "closures not supported in a '@const' expression", ()) +ERROR(const_unsupported_keypath,none, + "keypaths not supported in a '@const' expression", ()) +ERROR(const_opaque_decl_ref,none, + "unable to resolve variable reference in a '@const' expression", ()) +ERROR(const_opaque_func_decl_ref,none, + "unable to resolve function reference in a '@const' expression", ()) +ERROR(const_non_convention_c_conversion,none, + "only 'convention(c)' function values are supported in a '@const' expression", ()) +ERROR(const_opaque_callee,none, + "unable to resolve callee in a '@const' expression", ()) +ERROR(const_non_const_param,none, + "reference to a non-'@const' parameter in a '@const' expression", ()) +ERROR(const_unknown_default,none, + "not supported in a '@const' expression", ()) + //------------------------------------------------------------------------------ // MARK: Import Resolution //------------------------------------------------------------------------------ @@ -855,6 +892,10 @@ ERROR(serialization_failed,none, WARNING(can_import_invalid_swiftmodule,none, "canImport() evaluated to false due to invalid swiftmodule: %0", (StringRef)) +ERROR(map_os_version_from_textual_interface_failed,none, + "failed to map OS version from %0 to %1 in %2", + (StringRef, StringRef, StringRef)) + ERROR(serialization_load_failed,Fatal, "failed to load module '%0'", (StringRef)) ERROR(module_interface_build_failed,Fatal, @@ -922,9 +963,6 @@ ERROR(serialization_target_too_new_repl,none, "deployment target of %0 %3: %4", (StringRef, llvm::VersionTuple, Identifier, llvm::VersionTuple, StringRef)) -ERROR(serialization_non_ossa_module_incompatible, Fatal, - "cannot import non-OSSA module into an OSSA module", - (Identifier)) ERROR(serialization_fatal,Fatal, "fatal error encountered while reading from module '%0'; " @@ -932,11 +970,12 @@ ERROR(serialization_fatal,Fatal, (StringRef)) ERROR(serialization_invalid_decl,Fatal, - "deserialized invalid declaration %0 (%1) in module '%2'", - (DeclName, DescriptiveDeclKind, StringRef)) + "deserialized invalid declaration %0 (%kindonly0) in module %1", + (const Decl *, const ModuleDecl *)) ERROR(serialization_allowing_invalid_decl,none, - "allowing deserialization of invalid declaration %0 (%1) in module '%2'", - (DeclName, DescriptiveDeclKind, StringRef)) + "allowing deserialization of invalid declaration %0 (%kindonly0) in " + "module %1", + (const Decl *, const ModuleDecl *)) ERROR(serialization_error_type,Fatal, "deserialized error type '%0' in module '%1'", (StringRef, StringRef)) @@ -1066,8 +1105,8 @@ ERROR(invalid_redecl_init,none, (const ValueDecl *, bool)) ERROR(invalid_redecl_implicit,none, "invalid redeclaration of synthesized " - "%select{%0|implementation for protocol requirement}1 %2", - (DescriptiveDeclKind, bool, const ValueDecl *)) + "%select{%kindonly0|implementation for protocol requirement}1 %2", + (const ValueDecl *, bool, const ValueDecl *)) WARNING(invalid_redecl_swift5_warning,Deprecation, "redeclaration of %0 is deprecated and will be an error in Swift 5", (const ValueDecl *)) @@ -1185,7 +1224,7 @@ ERROR(imported_decl_is_wrong_kind_typealias,none, ERROR(ambiguous_decl_in_module,none, "ambiguous name %0 in module %1", (Identifier, Identifier)) -ERROR(module_not_testable,Fatal, +GROUPED_ERROR(module_not_testable,ModuleNotTestable,Fatal, "module %0 was not compiled for testing", (Identifier)) ERROR(module_not_compiled_for_private_import,none, @@ -1211,11 +1250,13 @@ ERROR(module_not_compiled_with_library_evolution,none, "module %0 was not compiled with library evolution support; " "using it means binary compatibility for %1 can't be guaranteed", (Identifier, Identifier)) -WARNING(implementation_only_requires_library_evolution,none, +GROUPED_WARNING(implementation_only_requires_library_evolution, + ImplementationOnlyDeprecated,none, "using '@_implementationOnly' without enabling library evolution " "for %0 may lead to instability during execution", (Identifier)) -WARNING(implementation_only_deprecated,none, +GROUPED_WARNING(implementation_only_deprecated, + ImplementationOnlyDeprecated,none, "'@_implementationOnly' is deprecated, use 'internal import' instead", ()) @@ -1247,21 +1288,16 @@ REMARK(module_api_import_aliases,none, "%select{, which reexports definition from %2|}3", (const Decl *, ModuleDecl *, ModuleDecl *, bool)) -REMARK(dependency_scan_skip_module_invalid,none, - "module file '%0' skipped by the dependency scan because it is " - "incompatible with this Swift compiler: %1", (StringRef, StringRef)) -WARNING(skip_module_not_testable,none, - "ignore swiftmodule built without '-enable-testing': %0", (StringRef)) -WARNING(dependency_scan_module_incompatible, none, - "module file '%0' is incompatible with this Swift compiler: %1", - (StringRef, StringRef)) - REMARK(macro_loaded,none, "loaded macro implementation module %0 from " "%select{shared library '%2'|executable '%2'|" "compiler plugin server '%2' with shared library '%3'}1", (Identifier, unsigned, StringRef, StringRef)) +ERROR(resolved_macro_changed,none, + "resolved macro library '%0' failed verification: %1", + (StringRef, StringRef)) + REMARK(transitive_dependency_behavior,none, "%1 has %select{a required|an optional|an ignored}2 " "transitive dependency on '%0'", @@ -1271,7 +1307,7 @@ REMARK(explicit_interface_build_skipped,none, "Skipped rebuilding module at %0 - up-to-date", (StringRef)) -WARNING(cannot_find_project_version,none, +GROUPED_WARNING(cannot_find_module_version,ModuleVersionMissing,none, "cannot find user version number for%select{| Clang}1 module '%0';" " version number ignored", (StringRef, bool)) @@ -1389,10 +1425,10 @@ ERROR(did_not_call_function_value,none, ()) ERROR(did_not_call_function,none, "function %0 was used as a property; add () to call it", - (Identifier)) + (DeclBaseName)) ERROR(did_not_call_method,none, "method %0 was used as a property; add () to call it", - (Identifier)) + (DeclBaseName)) ERROR(init_not_instance_member_use_assignment,none, "'init' is a member of the type; use assignment " @@ -1549,6 +1585,9 @@ ERROR(missing_address_of,none, ERROR(missing_address_of_yield,none, "yielding mutable value of type %0 requires explicit '&'", (Type)) +ERROR(missing_address_of_return,none, + "returning mutable value from a mutate accessor requires explicit '&'", + ()) ERROR(extraneous_address_of,none, "'&' may only be used to pass an argument to inout parameter", ()) @@ -1678,20 +1717,29 @@ NOTE(unbound_generic_parameter_explicit_fix,none, "explicitly specify the generic arguments to fix this issue", ()) GROUPED_ERROR(invalid_dynamic_callable_type,DynamicCallable,none, - "@dynamicCallable attribute requires %0 to have either a valid " + "'@dynamicCallable' requires %0 to have either a valid " "'dynamicallyCall(withArguments:)' method or " "'dynamicallyCall(withKeywordArguments:)' method", (Type)) GROUPED_ERROR(missing_dynamic_callable_kwargs_method,DynamicCallable,none, - "@dynamicCallable type %0 cannot be applied with keyword arguments; " + "'@dynamicCallable' type %0 cannot be applied with keyword arguments; " "missing 'dynamicCall(withKeywordArguments:)' method", (Type)) ERROR(invalid_dynamic_member_lookup_type,none, - "@dynamicMemberLookup attribute requires %0 to have a " + "'@dynamicMemberLookup' requires %0 to have a " "'subscript(dynamicMember:)' method that accepts either " "'ExpressibleByStringLiteral' or a key path", (Type)) NOTE(invalid_dynamic_member_subscript, none, "add an explicit argument label to this subscript to satisfy " - "the @dynamicMemberLookup requirement", ()) + "the '@dynamicMemberLookup' requirement", ()) +ERROR(dynamic_member_lookup_candidate_inaccessible,none, + "'@dynamicMemberLookup' requires %0 to be as accessible as its " + "enclosing type", + (ValueDecl *)) + +ERROR(too_many_dynamic_member_lookups,none, + "could not find member %0; exceeded the maximum number of nested " + "dynamic member lookups", + (DeclNameRef)) ERROR(string_index_not_integer,none, "String must not be indexed with %0, it has variable size elements", @@ -1709,7 +1757,7 @@ ERROR(c_function_pointer_from_generic_function,none, "a C function pointer cannot be formed from a reference to a generic " "function", ()) ERROR(invalid_autoclosure_forwarding,none, - "add () to forward @autoclosure parameter", ()) + "add () to forward '@autoclosure' parameter", ()) ERROR(invalid_differentiable_function_conversion_expr,none, "a '@differentiable' function can only be formed from " "a reference to a 'func' or 'init' or a literal closure", ()) @@ -1743,9 +1791,9 @@ ERROR(nominal_type_not_attribute,none, ERROR(mutating_invalid_global_scope,none, "%0 is only valid on methods", (SelfAccessKind)) -ERROR(mutating_invalid_classes,none, "%0 is not valid on %1s in " +ERROR(mutating_invalid_classes,none, "%0 is not valid on %kindonly1s in " "%select{classes|class-bound protocols}2", - (SelfAccessKind, DescriptiveDeclKind, bool)) + (SelfAccessKind, const FuncDecl *, bool)) ERROR(functions_mutating_and_not,none, "method must not be declared both %0 and %1", @@ -1764,25 +1812,25 @@ ERROR(transparent_in_classes_not_supported,none, "'@_transparent' attribute is not supported on declarations within classes", ()) ERROR(iboutlet_nonobjc_class,none, - "@IBOutlet property cannot %select{have|be an array of}0 " + "'@IBOutlet' property cannot %select{have|be an array of}0 " "non-'@objc' class type %1", (bool, Type)) ERROR(iboutlet_nonobjc_protocol,none, - "@IBOutlet property cannot %select{have|be an array of}0 " + "'@IBOutlet' property cannot %select{have|be an array of}0 " "non-'@objc' protocol type %1", (bool, Type)) ERROR(iboutlet_nonobject_type,none, - "@IBOutlet property cannot %select{have|be an array of}0 " + "'@IBOutlet' property cannot %select{have|be an array of}0 " "non-object type %1", (bool, Type)) ERROR(iboutlet_only_mutable,none, - "@IBOutlet attribute requires property to be mutable", ()) + "'@IBOutlet' requires property to be mutable", ()) ERROR(iboutlet_non_optional,none, - "@IBOutlet property has non-optional type %0", (Type)) + "'@IBOutlet' property has non-optional type %0", (Type)) NOTE(note_make_optional,none, "add '?' to form the optional type %0", (Type)) NOTE(note_make_implicitly_unwrapped_optional,none, "add '!' to form an implicitly unwrapped optional", ()) ERROR(invalid_ibdesignable_extension,none, - "@IBDesignable can only be applied to classes and extensions " + "'@IBDesignable' can only be applied to classes and extensions " "of classes", ()) ERROR(attr_must_be_used_on_class_instance,none, "only class instance properties can be declared %0", (DeclAttribute)) @@ -1811,11 +1859,11 @@ NOTE(remove_async_add_task,none, "remove 'async' and wrap in 'Task' to use concurrency in %0", (const FuncDecl *)) ERROR(no_objc_tagged_pointer_not_class_protocol,none, - "@unsafe_no_objc_tagged_pointer can only be applied to class protocols", + "'@unsafe_no_objc_tagged_pointer' can only be applied to class protocols", ()) ERROR(swift_native_objc_runtime_base_not_on_root_class,none, - "@_swift_native_objc_runtime_base_not_on_root_class can only be applied " - "to root classes", ()) + "'@_swift_native_objc_runtime_base_not_on_root_class' can only be " + "applied to root classes", ()) WARNING(objc_implementation_early_spelling_deprecated,none, "'@_objcImplementation' is deprecated; use '@implementation' instead", @@ -1836,12 +1884,12 @@ ERROR(objc_implementation_cannot_have_generics,none, "'@objc @implementation' cannot be used to implement %kind0", (ValueDecl *)) ERROR(attr_objc_implementation_category_not_found,none, - "could not find category %0 on Objective-C class %1; make sure your " - "umbrella or bridging header imports the header that declares it", + "could not find category %0 on Objective-C class %1; make sure you " + "import the module or header that declares it", (Identifier, ValueDecl*)) ERROR(attr_objc_implementation_func_not_found,none, - "could not find imported function '%0' matching %kind1; make sure your " - "umbrella or bridging header imports the header that declares it", + "could not find imported function '%0' matching %kind1; make sure you " + "import the module or header that declares it", (StringRef, ValueDecl*)) NOTE(attr_objc_implementation_fixit_remove_category_name,none, "remove arguments to implement the main '@interface' for this class", @@ -1888,13 +1936,13 @@ ERROR(member_of_objc_implementation_not_objc_or_final,none, "did you use the %kindonly0's Swift name?", (ValueDecl *, ValueDecl *)) NOTE(fixit_add_private_for_objc_implementation,none, - "add 'private' or 'fileprivate' to define an Objective-C-compatible %0 " - "not declared in the header", - (DescriptiveDeclKind)) + "add 'private' or 'fileprivate' to define an Objective-C-compatible " + "%kindonly0 not declared in the header", + (const ValueDecl *)) NOTE(fixit_add_nonobjc_or_final_for_objc_implementation,none, - "add '%select{@nonobjc|final}1' to define a Swift-only %0" + "add '%select{@nonobjc|final}1' to define a Swift-only %kindonly0" "%select{| that cannot be overridden}1", - (DescriptiveDeclKind, /*suggestFinal=*/bool)) + (const ValueDecl *, /*suggestFinal=*/bool)) ERROR(objc_implementation_wrong_category,none, "%kind0 should be implemented in extension for " @@ -1903,13 +1951,13 @@ ERROR(objc_implementation_wrong_category,none, (ValueDecl *, Identifier, Identifier)) ERROR(objc_implementation_wrong_decl_kind,none, - "%kind0 does not match the %1 declared by the header", - (ValueDecl *, DescriptiveDeclKind)) + "%kind0 does not match the %kindonly1 declared by the header", + (const ValueDecl *, const ValueDecl *)) ERROR(objc_implementation_must_be_settable,none, - "%kind0 should be settable to match the settable %1 declared by the " - "header", - (ValueDecl *, DescriptiveDeclKind)) + "%kind0 should be settable to match the settable %kindonly1 declared by " + "the header", + (const ValueDecl *, const ValueDecl *)) ERROR(objc_implementation_type_mismatch,none, "%kind0 of type %1 does not match type %2 declared by the header", @@ -1921,9 +1969,9 @@ ERROR(objc_implementation_sendability_mismatch,none, (ValueDecl *, Type, Type)) ERROR(objc_implementation_required_attr_mismatch,none, - "%kind0 %select{should not|should}2 be 'required' to match %1 declared " + "%kind0 should%select{ not|}2 be 'required' to match %kindonly1 declared " "by the header", - (ValueDecl *, DescriptiveDeclKind, bool)) + (const ValueDecl *, const ValueDecl *, bool)) ERROR(objc_implementation_candidate_has_error_convention,none, "%kind0 does not match the declaration in the header because it throws an " @@ -1977,14 +2025,23 @@ ERROR(objc_implementation_wrong_swift_name,none, "you mean %1?", (ObjCSelector, const ValueDecl *)) -ERROR(objc_implementation_missing_impl,none, - "extension for %select{main class interface|category %0}0 should " - "provide implementation for %kind1", - (Identifier, ValueDecl *)) +ERROR(objc_implementation_missing_impls,none, + "extension for %select{main class interface|category %0}0 does not " + "provide all required implementations", + (Identifier)) +NOTE(objc_implementation_missing_impls_fixit,none, + "add stub%s0 for missing '@implementation' requirement%s0", + (unsigned)) +NOTE(objc_implementation_missing_impl,none, + "missing %kind0", + (ValueDecl *)) +NOTE(objc_implementation_missing_impl_either,none, + "missing %kind0 or %1", + (ValueDecl *, ValueDecl *)) ERROR(objc_implementation_class_or_instance_mismatch,none, - "%kind0 does not match %1 declared in header", - (ValueDecl *, DescriptiveDeclKind)) + "%kind0 does not match %kindonly1 declared in header", + (const ValueDecl *, const ValueDecl *)) ERROR(objc_implementation_multiple_matching_candidates,none, "found multiple implementations that could match %kind0 with selector %1", @@ -2028,17 +2085,23 @@ WARNING(wrap_objc_implementation_will_become_error,none, (DiagnosticInfo *)) ERROR(cdecl_not_at_top_level,none, - "@_cdecl can only be applied to global functions", ()) + "%0 can only be applied to global functions", (DeclAttribute)) ERROR(cdecl_empty_name,none, - "@_cdecl symbol name cannot be empty", ()) + "%0 symbol name cannot be empty", (DeclAttribute)) ERROR(cdecl_throws,none, - "raising errors from @_cdecl functions is not supported", ()) + "raising errors from %0 functions is not supported", (DeclAttribute)) +ERROR(cdecl_incompatible_with_objc,none, + "cannot apply both '@c' and '@objc' to %kindonly0", + (const Decl *)) +ERROR(cdecl_feature_required,none, + "'@c' requires '-enable-experimental-feature CDecl'", + ()) // @_used and @_section ERROR(section_linkage_markers_disabled,none, "attribute requires '-enable-experimental-feature SymbolLinkageMarkers'", ()) ERROR(section_empty_name,none, - "@_section section name cannot be empty", ()) + "'@_section' section name cannot be empty", ()) // @_silgen_name and friends WARNING(reserved_runtime_symbol_name,none, @@ -2048,23 +2111,24 @@ WARNING(reserved_runtime_symbol_name,none, // @_extern ERROR(attr_extern_experimental,none, - "@_extern requires '-enable-experimental-feature Extern'", ()) + "'@_extern' requires '-enable-experimental-feature Extern'", ()) ERROR(extern_not_at_top_level_func,none, - "@_extern attribute can only be applied to global functions", ()) + "'@_extern' can only be applied to global functions", ()) ERROR(extern_empty_c_name,none, - "expected non-empty C name in @_extern attribute", ()) + "expected non-empty C name in '@_extern'", ()) ERROR(extern_only_non_other_attr,none, - "@_extern attribute cannot be applied to an %0 declaration", + "'@_extern' cannot be applied to an %0 declaration", (DeclAttribute)) WARNING(extern_c_maybe_invalid_name, none, - "C name '%0' may be invalid; explicitly specify the name in @_extern(c) to suppress this warning", + "C name '%0' may be invalid; explicitly specify the name in " + "'@_extern(c)' to suppress this warning", (StringRef)) // @_staticExclusiveOnly ERROR(attr_static_exclusive_only_disabled,none, "attribute requires '-enable-experimental feature StaticExclusiveOnly'", ()) ERROR(attr_static_exclusive_only_noncopyable,none, - "@_staticExclusiveOnly can only be applied to noncopyable types", ()) + "'@_staticExclusiveOnly' can only be applied to noncopyable types", ()) ERROR(attr_static_exclusive_only_let_only,none, "variable of type %0 must be declared with a 'let'", (Type)) NOTE(attr_static_exclusive_only_type_nonmutating,none, @@ -2076,13 +2140,20 @@ ERROR(attr_static_exclusive_only_mutating,none, ERROR(attr_static_exclusive_no_setters,none, "variable of type %0 must not have a setter", (Type)) +// @_manualOwnership +ERROR(attr_manual_ownership_experimental,none, + "'@_manualOwnership' requires '-enable-experimental-feature ManualOwnership'", ()) +ERROR(attr_manual_ownership_noimplicitcopy,none, + "'@_noImplicitCopy' cannot be used with ManualOwnership", ()) + // @extractConstantsFromMembers ERROR(attr_extractConstantsFromMembers_experimental,none, - "@extractConstantsFromMembers requires '-enable-experimental-feature ExtractConstantsFromMembers'", ()) + "'@extractConstantsFromMembers' requires " + "'-enable-experimental-feature ExtractConstantsFromMembers'", ()) // @sensitive ERROR(attr_sensitive_experimental,none, - "@sensitive requires '-enable-experimental-feature Sensitive'", ()) + "'@sensitive' requires '-enable-experimental-feature Sensitive'", ()) ERROR(c_func_variadic, none, "cannot declare variadic argument %0 in %kind1", @@ -2098,18 +2169,22 @@ ERROR(c_func_throws,none, "raising errors from C functions is not supported", ()) ERROR(expose_wasm_not_at_top_level_func,none, - "@_expose attribute with 'wasm' can only be applied to global functions", ()) + "'@_expose' with 'wasm' can only be applied to global " + "functions", ()) ERROR(expose_only_non_other_attr,none, - "@_expose attribute cannot be applied to an '%0' declaration", (StringRef)) + "'@_expose' cannot be applied to an '%0' declaration", + (StringRef)) ERROR(expose_inside_unexposed_decl,none, - "@_expose attribute cannot be applied inside of unexposed declaration %0", + "'@_expose' cannot be applied inside of unexposed declaration %0", (const ValueDecl *)) +ERROR(expose_redundant_name_provided, none, + "'@_expose(!Cxx)' does not accept a name argument", ()) ERROR(expose_invalid_name_pattern_init,none, - "invalid declaration name '%0' specified in an @_expose attribute; " + "invalid declaration name '%0' specified in '@_expose'; " "exposed initializer name must start with 'init'", (StringRef)) ERROR(expose_unsupported_objc_decl_to_cxx,none, - "@objc %kind0 can not yet be exposed to C++", (ValueDecl *)) + "'@objc' %kind0 can not yet be exposed to C++", (ValueDecl *)) ERROR(expose_unsupported_async_decl_to_cxx,none, "async %kind0 can not be exposed to C++", (ValueDecl *)) ERROR(expose_unsupported_actor_isolated_to_cxx,none, @@ -2136,6 +2211,12 @@ ERROR(expose_nested_type_to_cxx,none, "nested %kind0 can not yet be represented in C++", (ValueDecl *)) ERROR(expose_macro_to_cxx,none, "Swift macro can not yet be represented in C++", (ValueDecl *)) +WARNING(warn_unannotated_cxx_func_returning_frt, none, + "cannot infer the ownership of the returned value, annotate %0 with " + "either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED", + (const ValueDecl *)) +NOTE(note_unannotated_cxx_func_returning_frt, none, "%0 is defined here", + (const ValueDecl *)) ERROR(unexposed_other_decl_in_cxx,none, "%kind0 is not yet exposed to C++", (ValueDecl *)) ERROR(unsupported_other_decl_in_cxx,none, @@ -2157,9 +2238,6 @@ ERROR(attr_only_at_non_generic_scope, none, ERROR(attr_only_on_static_properties, none, "properties with attribute %0 must be static", (DeclAttribute)) -ERROR(weak_unowned_in_embedded_swift, none, - "attribute %0 cannot be used in embedded Swift", (ReferenceOwnership)) - ERROR(access_control_in_protocol,none, "%0 modifier cannot be used in protocols", (DeclAttribute)) NOTE(access_control_in_protocol_detail,none, @@ -2182,8 +2260,8 @@ ERROR(access_control_setter_more,none, WARNING(access_control_setter_redundant,none, "'%select{private|fileprivate|internal|package|public|open}0(set)' modifier is " "redundant for %select{a private|a fileprivate|an internal|a package|a public|an open}2 " - "%1", - (AccessLevel, DescriptiveDeclKind, AccessLevel)) + "%kindonly1", + (AccessLevel, const Decl *, AccessLevel)) WARNING(access_control_ext_member_more,none, "'%select{%error|fileprivate|internal|package|public|open}0' modifier conflicts " "with extension's default access of " @@ -2191,18 +2269,18 @@ WARNING(access_control_ext_member_more,none, (AccessLevel, AccessLevel)) WARNING(access_control_ext_member_redundant,none, "'%select{%error|fileprivate|internal|package|public|%error}0' modifier is redundant " - "for %1 declared in %select{a private (equivalent to fileprivate)|a fileprivate" + "for %kindonly1 declared in %select{a private (equivalent to fileprivate)|a fileprivate" "|an internal|a package|a public|%error}2 extension", - (AccessLevel, DescriptiveDeclKind, AccessLevel)) + (AccessLevel, const Decl *, AccessLevel)) ERROR(access_control_ext_requirement_member_more,none, - "cannot declare %select{%error|a fileprivate|an internal|a package|a public|an open}0 %1 " + "cannot declare %select{%error|a fileprivate|an internal|a package|a public|an open}0 %kindonly1 " "in an extension with %select{private|fileprivate|internal|package|public|%error}2 " "requirements", - (AccessLevel, DescriptiveDeclKind, AccessLevel)) + (AccessLevel, const Decl *, AccessLevel)) ERROR(access_control_extension_more,none, - "extension of %select{private|fileprivate|internal|package|%error|%error}0 %1 cannot " + "extension of %select{private|fileprivate|internal|package|%error|%error}0 %kindonly1 cannot " "be declared %select{%error|fileprivate|internal|package|public|%error}2", - (AccessLevel, DescriptiveDeclKind, AccessLevel)) + (AccessLevel, const NominalTypeDecl *, AccessLevel)) ERROR(access_control_extension_open,none, "extensions cannot be declared 'open'; declare individual members as 'open' instead", ()) @@ -2220,11 +2298,11 @@ ERROR(access_control_requires_package_name, none, ERROR(invalid_decl_attribute,none, "'%0' attribute cannot be applied to this declaration", (DeclAttribute)) ERROR(attr_invalid_on_decl_kind,none, - "'%0' attribute cannot be applied to %1 declarations", - (DeclAttribute, DescriptiveDeclKind)) + "'%0' attribute cannot be applied to %kindonly1 declarations", + (DeclAttribute, const Decl *)) ERROR(attr_invalid_in_context,none, - "'%0' attribute cannot be applied to declaration in %1", - (DeclAttribute, DescriptiveDeclKind)) + "'%0' attribute cannot be applied to declaration in %kindonly1", + (DeclAttribute, const Decl *)) ERROR(invalid_decl_modifier,none, "%0 modifier cannot be applied to this declaration", (DeclAttribute)) ERROR(attribute_does_not_apply_to_type,none, @@ -2232,7 +2310,7 @@ ERROR(attribute_does_not_apply_to_type,none, ERROR(optional_attribute_non_protocol,none, "'optional' can only be applied to protocol members", ()) ERROR(optional_attribute_non_objc_protocol,none, - "'optional' can only be applied to members of an @objc protocol", ()) + "'optional' can only be applied to members of an '@objc' protocol", ()) ERROR(optional_attribute_missing_explicit_objc,none, "'optional' requirements are an Objective-C compatibility feature; add '@objc'", ()) @@ -2241,25 +2319,25 @@ ERROR(objcmembers_attribute_nonclass,none, ERROR(optional_attribute_initializer,none, "'optional' cannot be applied to an initializer", ()) ERROR(unavailable_method_non_objc_protocol,none, - "protocol members can only be marked unavailable in an @objc protocol", + "protocol members can only be marked unavailable in an '@objc' protocol", ()) ERROR(spi_available_malformed,none, "SPI available only supports introducing version on specific platform", ()) ERROR(missing_in_class_init_1,none, "stored property %0 requires an initial value%select{| or should be " - "@NSManaged}1", (Identifier, bool)) + "'@NSManaged'}1", (Identifier, bool)) ERROR(missing_in_class_init_2,none, "stored properties %0 and %1 require initial values%select{| or should " - "be @NSManaged}2", + "be '@NSManaged'}2", (Identifier, Identifier, bool)) ERROR(missing_in_class_init_3plus,none, "stored properties %0, %1, %select{and %2|%2, and others}3 " - "require initial values%select{| or should be @NSManaged}4", + "require initial values%select{| or should be '@NSManaged'}4", (Identifier, Identifier, Identifier, bool, bool)) NOTE(requires_stored_property_inits_here,none, "%select{superclass|class}1 %0 requires all stored properties to have " - "initial values%select{| or use @NSManaged}2", (Type, bool, bool)) + "initial values%select{| or use '@NSManaged'}2", (Type, bool, bool)) ERROR(class_without_init,none, "%select{class|actor}0 %1 has no initializers", (bool, Type)) NOTE(note_no_in_class_init_1,none, @@ -2306,22 +2384,42 @@ ERROR(originally_definedin_must_not_before_available_version,none, "the OSs", ()) WARNING(spi_preferred_over_spi_available,none, - "symbols that are @_spi_available on all platforms should use @_spi " - "instead", ()) + "symbols that are '@_spi_available' on all platforms should use " + "'@_spi' instead", ()) // Alignment attribute ERROR(alignment_not_power_of_two,none, "alignment value must be a power of two", ()) // Dependency Scanning -ERROR(dependency_scan_module_not_found, none, "Unable to find module dependency: '%0'", (StringRef)) +ERROR(dependency_scan_module_not_found, none, "unable to resolve module dependency: '%0'", (StringRef)) + +GROUPED_ERROR(dependency_scan_module_not_found_on_specified_search_paths, MissingModuleOnKnownPaths, none, + "compilation search paths unable to resolve module dependency: '%0'", (StringRef)) + NOTE(unresolved_import_location,none, "also imported here", ()) NOTE(dependency_as_imported_by_main_module,none, "a dependency of main module '%0'", (StringRef)) NOTE(dependency_as_imported_by, none, "a dependency of %select{Swift|Clang}2 module '%0': '%1'", (StringRef, StringRef, bool)) -ERROR(clang_dependency_scan_error, none, "Clang dependency scanner failure: %0", (StringRef)) +NOTE(inherited_search_path_resolves_module,none, + "'%0' can be found using a search path that was specified when building module '%1' ('%2'). " + "This search path was not explicitly specified on the current compilation", (StringRef, StringRef, StringRef)) + +ERROR(dependency_scan_compatible_swift_module_not_found, none, + "unable to resolve Swift module dependency to a compatible module: '%0'", + (StringRef)) +NOTE(dependency_scan_incompatible_module_found,none, + "found incompatible module '%0': %1", (StringRef, StringRef)) + +WARNING(dependency_scan_module_incompatible, none, + "module file '%0' is incompatible with this Swift compiler: %1", + (StringRef, StringRef)) + +ERROR(clang_dependency_scan_error, none, "clang dependency scanning failure: %0", (StringRef)) + +ERROR(clang_header_dependency_scan_error, none, "bridging header dependency scanning failure: %0", (StringRef)) // Enum annotations ERROR(indirect_case_without_payload,none, @@ -2336,8 +2434,8 @@ ERROR(getset_init,none, "variable with getter/setter cannot have an initial value", ()) ERROR(effectful_not_representable_objc,none, - "%0 with 'throws' or 'async' is not representable in Objective-C", - (DescriptiveDeclKind)) + "%kindonly0 with 'throws' or 'async' is not representable in Objective-C", + (const AbstractStorageDecl *)) ERROR(unimplemented_static_var,none, "%select{ERROR|static|class}1 stored properties not supported" @@ -2571,10 +2669,10 @@ WARNING(function_type_usable_from_inline_warn,none, (unsigned, bool, bool)) ERROR(spi_attribute_on_non_public,none, - "%select{private|fileprivate|internal|package|%error|%error}0 %1 " + "%select{private|fileprivate|internal|package|%error|%error}0 %kindonly1 " "cannot be declared '@_spi' because only public and open " "declarations can be '@_spi'", - (AccessLevel, DescriptiveDeclKind)) + (AccessLevel, const ValueDecl *)) ERROR(spi_attribute_on_protocol_requirement,none, "protocol requirement %0 cannot be declared '@_spi' without " "a default implementation in a protocol extension", @@ -2608,13 +2706,13 @@ NOTE(module_imported_here,none, NOTE(decl_import_via_here,none, "%kind0 imported as " "'%select{private|fileprivate|internal|package|%ERROR|%ERROR}1' " - "from %2 here", - (const Decl *, AccessLevel, const ModuleDecl*)) + "from %select{%2 here|bridging header}3", + (const Decl *, AccessLevel, const ModuleDecl*, bool)) NOTE(decl_import_via_local,none, "%kind0 is imported by this file as " "'%select{private|fileprivate|internal|package|%ERROR|%ERROR}1' " - "from %2", - (const Decl *, AccessLevel, const ModuleDecl*)) + "from %select{%2|bridging header}3", + (const Decl *, AccessLevel, const ModuleDecl*, bool)) // Opaque return types ERROR(opaque_type_invalid_constraint,none, @@ -2687,10 +2785,6 @@ NOTE(invalid_extension_rewrite,none, ERROR(cannot_extend_nominal,none, "cannot extend %kind0", (const NominalTypeDecl *)) -ERROR(retroactive_not_in_extension_inheritance_clause,none, - "'retroactive' attribute only applies in inheritance clauses in " - "extensions", ()) - ERROR(retroactive_attr_does_not_apply,none, "'retroactive' attribute does not apply; %0 is declared in " "%select{the same package|this module}1", @@ -2740,16 +2834,16 @@ NOTE(non_sendable_nominal,none, NOTE(add_nominal_sendable_conformance,none, "consider making %kind0 conform to the 'Sendable' protocol", (const ValueDecl *)) -NOTE(add_generic_parameter_sendable_conformance,none, - "consider making generic parameter %0 conform to the 'Sendable' protocol", - (Type)) +NOTE(add_generic_parameter_conformance,none, + "consider making generic parameter %0 conform to the %1 protocol", + (Type, ProtocolDecl *)) WARNING(add_predates_concurrency_import,none, "add '@preconcurrency' to %select{suppress|treat}0 " "'Sendable'-related %select{warnings|errors}0 from module %1" "%select{| as warnings}0", (bool, Identifier)) GROUPED_WARNING(remove_predates_concurrency_import,PreconcurrencyImport, DefaultIgnore, - "'@preconcurrency' attribute on module %0 has no effect", (Identifier)) + "'@preconcurrency' on module %0 has no effect", (Identifier)) WARNING(remove_public_import,none, "public import of %0 was not used in public declarations or inlinable code", (Identifier)) @@ -2759,9 +2853,6 @@ WARNING(remove_package_import,none, WARNING(public_decl_needs_sendable,none, "public %kind0 does not specify whether it is 'Sendable' or not", (const ValueDecl *)) -NOTE(explicit_unchecked_sendable,none, - "add '@unchecked Sendable' conformance to %kind0 if this type manually implements concurrency safety", - (const ValueDecl *)) NOTE(explicit_disable_sendable,none, "make %kind0 explicitly non-Sendable to suppress this warning", (const ValueDecl *)) @@ -2877,11 +2968,11 @@ ERROR(non_class_cannot_conform_to_class_protocol,none, "non-class type %0 cannot conform to class protocol %1", (Type, Type)) ERROR(cf_class_cannot_conform_to_objc_protocol,none, - "Core Foundation class %0 cannot conform to @objc protocol %1 because " + "Core Foundation class %0 cannot conform to '@objc' protocol %1 because " "Core Foundation types are not classes in Objective-C", (Type, Type)) ERROR(objc_runtime_visible_cannot_conform_to_objc_protocol,none, - "class %0 cannot conform to @objc protocol %1 because " + "class %0 cannot conform to '@objc' protocol %1 because " "the class is only visible via the Objective-C runtime", (Type, Type)) ERROR(objc_generics_cannot_conditionally_conform,none, @@ -2889,13 +2980,13 @@ ERROR(objc_generics_cannot_conditionally_conform,none, "the type uses the Objective-C generics model", (Type, Type)) ERROR(objc_protocol_cannot_have_conditional_conformance,none, - "type %0 cannot conditionally conform to @objc protocol %1 because " + "type %0 cannot conditionally conform to '@objc' protocol %1 because " "Objective-C does not support conditional conformances", (Type, Type)) ERROR(objc_protocol_in_generic_extension,none, "conformance of " "%select{class from generic context|generic class}0 " - "%1 to @objc protocol %2 cannot be in an extension", + "%1 to '@objc' protocol %2 cannot be in an extension", (bool, Type, Type)) ERROR(conditional_conformances_cannot_imply_conformances,none, "conditional conformance of type %0 to protocol %1 does not imply conformance to " @@ -2994,12 +3085,15 @@ ERROR(type_witness_objc_generic_parameter,none, "used for associated type %3 of protocol %4", (Type, Type, bool, const AssociatedTypeDecl *, const ProtocolDecl *)) NOTE(witness_fix_access,none, - "mark the %0 as '%select{%error|fileprivate|internal|package|public|%error}1' to " - "satisfy the requirement", (DescriptiveDeclKind, AccessLevel)) + "mark the %kindonly0 as " + "'%select{%error|fileprivate|internal|package|public|%error}1' to " + "satisfy the requirement", + (const ValueDecl *, AccessLevel)) NOTE(witness_move_to_another_extension,none, - "move the %0 to another extension where it can be declared " + "move the %kindonly0 to another extension where it can be declared " "'%select{%error|%error|internal|package|public|%error}1' to " - "satisfy the requirement", (DescriptiveDeclKind, AccessLevel)) + "satisfy the requirement", + (const ValueDecl *, AccessLevel)) WARNING(assoc_type_default_conformance_failed,none, "default type %0 for associated type %1 does not satisfy constraint " "%2: %3", (Type, const AssociatedTypeDecl *, Type, Type)) @@ -3208,9 +3302,9 @@ NOTE(protocol_conformance_here,none, NOTE(declared_protocol_conformance_here,none, "%select{%0 inherits conformance to protocol %2 from superclass|" "%0 declares conformance to protocol %2|" - "%0 implicitly conforms to protocol %2 (via conformance to %3)|" - "%0 implicitly conforms to protocol %2 |" - "conformance to %2 generated by macro }1 here", + "conformance to %2 generated by macro|" + "%0 implicitly conforms to protocol %2|" + "%0 implicitly conforms to protocol %2 (via conformance to %3)}1 here", (Type, unsigned, Identifier, Identifier)) ERROR(witness_unavailable,none, @@ -3290,7 +3384,7 @@ ERROR(typealias_outside_of_protocol,none, (DeclNameRef, Type)) ERROR(objc_protocol_inherits_non_objc_protocol,none, - "@objc protocol %0 cannot refine non-@objc protocol %1", (Type, Type)) + "'@objc' protocol %0 cannot refine non-'@objc' protocol %1", (Type, Type)) ERROR(protocol_where_clause_self_requirement,none, "constraint with subject type of 'Self' is not supported; " @@ -3328,6 +3422,9 @@ ERROR(invalid_shape_requirement,none, ERROR(unsupported_same_element,none, "same-element requirements are not yet supported", ()) +ERROR(unsupported_pack_same_type,none, + "same-type requirements between packs and concrete types are not yet supported", + ()) ERROR(requires_generic_params_made_equal,none, "same-type requirement makes generic parameters %0 and %1 equivalent", @@ -3370,11 +3467,12 @@ WARNING(typealias_override_associated_type,none, (Identifier, Type)) WARNING(associated_type_override_typealias,none, "associated type %0 is redundant with type %0 declared in inherited " - "%1 %2", (Identifier, DescriptiveDeclKind, Type)) + "%kind1", + (Identifier, const NominalTypeDecl *)) ERROR(requirement_machine_completion_failed,none, "cannot build rewrite system for %select{generic signature|protocol}0; " - "%select{%error|rule count|rule length|concrete nesting}1 limit exceeded", + "%select{%error|rule count|rule length|concrete type nesting|concrete type size|concrete type difference}1 limit exceeded", (unsigned, unsigned)) NOTE(requirement_machine_completion_rule,none, "failed rewrite rule is %0", (StringRef)) @@ -3384,30 +3482,30 @@ ERROR(associated_type_objc,none, (Identifier, Identifier)) ERROR(generic_param_access,none, - "%0 %select{must be declared %select{" + "%kindonly0 %select{must be declared %select{" "%select{private|fileprivate|internal|package|%error|%error}3|private or fileprivate}4" "|cannot be declared " "%select{in this context|fileprivate|internal|package|public|open}2}1 " "because its generic %select{parameter|requirement}5 uses " "%select{a private|a fileprivate|an internal|a package|an '@_spi'|an '@_spi'}3 type", - (DescriptiveDeclKind, bool, AccessLevel, AccessLevel, bool, bool)) + (const Decl *, bool, AccessLevel, AccessLevel, bool, bool)) WARNING(generic_param_access_warn,none, - "%0 %select{should be declared " + "%kindonly0 %select{should be declared " "%select{private|fileprivate|internal|package|%error|%error}3" "|should not be declared %select{in this context|fileprivate|internal|package|public|open}2}1 " "because its generic %select{parameter|requirement}5 uses " "%select{a private|a fileprivate|an internal|a package|an '@_spi'|an '@_spi'}3 type", - (DescriptiveDeclKind, bool, AccessLevel, AccessLevel, bool, bool)) + (const Decl *, bool, AccessLevel, AccessLevel, bool, bool)) ERROR(generic_param_usable_from_inline,none, "type referenced from a " - "generic %select{parameter|requirement}1 of a '@usableFromInline' %0 " - "must be '@usableFromInline' or public", - (DescriptiveDeclKind, bool)) + "generic %select{parameter|requirement}1 of a '@usableFromInline' " + "%kindonly0 must be '@usableFromInline' or public", + (const Decl *, bool)) WARNING(generic_param_usable_from_inline_warn,none, "type referenced from a " - "generic %select{parameter|requirement}1 of a '@usableFromInline' %0 " - "should be '@usableFromInline' or public", - (DescriptiveDeclKind, bool)) + "generic %select{parameter|requirement}1 of a '@usableFromInline' " + "%kindonly0 should be '@usableFromInline' or public", + (const Decl *, bool)) ERROR(override_multiple_decls_base,none, "declaration %0 cannot override more than one superclass declaration", @@ -3425,7 +3523,7 @@ NOTE(overridden_near_match_here,none, "potential overridden %kind0 here", (const ValueDecl *)) ERROR(override_decl_extension,none, - "%select{|non-@objc}0 %kind2 %select{" + "%select{|non-'@objc'}0 %kind2 %select{" "is declared in extension of %3 and cannot be overridden|" "declared in %3 cannot be overridden from extension}1", (bool, bool, const ValueDecl *, DeclName)) @@ -3445,13 +3543,17 @@ ERROR(multiple_override,none, NOTE(multiple_override_prev,none, "%0 previously overridden here", (DeclName)) -ERROR(override_unavailable, none, - "cannot override %0 which has been marked unavailable%select{|: %1}1", - (DeclBaseName, StringRef)) +ERROR(cannot_override_unavailable, none, + "cannot override %base0 which has been marked unavailable%select{|: %1}1", + (ValueDecl *, StringRef)) NOTE(suggest_removing_override, none, "remove 'override' modifier to declare a new %0", (DeclBaseName)) +ERROR(override_unavailable, none, + "cannot override %base0 with a declaration that is marked unavailable", + (ValueDecl *)) + ERROR(override_less_available,none, "overriding %base0 must be as available as declaration it overrides", (ValueDecl *)) @@ -3462,13 +3564,13 @@ ERROR(override_let_property,none, ERROR(override_not_accessible,none, - "%select{|setter of }0overriding %1 must be as accessible as " + "%select{|setter of }0overriding %kindonly1 must be as accessible as " "%select{its enclosing type|the declaration it overrides}2", - (bool, DescriptiveDeclKind, bool)) + (bool, const ValueDecl *, bool)) ERROR(override_of_non_open,none, - "overriding non-open %0 outside of its defining module", - (DescriptiveDeclKind)) + "overriding non-open %kindonly0 outside of its defining module", + (const ValueDecl *)) ERROR(method_does_not_override,none, "method does not override any method from its %select{parent protocol|superclass}0", (bool)) @@ -3492,8 +3594,8 @@ NOTE(subscript_override_here,none, NOTE(convenience_init_override_here,none, "attempt to override convenience initializer here", ()) NOTE(override_type_mismatch_with_fixits,none, - "type does not match superclass %0 with type %1", - (DescriptiveDeclKind, Type)) + "type does not match superclass %kindonly0 with type %1", + (const ValueDecl *, Type)) NOTE(override_type_mismatch_with_fixits_init,none, "type does not match superclass initializer with %select{no arguments|argument %1|arguments %1}0", (unsigned, Type)) @@ -3529,34 +3631,34 @@ ERROR(override_class_declaration_in_extension,none, "cannot override a non-dynamic class declaration from an extension", ()) ERROR(override_with_more_effects,none, - "cannot override non-%1 %0 with %1 %0", - (DescriptiveDeclKind, StringRef)) + "cannot override non-%1 %kindonly0 with %1 %kindonly0", + (const ValueDecl *, StringRef)) ERROR(override_typed_throws,none, - "%0 that throws %1 cannot override one that throws %2", - (DescriptiveDeclKind, Type, Type)) + "%kindonly0 that throws %1 cannot override one that throws %2", + (const AbstractFunctionDecl *, Type, Type)) ERROR(override_throws_objc,none, - "overriding a throwing @objc %select{method|initializer}0 with " + "overriding a throwing '@objc' %select{method|initializer}0 with " "a non-throwing %select{method|initializer}0 is not supported", (bool)) ERROR(satisfy_throws_objc,none, - "satisfying a throwing @objc %select{method|initializer}0 with " + "satisfying a throwing '@objc' %select{method|initializer}0 with " "a non-throwing %select{method|initializer}0 is not supported", (bool)) ERROR(override_optional_mismatch,none, - "cannot override %0 %select{parameter|index}1 of type %2 with " + "cannot override %kindonly0 %select{parameter|index}1 of type %2 with " "non-optional type %3", - (DescriptiveDeclKind, bool, Type, Type)) + (const ValueDecl *, bool, Type, Type)) ERROR(override_optional_result_mismatch,none, - "cannot override %0 %select{result|element}1 type %2 with " + "cannot override %kindonly0 %select{result|element}1 type %2 with " "optional type %3", - (DescriptiveDeclKind, bool, Type, Type)) + (const ValueDecl *, bool, Type, Type)) WARNING(override_unnecessary_IUO,none, - "overriding %0 parameter of type %1 with implicitly unwrapped optional " - "type %2", - (DescriptiveDeclKind, Type, Type)) + "overriding %kindonly0 parameter of type %1 with implicitly unwrapped " + "optional type %2", + (const ValueDecl *, Type, Type)) WARNING(override_unnecessary_result_IUO,none, - "overriding %0 optional result type %1 with implicitly unwrapped " + "overriding %kindonly0 optional result type %1 with implicitly unwrapped " "optional type %2", - (DescriptiveDeclKind, Type, Type)) + (const ValueDecl *, Type, Type)) NOTE(override_unnecessary_IUO_remove,none, "remove '!' to make the parameter required", ()) NOTE(override_unnecessary_IUO_use_strict,none, @@ -3573,7 +3675,8 @@ ERROR(override_mutable_covariant_subscript,none, ERROR(static_decl_already_final,none, "static declarations are already final", ()) ERROR(open_decl_cannot_be_final,none, - "%0 cannot be declared both 'final' and 'open'", (DescriptiveDeclKind)) + "%kindonly0 cannot be declared both 'final' and 'open'", + (const Decl *)) ERROR(implicitly_final_cannot_be_open,none, "%select{'let' properties|members of 'final' classes|" "static declarations}0 are implicitly 'final'; use 'public' instead of " @@ -3601,8 +3704,6 @@ ERROR(inheritance_from_non_protocol,none, "inheritance from non-protocol type %0", (Type)) ERROR(inheritance_from_anyobject,none, "only protocols can inherit from 'AnyObject'", ()) -ERROR(inheritance_from_parameterized_protocol,none, - "cannot inherit from protocol type with generic argument %0", (Type)) ERROR(superclass_not_first,none, "superclass %0 must appear first in the inheritance clause", (Type)) ERROR(superclass_not_open,none, @@ -3705,9 +3806,9 @@ ERROR(enum_with_raw_type_case_with_argument,none, NOTE(enum_raw_type_here,none, "declared raw type %0 here", (Type)) ERROR(objc_enum_no_raw_type,none, - "'@objc' enum must declare an integer raw type", ()) + "'%0' enum must declare an integer raw type", (DeclAttribute)) ERROR(objc_enum_raw_type_not_integer,none, - "'@objc' enum raw type %0 is not an integer type", (Type)) + "'%0' enum raw type %1 is not an integer type", (DeclAttribute, Type)) ERROR(enum_non_integer_raw_value_auto_increment,none, "enum case must declare a raw value when the preceding raw value is not an integer", ()) ERROR(enum_non_integer_convertible_raw_type_no_value,none, @@ -3715,9 +3816,6 @@ ERROR(enum_non_integer_convertible_raw_type_no_value,none, "expressible by integer or string literal", ()) ERROR(enum_raw_value_not_unique,none, "raw value for enum case is not unique", ()) -ERROR(enum_raw_value_magic_literal,none, - "use of '%0' literal as raw value for enum case is not supported", - (StringRef)) NOTE(enum_raw_value_used_here,none, "raw value previously used here", ()) NOTE(enum_raw_value_incrementing_from_here,none, @@ -3732,13 +3830,15 @@ ERROR(decl_from_hidden_module,none, "as result builder here|" "in an extension with public or '@usableFromInline' members|" "in an extension with conditional conformances|" - "in a public or '@usableFromInline' conformance}1; " + "in a public or '@usableFromInline' conformance|" + "in an '@available' attribute here}1; " "%select{%2 has been imported as implementation-only|" "it is an SPI imported from %2|" "it is SPI|" "%2 was imported for SPI only|" "%2 was not imported by this file|" "C++ types from imported module %2 do not support library evolution|" + "it was imported via the internal bridging header|" "%2 was not imported publicly}3", (const Decl *, unsigned, Identifier, unsigned)) ERROR(typealias_desugars_to_type_from_hidden_module,none, @@ -3747,13 +3847,15 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none, "as result builder here|" "in an extension with public or '@usableFromInline' members|" "in an extension with conditional conformance|" - "in a public or '@usableFromInline' conformance}3 " + "in a public or '@usableFromInline' conformance|" + "<>}3 " "because %select{%4 has been imported as implementation-only|" "it is an SPI imported from %4|" "<>|" "%4 was imported for SPI only|" "%4 was not imported by this file|" "C++ types from imported module %4 do not support library evolution|" + "it was imported via the internal bridging header|" "%4 was not imported publicly}5", (const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned)) ERROR(conformance_from_implementation_only_module,none, @@ -3761,13 +3863,14 @@ ERROR(conformance_from_implementation_only_module,none, "as result builder here|" "in an extension with public or '@usableFromInline' members|" "in an extension with conditional conformances|" - "<>}2; " + "<>|<>}2; " "%select{%3 has been imported as implementation-only|" "the conformance is declared as SPI in %3|" "the conformance is declared as SPI|" "%3 was imported for SPI only|" "%3 was not imported by this file|" "C++ types from imported module %3 do not support library evolution|" + "it was imported via the internal bridging header|" "%3 was not imported publicly}4", (Type, Identifier, unsigned, Identifier, unsigned)) NOTE(assoc_conformance_from_implementation_only_module,none, @@ -3826,11 +3929,13 @@ ERROR(implementation_only_override_changed_type,none, "'@_implementationOnly' override must have the same type as the " "declaration it overrides (%0)", (Type)) ERROR(implementation_only_override_without_attr,none, - "override of '@_implementationOnly' %0 should also be declared " - "'@_implementationOnly'", (DescriptiveDeclKind)) + "override of '@_implementationOnly' %kindonly0 should also be declared " + "'@_implementationOnly'", + (const ValueDecl *)) ERROR(implementation_only_override_import_without_attr,none, - "override of %0 imported as implementation-only must be declared " - "'@_implementationOnly'", (DescriptiveDeclKind)) + "override of %kindonly0 imported as implementation-only must be declared " + "'@_implementationOnly'", + (const ValueDecl *)) ERROR(import_attr_conflict,none, "%0 inconsistently imported with %1", @@ -3959,8 +4064,8 @@ NOTE(missing_member_type_conformance_prevents_synthesis, none, "of %3 to %2", (unsigned, Type, Type, Type)) NOTE(automatic_protocol_synthesis_unsupported,none, - "automatic synthesis of '%0' is not supported for %1 declarations", - (StringRef, DescriptiveDeclKind)) + "automatic synthesis of %0 is not supported for %kindonly1 declarations", + (const ProtocolDecl *, const NominalTypeDecl *)) NOTE(comparable_synthesis_raw_value_not_allowed, none, "enum declares raw type %0, preventing synthesized conformance of %1 to %2", (Type, Type, Type)) @@ -4032,10 +4137,11 @@ ERROR(attr_contains_multiple_versions_for_platform,none, "'%0' contains multiple versions for %1", (DeclAttribute, StringRef)) ERROR(override_final,none, - "%0 overrides a 'final' %1", (DescriptiveDeclKind, DescriptiveDeclKind)) + "%kindonly0 overrides a 'final' %kindonly1", + (const ValueDecl *, const ValueDecl *)) ERROR(override_static,none, - "cannot override %0", (DescriptiveDeclKind)) + "cannot override %kindonly0", (const ValueDecl *)) ERROR(member_cannot_be_final,none, "only classes and class members may be marked with 'final'", @@ -4046,16 +4152,16 @@ ERROR(final_not_allowed_here,none, "subscripts", ()) ERROR(attr_incompatible_with_non_final,none, - "'%0' cannot be applied to a non-final %1", - (DeclAttribute, DescriptiveDeclKind)) + "'%0' cannot be applied to a non-final %kindonly1", + (DeclAttribute, const ValueDecl *)) ERROR(attr_incompatible_with_override,none, "'%0' cannot be combined with 'override'", (DeclAttribute)) ERROR(attr_incompatible_with_objc,none, - "'%0' must not be used on an '@objc' %1", - (DeclAttribute, DescriptiveDeclKind)) + "'%0' must not be used on an '@objc' %kindonly1", + (DeclAttribute, const ValueDecl *)) ERROR(attr_unusable_in_protocol,none, "'%0' cannot be used inside a protocol declaration", @@ -4083,19 +4189,19 @@ ERROR(isolation_in_inherits_executor,none, "#isolation%select{| (introduced by a default argument)}0 cannot be used " "within an '@_unsafeInheritExecutor' function", (bool)) ERROR(unsafe_inherits_executor_deprecated,none, - "@_unsafeInheritExecutor attribute is deprecated; consider an " + "'@_unsafeInheritExecutor' is deprecated; consider an " "'isolated' parameter defaulted to '#isolation' instead", ()) ERROR(lifetime_invalid_global_scope,none, "%0 is only valid on methods", (DeclAttribute)) ERROR(eagermove_and_lexical_combined,none, - "@_eagerMove and @_noEagerMove attributes are alternate styles of lifetimes " + "'@_eagerMove' and '@_noEagerMove' are alternate styles of lifetimes " "and can't be combined", ()) ERROR(eagermove_and_noncopyable_combined,none, - "@_eagerMove cannot be applied to NonCopyable types", ()) + "'@_eagerMove' cannot be applied to NonCopyable types", ()) ERROR(autoclosure_function_type,none, - "@autoclosure attribute only applies to function types", + "'@autoclosure' only applies to function types", ()) ERROR(invalid_autoclosure_and_convention_attributes,none, @@ -4103,10 +4209,11 @@ ERROR(invalid_autoclosure_and_convention_attributes,none, (StringRef)) ERROR(autoclosure_function_input_nonunit,none, - "argument type of @autoclosure parameter must be '()'", ()) + "argument type of '@autoclosure' parameter must be '()'", ()) ERROR(escaping_non_function_parameter,none, - "@escaping attribute may only be used in function parameter position", ()) + "'@escaping' may only be used in function parameter position", + ()) ERROR(escaping_inout_parameter,none, "inout expression is implicitly escaping", ()) @@ -4127,34 +4234,34 @@ ERROR(instancemember_compilerinitialized,none, // @_nonEphemeral attribute ERROR(non_ephemeral_non_pointer_type,none, - "@_nonEphemeral attribute only applies to pointer types", ()) + "'@_nonEphemeral' only applies to pointer types", ()) // NSManaged attribute ERROR(attr_NSManaged_not_instance_member,none, - "@NSManaged only allowed on an instance property or method", ()) + "'@NSManaged' only allowed on an instance property or method", ()) ERROR(attr_NSManaged_not_stored,none, - "@NSManaged not allowed on %select{computed|observing|addressed}0 " + "'@NSManaged' not allowed on %select{computed|observing|addressed}0 " "properties", (unsigned)) ERROR(attr_NSManaged_let_property,none, - "@NSManaged not allowed on a 'let' property", ()) + "'@NSManaged' not allowed on a 'let' property", ()) ERROR(attr_NSManaged_initial_value,none, - "@NSManaged property cannot have an initial value", ()) + "'@NSManaged' property cannot have an initial value", ()) ERROR(attr_NSManaged_NSCopying,none, - "@NSManaged property cannot also be marked @NSCopying", ()) + "'@NSManaged' property cannot also be marked '@NSCopying'", ()) ERROR(attr_NSManaged_method_body,none, - "@NSManaged method cannot have a body; it must be provided at runtime",()) + "'@NSManaged' method cannot have a body; it must be provided at runtime",()) // NSCopying attribute ERROR(nscopying_only_on_class_properties,none, - "@NSCopying may only be used on properties in classes", + "'@NSCopying' may only be used on properties in classes", ()) ERROR(nscopying_only_mutable,none, - "@NSCopying requires property to be mutable", ()) + "'@NSCopying' requires property to be mutable", ()) ERROR(nscopying_only_stored_property,none, - "@NSCopying is only valid on stored properties", ()) + "'@NSCopying' is only valid on stored properties", ()) ERROR(nscopying_doesnt_conform,none, - "@NSCopying is only valid with types that conform to" + "'@NSCopying' is only valid with types that conform to" " the NSCopying protocol", ()) // UIApplicationMain/NSApplicationMain attribute @@ -4181,7 +4288,7 @@ ERROR(attr_ApplicationMain_deprecated,none, "%" SELECT_APPLICATION_MAIN "0 is deprecated", (unsigned)) NOTE(attr_ApplicationMain_deprecated_use_attr_main,none, - "use @main instead", ()) + "use '@main' instead", ()) NOTE(attr_ApplicationMain_parse_as_library,none, "pass '-parse-as-library' to compiler invocation if this is intentional", @@ -4191,25 +4298,35 @@ NOTE(attr_ApplicationMain_script_here,none, ()) ERROR(attr_MainType_without_main,none, - "%0 is annotated with @main and must provide a main static function of type %" - SELECT_APPLICATION_MAIN_TYPES "1", + "%0 is annotated with '@main' and must provide a main static function " + "of type %" SELECT_APPLICATION_MAIN_TYPES "1", (const ValueDecl *, bool)) +NOTE(note_add_main_sync,none, + "add 'static func main()'", ()) +NOTE(note_add_main_sync_throws,none, + "add 'static func main() throws'", ()) +NOTE(note_add_main_async,none, + "add 'static func main() async'", ()) +NOTE(note_add_main_async_throws,none, + "add 'static func main() async throws'", ()) #undef SELECT_APPLICATION_MAIN_TYPES #undef SELECT_APPLICATION_MAIN #undef SELECT_APPLICATION_DELEGATE ERROR(attr_rawlayout_experimental,none, - "the @_rawLayout attribute is experimental", ()) + "the '@_rawLayout' attribute is experimental", ()) ERROR(attr_rawlayout_cannot_be_copyable,none, - "type with @_rawLayout cannot be copied and must be declared ~Copyable", ()) + "type with '@_rawLayout' cannot be copied and must be declared ~Copyable", + ()) ERROR(attr_rawlayout_cannot_have_stored_properties,none, - "type with @_rawLayout cannot have stored properties", ()) + "type with '@_rawLayout' cannot have stored properties", ()) ERROR(attr_rawlayout_cannot_have_alignment_attr,none, - "type with @_rawLayout cannot also have an @_alignment attribute", ()) + "type with '@_rawLayout' cannot also have an '@_alignment' attribute", ()) ERROR(attr_rawlayout_invalid_count_type,none, - "@_rawLayout count must either be integer literal or a generic argument", ()) - + "'@_rawLayout' count must either be integer literal or a generic " + "argument", ()) + // lazy ERROR(lazy_not_on_let,none, "'lazy' cannot be used on a let", ()) @@ -4233,14 +4350,14 @@ ERROR(lazy_var_storage_access,none, // Debugger function attribute. ERROR(attr_for_debugger_support_only,none, - "@LLDBDebuggerSupport may only be used when debugger support is on", ()) + "'@LLDBDebuggerSupport' may only be used when debugger support is on", ()) // @_implements ERROR(implements_attr_protocol_lacks_member,none, "protocol %0 has no member %1", (const ProtocolDecl *, DeclName)) ERROR(implements_attr_non_protocol_type,none, - "non-protocol type in @_implements attribute", ()) + "non-protocol type in '@_implements'", ()) ERROR(implements_attr_protocol_not_conformed_to,none, "containing type %0 does not conform to protocol %1", @@ -4331,6 +4448,9 @@ NOTE(derivative_attr_fix_access,none, "mark the derivative function as " "'%select{private|fileprivate|internal|package|@usableFromInline|@usableFromInline}0' " "to match the original function", (AccessLevel)) +ERROR(derivative_attr_always_emit_into_client_mismatch,none, + "either both or none of derivative and original function must have " + "@alwaysEmitIntoClient attribute", ()) ERROR(derivative_attr_static_method_mismatch_original,none, "unexpected derivative function declaration; " "%0 requires the derivative function %1 to be %select{an instance|a 'static'}2 method", @@ -4373,22 +4493,23 @@ NOTE(transpose_attr_static_method_mismatch_fix,none, ERROR(autodiff_attr_original_decl_ambiguous,none, "referenced declaration %0 is ambiguous", (DeclNameRef)) NOTE(autodiff_attr_original_decl_ambiguous_candidate,none, - "candidate %0 found here", (DescriptiveDeclKind)) + "candidate %kindonly0 found here", (const AbstractFunctionDecl *)) ERROR(autodiff_attr_original_decl_none_valid,none, "referenced declaration %0 could not be resolved", (DeclNameRef)) NOTE(autodiff_attr_original_decl_invalid_kind,none, - "candidate %0 is not a 'func', 'init', 'subscript', or 'var' computed " - "property declaration", (DescriptiveDeclKind)) + "candidate %kindonly0 is not a 'func', 'init', 'subscript', or 'var' " + "computed property declaration", + (const ValueDecl *)) NOTE(autodiff_attr_original_decl_missing_accessor,none, - "candidate %0 does not have a %1", - (DescriptiveDeclKind, /*accessorDeclKind*/ DescriptiveDeclKind)) + "candidate %kindonly0 does not have a %1", + (const ValueDecl *, /*accessorDeclKind*/ DescriptiveDeclKind)) NOTE(autodiff_attr_original_decl_type_mismatch,none, - "candidate %0 does not have " + "candidate %kindonly0 does not have " "%select{expected type|type equal to or less constrained than}2 %1", - (DescriptiveDeclKind, Type, /*hasGenericSignature*/ bool)) + (const ValueDecl *, Type, /*hasGenericSignature*/ bool)) NOTE(autodiff_attr_original_decl_not_same_type_context,none, - "candidate %0 is not defined in the current type context", - (DescriptiveDeclKind)) + "candidate %kindonly0 is not defined in the current type context", + (const ValueDecl *)) ERROR(autodiff_attr_original_void_result,none, "cannot differentiate void function %0", (DeclName)) ERROR(autodiff_attr_result_not_differentiable,none, @@ -4648,15 +4769,10 @@ ERROR(could_not_infer_pack_element,none, NOTE(note_in_opening_pack_element,none, "in inferring pack element #%0 of '%1'", (unsigned,StringRef)) - -ERROR(type_of_expression_is_ambiguous,none, - "type of expression is ambiguous without a type annotation", ()) - -ERROR(failed_to_produce_diagnostic,Fatal, +ERROR(failed_to_produce_diagnostic,none, "failed to produce diagnostic for expression; " SWIFT_BUG_REPORT_MESSAGE, ()) - ERROR(missing_protocol,none, "missing protocol %0", (Identifier)) ERROR(nil_literal_broken_proto,none, @@ -4754,10 +4870,6 @@ NOTE(descriptive_generic_type_declared_here,none, ERROR(placeholder_type_not_allowed,none, "type placeholder not allowed here", ()) -ERROR(placeholder_type_not_allowed_in_return_type,none, - "type placeholder may not appear in function return type", ()) -ERROR(placeholder_type_not_allowed_in_parameter,none, - "type placeholder may not appear in top-level parameter", ()) ERROR(placeholder_type_not_allowed_in_pattern,none, "placeholder type may not appear as type of a variable", ()) NOTE(replace_placeholder_with_inferred_type,none, @@ -4851,8 +4963,8 @@ NOTE(add_self_to_type,none, "use '.self' to reference the type object", ()) WARNING(warn_unqualified_access,none, - "use of %0 treated as a reference to %1 in %kind2", - (Identifier, DescriptiveDeclKind, const ValueDecl *)) + "use of %base0 treated as a reference to %kindonly0 in %kind1", + (const ValueDecl *, const ValueDecl *)) WARNING(self_refers_to_method,none, "'self' refers to the method '%0.self', which may be unexpected", (StringRef)) @@ -4861,22 +4973,22 @@ NOTE(fix_unqualified_access_member,none, NOTE(fix_unqualified_access_member_named_self,none, "use '%0.self' to silence this warning", (StringRef)) NOTE(fix_unqualified_access_top_level,none, - "use '%0' to reference the %1", - (StringRef, DescriptiveDeclKind, Identifier)) + "use '%0' to reference the %kindonly1", + (StringRef, const ValueDecl *, const ModuleDecl *)) NOTE(fix_unqualified_access_top_level_multi,none, - "use '%0' to reference the %1 in module %2", - (StringRef, DescriptiveDeclKind, Identifier)) + "use '%0' to reference the %kindonly1 in module %2", + (StringRef, const ValueDecl *, const ModuleDecl *)) WARNING(warn_deprecated_conditional_conformance_outer_access,Deprecation, - "use of %0 as reference to %1 in %kind2 will change in future versions " - "of Swift to reference %3 in %kind4 which comes via a conditional " - "conformance", + "use of %0 as reference to %kindonly1 in %kind2 will change in future " + "versions of Swift to reference %kindonly3 in %kind4 which comes via " + "a conditional conformance", (DeclNameRef, - /*current*/DescriptiveDeclKind, const ValueDecl *, - /*future*/ DescriptiveDeclKind, const ValueDecl *)) + /*current*/const ValueDecl *, const ValueDecl *, + /*future*/const ValueDecl *, const ValueDecl *)) NOTE(fix_deprecated_conditional_conformance_outer_access,none, - "use '%0' to continue to reference the %1", - (StringRef, DescriptiveDeclKind)) + "use '%0' to continue to reference the %kindonly1", + (StringRef, const ValueDecl *)) ERROR(unsupported_special_decl_ref, none, "referencing %0 as a function value is not implemented", (Identifier)) @@ -4887,7 +4999,7 @@ WARNING(bitcasting_away_noescape, none, "'withoutActuallyEscaping' to temporarily escape a function", (Type, Type)) WARNING(bitcasting_to_change_function_rep, none, - "'unsafeBitCast' from function type %0 to %1 changes @convention and " + "'unsafeBitCast' from function type %0 to %1 changes '@convention' and " "is undefined; use an implicit conversion to change conventions", (Type, Type)) WARNING(bitcasting_to_downcast, none, @@ -4958,6 +5070,8 @@ NOTE(iuo_to_any_coercion_note_func_result,none, (const ValueDecl *)) NOTE(default_optional_to_any,none, "provide a default value to avoid this warning", ()) +NOTE(default_optional_parameter,none, + "use a default value parameter to avoid this warning", ()) NOTE(force_optional_to_any,none, "force-unwrap the value to avoid this warning", ()) NOTE(silence_optional_to_any,none, @@ -4971,43 +5085,48 @@ NOTE(silence_debug_description_in_interpolation_segment_call,none, "use 'String(describing:)' to silence this warning", ()) NOTE(noescape_parameter,none, - "parameter %1 is implicitly %select{non-escaping|non-sendable}0", + "parameter %1 is implicitly %select{non-escaping|non-Sendable}0", (unsigned, Identifier)) NOTE(generic_parameters_always_escaping,none, "generic parameters are always considered '@escaping'", ()) ERROR(passing_noattrfunc_to_attrfunc,none, - "passing %select{non-escaping|non-sendable}0 parameter %1 to function " - "expecting %select{an @escaping|a @Sendable}0 closure", + "passing %select{non-escaping|non-Sendable}0 parameter %1 to function " + "expecting %select{an '@escaping'|a '@Sendable'}0 closure", (unsigned, Identifier)) ERROR(converting_noescape_param_to_generic_type,none, "converting non-escaping parameter %0 to generic parameter %1 may allow it to escape", (Identifier, Type)) ERROR(assigning_noattrfunc_to_attrfunc,none, - "assigning %select{non-escaping|non-sendable}0 parameter %1 to " - "%select{an @escaping|a @Sendable}0 closure", + "assigning %select{non-escaping|non-Sendable}0 parameter %1 to " + "%select{an '@escaping'|a '@Sendable'}0 closure", (unsigned, Identifier)) ERROR(general_noattrfunc_to_attr,none, - "using %select{non-escaping|non-sendable}0 parameter %1 in a context " - "expecting %select{an @escaping|a @Sendable}0 closure", + "using %select{non-escaping|non-Sendable}0 parameter %1 in a context " + "expecting %select{an '@escaping'|a '@Sendable'}0 closure", (unsigned, Identifier)) ERROR(converting_noattrfunc_to_type,none, - "converting %select{non-escaping|non-sendable function}0 value to %1 " + "converting %select{non-escaping|non-Sendable function}0 value to %1 " "may %select{allow it to escape|introduce data races}0", (unsigned, Type)) NOTE(escape_expected_at_parameter_position,none, "parameter #%0 expects escaping value of type %1", (unsigned, Type)) NOTE(add_explicit_escaping,none, - "add explicit @escaping to function parameter #%0", + "add explicit '@escaping' to function parameter #%0", (unsigned)) ERROR(converting_func_loses_global_actor,none, "converting function value of type %0 to %1 loses global actor %2", (Type, Type, Type)) ERROR(capture_across_type_decl,none, - "%0 declaration cannot close over value %1 defined in outer scope", - (DescriptiveDeclKind, Identifier)) + "%kindonly0 declaration cannot close over value %base1 defined in outer " + "scope", + (const NominalTypeDecl *, const ValueDecl *)) + +ERROR(isolated_any_conversion_to_synchronous_func,none, + "converting @isolated(any) function of type %0 to synchronous function type %1 is not allowed", + (Type, Type)) ERROR(reference_to_invalid_decl,none, "cannot reference invalid declaration %0", (const ValueDecl *)) @@ -5106,13 +5225,16 @@ ERROR(assignment_bang_has_immutable_subcomponent,none, NOTE(candidate_is_not_assignable,none, "candidate is not assignable: %kind0", (const ValueDecl *)) +NOTE(candidate_expects_inout_argument,none, + "candidate expects in-out value for parameter #%0 but argument is immutable", + (unsigned)) NOTE(change_to_mutating,none, "mark %select{method|%1}0 'mutating' to make 'self' mutable", (bool, StringRef)) NOTE(masked_mutable_property,none, - "add explicit '%0' to refer to mutable %1 of %2", - (StringRef, DescriptiveDeclKind, Type)) + "add explicit '%0' to refer to mutable %kindonly1 of %2", + (StringRef, const VarDecl *, Type)) ERROR(assignment_let_property_delegating_init,none, "'let' property %0 may not be initialized directly; use " @@ -5214,14 +5336,16 @@ GROUPED_ERROR(opaque_type_var_no_init,OpaqueTypeInference,none, GROUPED_ERROR(opaque_type_var_no_underlying_type,OpaqueTypeInference,none, "property declares an opaque return type, but cannot infer the " "underlying type from its initializer expression", ()) - +GROUPED_ERROR(opaque_type_unsupported_availability,OpaqueTypeInference,none, + "opaque return type cannot depend on %0 availability", + (AvailabilityDomain)) //------------------------------------------------------------------------------ // MARK: Discard Statement //------------------------------------------------------------------------------ ERROR(discard_wrong_context_decl,none, - "'discard' statement cannot appear in %0", - (DescriptiveDeclKind)) + "'discard' statement cannot appear in %kindonly0", + (const Decl *)) ERROR(discard_no_deinit,none, "'discard' has no effect for type %0 unless it has a deinitializer", (Type)) @@ -5233,10 +5357,10 @@ ERROR(discard_wrong_context_misc,none, ()) ERROR(discard_wrong_context_copyable,none, "'discard' statement can only appear in noncopyable type's member", - (DescriptiveDeclKind)) + ()) ERROR(discard_wrong_context_nonconsuming,none, - "'discard' statement can only appear in consuming %0", - (DescriptiveDeclKind)) + "'discard' statement can only appear in consuming %kindonly0", + (const FuncDecl *)) ERROR(discard_wrong_not_self,none, "you can only discard 'self'", ()) ERROR(discard_wrong_module,none, @@ -5287,8 +5411,8 @@ ERROR(closure_tuple_parameter_destructuring_implicit,none, "closure tuple parameter %0 does not support destructuring " "with implicit parameters", (Type)) ERROR(single_tuple_parameter_mismatch_special,none, - "%0 expects a single parameter of type %1%2", - (DescriptiveDeclKind, Type, StringRef)) + "%kindonly0 expects a single parameter of type %1%2", + (const ValueDecl *, Type, StringRef)) ERROR(single_tuple_parameter_mismatch_normal,none, "%kindbase0 expects a single parameter of type %1%2", (const ValueDecl *, Type, StringRef)) @@ -5436,6 +5560,10 @@ WARNING(no_throw_in_do_with_catch,none, ERROR(thrown_type_not_error,none, "thrown type %0 does not conform to the 'Error' protocol", (Type)) +ERROR(typed_throw_in_objc_forbidden,none, + "typed 'throws' %kindonly0 cannot be represented in Objective-C", + (const AbstractFunctionDecl *)) + //------------------------------------------------------------------------------ // MARK: Concurrency //------------------------------------------------------------------------------ @@ -5470,15 +5598,21 @@ FIXIT(insert_globalactor_attr, "@%0 ", (Type)) ERROR(main_function_must_be_mainActor,none, "main() must be '@MainActor'", ()) +// Keep aligned with enum ForeignLanguage +#define FOREIGN_LANG_SELECT "select{C|Objective-C}" + ERROR(not_objc_function_async,none, - "'async' %0 cannot be represented in Objective-C", (DescriptiveDeclKind)) + "'async' %kindonly0 cannot be represented in Objective-C", + (const AbstractFunctionDecl *)) NOTE(not_objc_function_type_async,none, - "'async' function types cannot be represented in Objective-C", ()) + "'async' function types cannot be represented " + "in %" FOREIGN_LANG_SELECT "0", (ForeignLanguage)) ERROR(actor_isolated_objc,none, - "actor-isolated %kind0 cannot be @objc", + "actor-isolated %kind0 cannot be '@objc'", (const ValueDecl *)) NOTE(protocol_witness_async_conflict,none, - "candidate is %select{not |}0'async', but%select{| @objc}1 protocol requirement is%select{| not}0", + "candidate is %select{not |}0'async', but%select{| '@objc'}1 protocol " + "requirement is%select{| not}0", (bool, bool)) ERROR(async_autoclosure_nonasync_function,none, "'async' autoclosure parameter in a non-'async' function", ()) @@ -5560,6 +5694,9 @@ ERROR(actor_isolated_non_self_reference,none, "from a nonisolated autoclosure}2", (const ValueDecl *, unsigned, unsigned, Type, ActorIsolation)) +ERROR(actor_isolated_access_outside_of_actor_context,none, + "%0 %kind1 cannot be %select{accessed|called}2 from outside of the actor", + (ActorIsolation, const ValueDecl *, bool)) ERROR(distributed_actor_isolated_non_self_reference,none, "distributed actor-isolated %kind0 can not be accessed from a " "nonisolated context", @@ -5571,8 +5708,8 @@ ERROR(conflicting_stored_property_isolation,none, "%select{default|memberwise}0 initializer for %1 cannot be both %2 and %3", (bool, Type, ActorIsolation, ActorIsolation)) NOTE(property_requires_actor,none, - "initializer for %0 %1 is %2", - (DescriptiveDeclKind, Identifier, ActorIsolation)) + "initializer for %kind0 is %1", + (const VarDecl *, ActorIsolation)) ERROR(isolated_default_argument_context,none, "%0 default value in a %1 context", (ActorIsolation, ActorIsolation)) @@ -5607,8 +5744,9 @@ ERROR(actor_isolated_keypath_component,none, "cannot form key path to %0 %kind1", (ActorIsolation, const ValueDecl *)) ERROR(effectful_keypath_component,none, - "cannot form %select{key path|dynamic key path member lookup}1 to %0 with 'throws' or 'async'", - (DescriptiveDeclKind, bool)) + "cannot form %select{key path|dynamic key path member lookup}1 to " + "%kindonly0 with 'throws' or 'async'", + (const ValueDecl *, bool)) ERROR(local_function_executed_concurrently,none, "concurrently-executed %kind0 must be marked as '@Sendable'", (const ValueDecl *)) @@ -5627,15 +5765,15 @@ GROUPED_ERROR(concurrent_access_of_inout_param,SendableClosureCaptures,none, "concurrently-executing code", (DeclName)) GROUPED_ERROR(non_sendable_capture,SendableClosureCaptures,none, - "capture of %1 with non-sendable type %0 in a '@Sendable' " + "capture of %1 with non-Sendable type %0 in a '@Sendable' " "%select{local function|closure}2", (Type, DeclName, bool)) ERROR(non_sendable_isolated_capture,none, - "capture of %1 with non-sendable type %0 in an isolated " + "capture of %1 with non-Sendable type %0 in an isolated " "%select{local function|closure}2", (Type, DeclName, bool)) -ERROR(non_sendable_metatype_capture,none, - "capture of non-sendable type %0 in an isolated " +GROUPED_ERROR(non_sendable_metatype_capture,SendableMetatypes,none, + "capture of non-Sendable type %0 in an isolated " "%select{local function|closure}1", (Type, bool)) ERROR(self_capture_deinit_task,none, @@ -5667,8 +5805,9 @@ ERROR(distributed_local_cannot_be_used,none, "'local' cannot be used in user-defined code currently", ()) ERROR(distributed_actor_func_param_not_codable,none, - "parameter '%0' of type %1 in %2 does not conform to serialization requirement '%3'", - (StringRef, Type, DescriptiveDeclKind, StringRef)) + "parameter %0 of type %1 in %kindonly2 does not conform to " + "serialization requirement '%3'", + (Identifier, Type, const AbstractFunctionDecl *, StringRef)) ERROR(distributed_actor_target_result_not_codable,none, "result type %0 of %kindbase1 does not conform to serialization requirement '%2'", (Type, const ValueDecl *, StringRef)) @@ -5696,8 +5835,8 @@ ERROR(conformance_missing_distributed,none, NOTE(note_add_distributed_multi,none, "mark all declarations used in the conformance 'distributed'", ()) NOTE(actor_mutable_state,none, - "mutation of this %0 is only permitted within the actor", - (DescriptiveDeclKind)) + "mutation of this %kindonly0 is only permitted within the actor", + (const ValueDecl *)) ERROR(shared_mutable_state_access,none, "reference to %kind0 is not concurrency-safe because it involves shared " "mutable state", (const ValueDecl *)) @@ -5717,7 +5856,7 @@ NOTE(note_actor_isolated_witness,none, "%0 %kind1 cannot satisfy %2 requirement", (ActorIsolation, const ValueDecl *, ActorIsolation)) ERROR(actor_cannot_conform_to_global_actor_protocol,none, - "actor %0 cannot conform to global actor isolated protocol %1", + "actor %0 cannot conform to global-actor-isolated protocol %1", (Type, Type)) NOTE(protocol_isolated_to_global_actor_here,none, "%0 is isolated to global actor %1 here", (Type, Type)) @@ -5744,15 +5883,15 @@ ERROR(isolated_parameter_duplicate_type,none, NOTE(isolated_parameter_previous_note,none, "previous 'isolated' parameter %0", (DeclName)) ERROR(isolated_parameter_combined_global_actor_attr,none, - "%0 with 'isolated' parameter cannot have a global actor", - (DescriptiveDeclKind)) + "%kindonly0 with 'isolated' parameter cannot have a global actor", + (const ValueDecl *)) ERROR(isolated_parameter_closure_combined_global_actor_attr,none, "closure with 'isolated' parameter %0 cannot have a global actor", (DeclName)) ERROR(isolated_parameter_global_actor_type,none, "function type cannot have global actor and 'isolated' parameter", ()) ERROR(isolated_parameter_combined_nonisolated,none, - "%0 with 'isolated' parameter cannot be 'nonisolated'", - (DescriptiveDeclKind)) + "%kindonly0 with 'isolated' parameter cannot be 'nonisolated'", + (const ValueDecl *)) ERROR(isolated_parameter_isolated_attr_type,none, "function with 'isolated' parameter cannot also be '@isolated(%0)'", (StringRef)) @@ -5767,82 +5906,82 @@ NOTE(in_derived_conformance, none, "in derived conformance to %0", (Type)) NOTE(in_derived_witness, none, - "in %0 %1 for derived conformance to %2", - (DescriptiveDeclKind, DeclName, Type)) + "in %kindonly0 %1 for derived conformance to %2", + (const ValueDecl *, DeclName, Type)) ERROR(non_sendable_arg_into_actor,none, - "non-sendable type %0 cannot be sent into %2 context in call to %kind1", + "non-Sendable type %0 cannot be sent into %2 context in call to %kind1", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_arg_exits_actor,none, - "non-sendable type %0 cannot exit %2 context in call to nonisolated " + "non-Sendable type %0 cannot exit %2 context in call to nonisolated " "%kind1", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_param_in_witness,none, - "non-sendable parameter type %0 cannot be sent from caller of " + "non-Sendable parameter type %0 cannot be sent from caller of " "protocol requirement %1 into %2 implementation", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_param_in_override,none, - "non-sendable parameter type %0 cannot be sent from caller of " + "non-Sendable parameter type %0 cannot be sent from caller of " "superclass %kind1 into %2 override", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_param_in_objc,none, - "non-sendable parameter type %0 of %2 '@objc' %kind1 cannot cross actor " + "non-Sendable parameter type %0 of %2 '@objc' %kind1 cannot cross actor " "boundary", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_result_into_actor,none, - "non-sendable result type %0 cannot be sent from %2 context in call " + "non-Sendable result type %0 cannot be sent from %2 context in call " "to %kind1", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_result_exits_actor,none, - "non-sendable result type %0 cannot exit %2 context in call to " + "non-Sendable result type %0 cannot exit %2 context in call to " "nonisolated %kind1", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_result_in_witness,none, - "non-sendable type %0 cannot be returned from %2 implementation " + "non-Sendable type %0 cannot be returned from %2 implementation " "to caller of protocol requirement %1", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_result_in_override,none, - "non-sendable type %0 cannot be returned from %2 override to " + "non-Sendable type %0 cannot be returned from %2 override to " "caller of superclass %kind1", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_result_in_objc,none, - "non-sendable type %0 returned by %2 '@objc' %kind1 cannot cross " + "non-Sendable type %0 returned by %2 '@objc' %kind1 cannot cross " "actor boundary", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_call_result_type,none, - "non-sendable result type %0 cannot be sent from %1 context in call " + "non-Sendable result type %0 cannot be sent from %1 context in call " "to async function", (Type, ActorIsolation)) ERROR(non_sendable_property_exits_actor,none, - "non-sendable type %0 of %kind1 cannot exit %2 context", + "non-Sendable type %0 of %kind1 cannot exit %2 context", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_property_into_actor,none, - "non-sendable type %0 of nonisolated %kind1 cannot be sent to " + "non-Sendable type %0 of nonisolated %kind1 cannot be sent to " "%2 context", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_property_in_witness,none, - "non-sendable type %0 cannot be returned from %2 implementation " + "non-Sendable type %0 cannot be returned from %2 implementation " "to caller of protocol requirement %1", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_property_in_override,none, - "non-sendable type %0 cannot be returned from %2 override to " + "non-Sendable type %0 cannot be returned from %2 override to " "caller of superclass %kind1", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_property_in_objc,none, - "non-sendable type %0 returned by %2 '@objc' %kind1 cannot cross " + "non-Sendable type %0 returned by %2 '@objc' %kind1 cannot cross " "actor boundary", (Type, const ValueDecl *, ActorIsolation)) ERROR(non_sendable_keypath_capture,none, - "cannot form key path that captures non-sendable type %0", + "cannot form key path that captures non-Sendable type %0", (Type)) ERROR(non_concurrent_type_member,none, "%select{stored property %2|associated value %2}1 of " - "'Sendable'-conforming %kind3 has non-sendable type %0", - (Type, bool, DeclName, const ValueDecl *)) + "'Sendable'-conforming %kind3 %select{contains|has}4 non-Sendable type %0", + (Type, bool, DeclName, const ValueDecl *, bool)) ERROR(concurrent_value_class_mutable_property,none, "stored property %0 of 'Sendable'-conforming %kind1 is mutable", (DeclName, const ValueDecl *)) @@ -5851,30 +5990,38 @@ ERROR(concurrent_value_outside_source_file,none, "%kind0; use '@unchecked Sendable' for retroactive conformance", (const ValueDecl *)) ERROR(concurrent_value_nonfinal_class,none, - "non-final class %0 cannot conform to 'Sendable'; " - "use '@unchecked Sendable'", (DeclName)) + "non-final class %0 cannot conform to the 'Sendable' protocol", (DeclName)) ERROR(concurrent_value_inherit,none, "'Sendable' class %1 cannot inherit from another class" "%select{| other than 'NSObject'}0", (bool, DeclName)) ERROR(non_sendable_type,none, "type %0 does not conform to the 'Sendable' protocol", (Type)) +GROUPED_ERROR(non_sendable_metatype_type,SendableMetatypes,none, + "type %0 does not conform to the 'SendableMetatype' protocol", (Type)) ERROR(sendable_raw_storage,none, - "struct %0 with @_rawLayout does not conform to the 'Sendable' protocol", (DeclName)) + "struct %0 with '@_rawLayout' does not conform to the 'Sendable' " + "protocol", (DeclName)) ERROR(typeattr_not_inheritance_clause,none, - "'%0' attribute only applies in inheritance clauses", (StringRef)) + "%0 only applies in inheritance clauses", + (const TypeAttribute *)) +ERROR(typeattr_not_extension_inheritance_clause,none, + "%0 only applies in inheritance clauses in extensions", + (const TypeAttribute *)) ERROR(typeattr_not_existential,none, - "'%0' attribute cannot apply to non-protocol type %1", (StringRef, Type)) + "%0 cannot apply to non-protocol type %1", + (const TypeAttribute *, Type)) WARNING(unchecked_conformance_not_special,none, - "@unchecked conformance to %0 has no meaning", (Type)) + "'@unchecked' conformance to %0 has no meaning", (Type)) WARNING(restate_unchecked_sendable,none, "class %0 must restate inherited '@unchecked Sendable' conformance", (DeclName)) WARNING(preconcurrency_conformance_not_used,none, - "@preconcurrency attribute on conformance to %0 has no effect", (Type)) + "'@preconcurrency' on conformance to %0 has no effect", + (Type)) ERROR(redundant_any_in_existential,none, "redundant 'any' in type %0", @@ -5912,9 +6059,9 @@ WARNING(nonisolated_unsafe_sendable_actor_constant,none, "property with 'Sendable' type %0, consider removing it", (Type)) WARNING(nonisolated_unsafe_uneffective_on_funcs,none, - "'nonisolated(unsafe)' has no effect on %0 %1, " + "'nonisolated(unsafe)' has no effect on %kind0, " "consider using 'nonisolated'", - (DescriptiveDeclKind, const ValueDecl*)) + (const ValueDecl *)) ERROR(nonisolated_non_sendable,none, "'nonisolated' can not be applied to variable with non-'Sendable' " "type %0", @@ -5925,16 +6072,11 @@ ERROR(nonisolated_local_var,none, ERROR(nonisolated_actor_sync_init,none, "'nonisolated' on an actor's synchronous initializer is invalid", ()) -ERROR(nonisolated_wrapped_property,none, - "'nonisolated' is not supported on properties with property wrappers", - ()) -ERROR(nonisolated_lazy,none, - "'nonisolated' is not supported on lazy properties", - ()) ERROR(non_sendable_from_deinit,none, - "cannot access %1 %2 with a non-sendable type %0 from nonisolated deinit", - (Type, DescriptiveDeclKind, DeclName)) + "cannot access %kind1 with a non-Sendable type %0 from nonisolated " + "deinit", + (Type, const VarDecl *)) ERROR(actor_instance_property_wrapper,none, "%0 property in property wrapper type %1 cannot be isolated to " @@ -5984,7 +6126,7 @@ 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)) + "%kindonly0 cannot have a global actor", (const Decl *)) ERROR(global_actor_on_actor_class,none, "actor %0 cannot have a global actor", (Identifier)) ERROR(global_actor_on_local_variable,none, @@ -6015,7 +6157,7 @@ ERROR(actor_isolation_multiple_attr_2,none, "%kind0 has multiple actor-isolation attributes (%1 and %2)", (const Decl *, DeclAttribute, DeclAttribute)) ERROR(actor_isolation_multiple_attr_3,none, - "%0 %1 has multiple actor-isolation attributes (%2, %3 and %4)", + "%0 %1 has multiple actor-isolation attributes (%2, %3, and %4)", (const Decl *, DeclAttribute, DeclAttribute, DeclAttribute)) ERROR(actor_isolation_multiple_attr_4,none, "%0 %1 has multiple actor-isolation attributes (%2, %3, %4, and %5)", @@ -6029,8 +6171,8 @@ ERROR(actor_isolation_superclass_mismatch,none, (ActorIsolation, const ClassDecl *, ActorIsolation, const ClassDecl *)) ERROR(async_decl_must_be_available_from_async,none, - "asynchronous %0 must be available from asynchronous contexts", - (DescriptiveDeclKind)) + "asynchronous %kindonly0 must be available from asynchronous contexts", + (const Decl *)) ERROR(async_named_decl_must_be_available_from_async,none, "asynchronous %kind0 must be available from asynchronous contexts", (const ValueDecl *)) @@ -6224,6 +6366,9 @@ ERROR(invalid_ownership_protocol_type,none, ERROR(invalid_ownership_incompatible_class,none, "%0 is incompatible with %1 references", (Type, ReferenceOwnership)) +ERROR(invalid_ownership_incompatible_foreign_reference_type,none, + "%0 is incompatible with 'weak' references, because it is a foreign reference type", + (Type)) ERROR(invalid_ownership_with_optional,none, "%0 variable cannot have optional type", (ReferenceOwnership)) ERROR(invalid_ownership_not_optional,none, @@ -6263,8 +6408,8 @@ NOTE(overridden_required_initializer_here,none, "overridden required initializer is here", ()) // Functions -ERROR(attribute_requires_function_type,none, - "@%0 attribute only applies to function types", (StringRef)) +ERROR(type_attr_requires_function_type,none, + "%0 only applies to function types", (const TypeAttribute *)) ERROR(generic_function_type,none, "function values cannot be generic", ()) ERROR(unsupported_convention,none, @@ -6276,8 +6421,9 @@ ERROR(unreferenced_generic_parameter,NoUsage, "generic parameter '%0' is not used in function signature", (StringRef)) ERROR(unexpected_ctype_for_non_c_convention,none, "convention '%0' does not support the 'cType' argument label, did you " - "mean @convention(c, cType: \"%1\") or @convention(block, cType: \"%1\") " - "instead?", (StringRef, StringRef)) + "mean '@convention(c, cType: \"%1\")' or " + "'@convention(block, cType: \"%1\")' instead?", + (StringRef, StringRef)) ERROR(unable_to_parse_c_function_type,none, "unable to parse '%0'; it should be a C function pointer type or a " "block pointer type", (StringRef)) @@ -6331,20 +6477,21 @@ ERROR(differentiable_function_type_no_differentiability_parameters, // SIL ERROR(opened_bad_constraint_type,none, - "@opened constraint type %0 is not a protocol or protocol composition", (Type)) + "'@opened' constraint type %0 is not a protocol or protocol composition", + (Type)) ERROR(opened_bad_interface_type,none, - "@opened interface type %0 is not a type parameter", (Type)) + "'@opened' interface type %0 is not a type parameter", (Type)) ERROR(sil_function_input_label,PointsToFirstBadToken, "SIL function types cannot have labeled inputs", ()) ERROR(sil_non_coro_yields,PointsToFirstBadToken, - "non-coroutine SIL function types cannot have @yield results", ()) + "non-coroutine SIL function types cannot have '@yield' results", ()) ERROR(sil_function_invalid_convention,PointsToFirstBadToken, "convention attribute isn't valid on a %select{parameter|result|callee}0", (unsigned)) ERROR(ast_subst_function_type,none, "substitutions cannot be provided on a formal function type", ()) ERROR(sil_function_multiple_error_results,PointsToFirstBadToken, - "SIL function types cannot have multiple @error results", ()) + "SIL function types cannot have multiple '@error' results", ()) ERROR(unsupported_sil_convention,none, "convention '%0' not supported in SIL", (StringRef)) ERROR(illegal_sil_type,none, @@ -6352,17 +6499,19 @@ ERROR(illegal_sil_type,none, ERROR(sil_box_arg_mismatch,none, "SIL box type has wrong number of generic arguments for layout", ()) ERROR(sil_pack_element_uuid_not_found,none, - "open_pack_element instruction not found for @pack_element type UUID; " + "open_pack_element instruction not found for '@pack_element' type UUID; " "possible forward reference?", ()) // SIL Metatypes ERROR(sil_metatype_without_repr,none, - "metatypes in SIL must have @thin, @thick, or @objc_metatype attribute", + "metatypes in SIL must be '@thin', '@thick', or '@objc_metatype'", ()) ERROR(sil_metatype_multiple_reprs,none, - "metatypes in SIL can only be one of @thin, @thick, or @objc_metatype", + "metatypes in SIL can only be one of '@thin', '@thick', or " + "'@objc_metatype'", ()) ERROR(sil_metatype_not_metatype,none, - "@thin, @thick, or @objc_metatype can only apply to metatype types in SIL", + "'@thin', '@thick', or '@objc_metatype' can only apply to metatype " + "types in SIL", ()) //------------------------------------------------------------------------------ @@ -6377,14 +6526,14 @@ ERROR(attr_used_without_required_module, none, (DeclAttribute, Identifier)) ERROR(invalid_objc_decl_context,none, - "@objc can only be used with members of classes, @objc protocols, and " - "concrete extensions of classes", ()) + "'@objc' can only be used with members of classes, '@objc' protocols, " + "and concrete extensions of classes", ()) ERROR(invalid_objc_decl,none, "only classes (and their extensions), non-marker protocols, methods, " "initializers, properties, and subscript declarations can be declared" - " @objc", ()) + " '@objc'", ()) ERROR(invalid_objc_swift_rooted_class,none, - "only classes that inherit from NSObject can be declared @objc", ()) + "only classes that inherit from NSObject can be declared '@objc'", ()) NOTE(invalid_objc_swift_root_class_insert_nsobject,none, "inherit from 'NSObject' to silence this error", ()) @@ -6400,22 +6549,24 @@ NOTE(objc_header_sorting_arbitrary_please_report,none, "declarations", ()) ERROR(invalid_nonobjc_decl,none, - "only class members and extensions of classes can be declared @nonobjc", ()) + "only class members and extensions of classes can be declared " + "'@nonobjc'", ()) ERROR(invalid_nonobjc_extension,none, - "only extensions of classes can be declared @nonobjc", ()) + "only extensions of classes can be declared '@nonobjc'", ()) ERROR(objc_in_extension_context,none, - "members of constrained extensions cannot be declared @objc", ()) + "members of constrained extensions cannot be declared '@objc'", ()) ERROR(objc_in_generic_extension,none, "extensions of %select{classes from generic context|generic classes}0 " "cannot contain '@objc' members", (bool)) ERROR(objc_in_resilient_extension,none, - "'@objc' %0 in extension of subclass of %1 requires %2 %3", - (DescriptiveDeclKind, Identifier, AvailabilityDomain, AvailabilityRange)) + "'@objc' %kindonly0 in extension of subclass of %1 requires %2 %3", + (const ValueDecl *, const ClassDecl *, AvailabilityDomain, + AvailabilityRange)) ERROR(objc_operator, none, - "operator methods cannot be declared @objc", ()) + "operator methods cannot be declared '@objc'", ()) ERROR(objc_operator_proto, none, - "@objc protocols must not have operator requirements", ()) + "'@objc' protocols must not have operator requirements", ()) ERROR(objc_for_generic_class,none, "generic subclasses of '@objc' classes cannot have an explicit '@objc' " @@ -6435,7 +6586,7 @@ ERROR(objc_setter_for_nonobjc_subscript,none, ERROR(objc_enum_generic,none, "'@objc' enum cannot be generic", ()) ERROR(objc_name_req_nullary,none, - "'@objc' %0 must have a simple name", (DescriptiveDeclKind)) + "'@objc' %kindonly0 must have a simple name", (const Decl *)) ERROR(objc_name_subscript,none, "'@objc' subscript cannot have a name; did you mean to put " "the name on the getter or setter?", ()) @@ -6459,15 +6610,15 @@ ERROR(objc_extension_not_class,none, "'@objc' can only be applied to an extension of a class", ()) ERROR(objc_name_not_valid_identifier,none, - "'@objc' %0 name is not a valid Objective-C identifier", - (DescriptiveDeclKind)) + "'@objc' %kindonly0 name is not a valid Objective-C identifier", + (const ValueDecl *)) ERROR(objc_cannot_infer_name_raw_identifier,none, - "cannot infer '@objc' %0 name because the Swift name is not a valid " - "Objective-C identifier; specify the name in '@objc' explicitly", - (DescriptiveDeclKind)) + "cannot infer '@objc' %kindonly0 name because the Swift name is not a " + "valid Objective-C identifier; specify the name in '@objc' explicitly", + (const ValueDecl *)) // If you change this, also change enum ObjCReason -#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @objcMembers|marked @IBOutlet|marked @IBAction|marked @IBSegueAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable|in an @objc extension of a class (without @nonobjc)|in an @objc @implementation extension of a class (without final or @nonobjc)|marked @objc by an access note}" +#define OBJC_ATTR_SELECT "select{marked '@c'|marked '@_cdecl'|marked dynamic|marked '@objc'|marked '@objcMembers'|marked '@IBOutlet'|marked '@IBAction'|marked '@IBSegueAction'|marked '@NSManaged'|a member of an '@objc' protocol|implicitly '@objc'|an '@objc' override|an implementation of an '@objc' requirement|marked '@IBInspectable'|marked '@GKInspectable'|in an '@objc' extension of a class (without '@nonobjc')|in an '@objc @implementation' extension of a class (without final or '@nonobjc')|marked '@objc' by an access note}" ERROR(objc_invalid_on_var,none, "property cannot be %" OBJC_ATTR_SELECT "0 " @@ -6476,13 +6627,16 @@ ERROR(objc_invalid_on_subscript,none, "subscript cannot be %" OBJC_ATTR_SELECT "0 because its type " "cannot be represented in Objective-C", (unsigned)) ERROR(objc_invalid_on_static_subscript,none, - "%0 cannot be %" OBJC_ATTR_SELECT "1", (DescriptiveDeclKind, unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1", + (const SubscriptDecl *, unsigned)) ERROR(objc_invalid_with_generic_params,none, - "%0 cannot be %" OBJC_ATTR_SELECT "1 because it has generic parameters", - (DescriptiveDeclKind, unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because it has generic " + "parameters", + (const ValueDecl *, unsigned)) ERROR(objc_invalid_with_generic_requirements,none, - "%0 cannot be %" OBJC_ATTR_SELECT "1 because it has a 'where' clause", - (DescriptiveDeclKind, unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because it has a 'where' " + "clause", + (const ValueDecl *, unsigned)) ERROR(objc_convention_invalid,none, "%0 is not representable in Objective-C, so it cannot be used" " with '@convention(%1)'", (Type, StringRef)) @@ -6500,25 +6654,36 @@ NOTE(not_objc_error_protocol_composition,none, "protocol-constrained type containing 'Error' cannot be represented " "in Objective-C", ()) NOTE(not_objc_empty_tuple,none, - "empty tuple type cannot be represented in Objective-C", ()) + "empty tuple type cannot be represented in %" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_non_trivial_cxx_class,none, - "non-trivial C++ classes cannot be represented in Objective-C", ()) + "non-trivial C++ classes cannot be represented in " + "%" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_tuple,none, - "tuples cannot be represented in Objective-C", ()) + "tuples cannot be represented in %" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_swift_class,none, - "classes not annotated with @objc cannot be represented " + "classes not annotated with '@objc' cannot be represented " "in Objective-C", ()) NOTE(not_objc_swift_struct,none, - "Swift structs cannot be represented in Objective-C", ()) + "Swift structs cannot be represented in %" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_swift_enum,none, "non-'@objc' enums cannot be represented in Objective-C", ()) +NOTE(not_cdecl_or_objc_swift_enum,none, + "Swift enums not marked '@c'%select{| or '@objc'}0 cannot be " + "represented in %" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_generic_type_param,none, - "generic type parameters cannot be represented in Objective-C", ()) + "generic type parameters cannot be represented in " + "%" FOREIGN_LANG_SELECT "0", (ForeignLanguage)) NOTE(not_objc_function_type_param,none, - "function types cannot be represented in Objective-C unless their " - "parameters and returns can be", ()) + "function types cannot be represented in %" FOREIGN_LANG_SELECT "0 " + "unless their parameters and returns can be", (ForeignLanguage)) NOTE(not_objc_function_type_throwing,none, - "throwing function types cannot be represented in Objective-C", ()) + "throwing function types cannot be represented in " + "%" FOREIGN_LANG_SELECT "0", (ForeignLanguage)) NOTE(objc_inferring_on_objc_protocol_member,none, "inferring '@objc' because the declaration is a member of " "an '@objc' protocol", ()) @@ -6528,32 +6693,44 @@ NOTE(objc_witness_objc_requirement,none, "satisfying requirement for %kind0 in protocol %1", (const ValueDecl *, const ProtocolDecl *)) +NOTE(cdecl_incompatible_with_protocols,none, + "protocols cannot be represented in C", ()) +NOTE(cdecl_incompatible_with_classes,none, + "classes cannot be represented in C", ()) + ERROR(no_opaque_return_type_of,none, "unable to resolve type for _opaqueReturnTypeOf attribute", ()) ERROR(objc_observing_accessor, none, - "observers (%0) are not allowed to be marked '@objc'", (DescriptiveDeclKind)) + "observers (%kindonly0) are not allowed to be marked '@objc'", + (const AccessorDecl *)) ERROR(objc_init_accessor, none, - "init accessors cannot be marked @objc", ()) + "init accessors cannot be marked '@objc'", ()) ERROR(objc_addressor, none, - "addressors are not allowed to be marked @objc", ()) + "addressors are not allowed to be marked '@objc'", ()) ERROR(objc_coroutine_accessor, none, - "'read' and 'modify' accessors are not allowed to be marked @objc", ()) + "'read' and 'modify' accessors are not allowed to be marked '@objc'", ()) +ERROR(objc_borrow_mutate_accessor, none, + "borrow and mutate accessors are not allowed to be marked '@objc'", ()) ERROR(objc_invalid_on_func_variadic,none, - "method cannot be %" OBJC_ATTR_SELECT "0 because it has a variadic " - "parameter", (unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because it has a variadic " + "parameter", (const AbstractFunctionDecl*, unsigned)) ERROR(objc_invalid_on_func_inout,none, - "method cannot be %" OBJC_ATTR_SELECT "0 because inout " - "parameters cannot be represented in Objective-C", (unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because inout " + "parameters cannot be represented in %" FOREIGN_LANG_SELECT "2", + (const AbstractFunctionDecl*, unsigned, ForeignLanguage)) ERROR(objc_invalid_on_func_param_type,none, - "method cannot be %" OBJC_ATTR_SELECT "1 because the type of the " - "parameter %0 cannot be represented in Objective-C", (unsigned, unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "2 because the type of the " + "parameter %1 cannot be represented in %" FOREIGN_LANG_SELECT "3", + (const AbstractFunctionDecl*, unsigned, unsigned, ForeignLanguage)) ERROR(objc_invalid_on_func_single_param_type,none, - "method cannot be %" OBJC_ATTR_SELECT "0 because the type of the " - "parameter cannot be represented in Objective-C", (unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because the type of the " + "parameter cannot be represented in %" FOREIGN_LANG_SELECT "2", + (const AbstractFunctionDecl*, unsigned, ForeignLanguage)) ERROR(objc_invalid_on_func_result_type,none, - "method cannot be %" OBJC_ATTR_SELECT "0 because its result type " - "cannot be represented in Objective-C", (unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because its result type " + "cannot be represented in %" FOREIGN_LANG_SELECT "2", + (const AbstractFunctionDecl*, unsigned, ForeignLanguage)) ERROR(objc_invalid_on_foreign_class,none, "method cannot be %" OBJC_ATTR_SELECT "0 because Core Foundation " "types are not classes in Objective-C", (unsigned)) @@ -6573,9 +6750,9 @@ ERROR(objc_invalid_on_failing_init,none, (unsigned)) ERROR(objc_in_objc_runtime_visible,none, - "%0 cannot be %" OBJC_ATTR_SELECT "1 because class %2 is only visible " - "via the Objective-C runtime", - (DescriptiveDeclKind, unsigned, Identifier)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because class %2 is only " + "visible via the Objective-C runtime", + (const ValueDecl *, unsigned, const ClassDecl *)) ERROR(objc_override_method_selector_mismatch,none, "Objective-C method has a different selector from the " @@ -6673,36 +6850,39 @@ WARNING(witness_non_objc_storage_optional,none, (bool, DeclName, Identifier)) ERROR(nonobjc_not_allowed,none, - "declaration is %" OBJC_ATTR_SELECT "0, and cannot be marked @nonobjc", + "declaration is %" OBJC_ATTR_SELECT "0, and cannot be marked '@nonobjc'", (unsigned)) #undef OBJC_DIAG_SELECT_2 #undef OBJC_DIAG_SELECT #undef OBJC_ATTR_SELECT +#undef FOREIGN_LANG_SELECT //------------------------------------------------------------------------------ // MARK: @exclusivity //------------------------------------------------------------------------------ ERROR(exclusivity_on_wrong_decl,none, - "@exclusivity can only be used on class properties, static properties and global variables", + "'@exclusivity' can only be used on class properties, static properties " + "and global variables", ()) ERROR(exclusivity_on_computed_property,none, - "@exclusivity can only be used on stored properties", + "'@exclusivity' can only be used on stored properties", ()) //------------------------------------------------------------------------------ // MARK: @_borrowed //------------------------------------------------------------------------------ ERROR(borrowed_with_objc_dynamic,none, - "%0 cannot be '@_borrowed' if it is '@objc dynamic'", - (DescriptiveDeclKind)) + "%kindonly0 cannot be '@_borrowed' if it is '@objc dynamic'", + (const AbstractStorageDecl *)) ERROR(borrowed_on_objc_protocol_requirement,none, - "%0 cannot be '@_borrowed' if it is an @objc protocol requirement", - (DescriptiveDeclKind)) + "%kindonly0 cannot be '@_borrowed' if it is an '@objc' protocol " + "requirement", + (const AbstractStorageDecl *)) ERROR(borrowed_with_effect,none, - "%0 cannot be '@_borrowed' if it is 'async' or 'throws'", - (DescriptiveDeclKind)) + "%kindonly0 cannot be '@_borrowed' if it is 'async' or 'throws'", + (const AccessorDecl *)) //------------------------------------------------------------------------------ // MARK: dynamic @@ -6741,9 +6921,9 @@ ERROR(dynamic_replacement_not_in_extension, none, ERROR(dynamic_replacement_must_not_be_dynamic, none, "dynamicReplacement(for:) of %0 must not be dynamic itself", (DeclName)) ERROR(dynamic_replacement_replaced_not_objc_dynamic, none, - "%0 is not marked @objc dynamic", (DeclName)) + "%0 is not marked '@objc dynamic'", (DeclName)) ERROR(dynamic_replacement_replacement_not_objc_dynamic, none, - "%0 is marked @objc dynamic", (DeclName)) + "%0 is marked '@objc dynamic'", (DeclName)) ERROR(dynamic_replacement_replaced_constructor_is_convenience, none, "replaced initializer %0 is marked as convenience", (DeclNameRef)) ERROR(dynamic_replacement_replaced_constructor_is_not_convenience, none, @@ -6794,14 +6974,24 @@ ERROR(availability_must_occur_alone, none, (AvailabilityDomain, bool)) ERROR(availability_unexpected_version, none, "unexpected version number for %0", (AvailabilityDomain)) -WARNING(availability_unrecognized_platform_name, - PointsToFirstBadToken, "unrecognized platform name %0", (Identifier)) -WARNING(availability_suggest_platform_name, - PointsToFirstBadToken, "unrecognized platform name %0;" - " did you mean '%1'?", +GROUPED_ERROR(availability_unrecognized_platform_name, + AvailabilityUnrecognizedName, PointsToFirstBadToken, + "unrecognized platform name %0", (Identifier)) +GROUPED_ERROR(availability_suggest_platform_name, + AvailabilityUnrecognizedName, PointsToFirstBadToken, + "unrecognized platform name %0; did you mean '%1'?", (Identifier, StringRef)) WARNING(availability_unsupported_version_number, none, "'%0' is not a supported version number", (llvm::VersionTuple)) +WARNING(availability_invalid_version_number_for_domain, none, + "'%0' is not a valid version number for %1", + (llvm::VersionTuple, AvailabilityDomain)) +WARNING(availability_domain_renamed, none, + "%0 has been renamed to '%1'", + (Identifier, StringRef)) +ERROR(availability_domain_requires_feature, none, + "%0 requires '-enable-experimental-feature %1'", + (AvailabilityDomain, StringRef)) WARNING(attr_availability_expected_deprecated_version, none, "expected version number with 'deprecated' in '%0' attribute for %1", @@ -6812,9 +7002,26 @@ WARNING(attr_availability_cannot_be_used_for_domain, none, WARNING(attr_availability_expected_version_spec, none, "expected 'introduced', 'deprecated', or 'obsoleted' in '%0' attribute " "for %1", (const DeclAttribute, AvailabilityDomain)) -ERROR(attr_availability_requires_custom_availability, none, - "%0 requires '-enable-experimental-feature CustomAvailability'", - (AvailabilityDomain)) +ERROR(attr_availability_domain_access, none, + "availability domain '%0' is " + "%select{private|fileprivate|internal|package|%error|%error}1 " + "and cannot be used in '%2' on " + "%select{private|fileprivate|internal|package|public|%error}3 %kind4", + (AvailabilityDomain, AccessLevel, DeclAttribute, AccessLevel, + const Decl *)) +ERROR(attr_availability_domain_not_usable_from_inline, none, + "availability domain '%0' used in '%1' on %kind2 must be " + "'@usableFromInline' or public", + (AvailabilityDomain, DeclAttribute, const Decl *)) + +GROUPED_WARNING(attr_availability_has_no_effect_domain_always_available, + AlwaysAvailableDomain, none, + "'%0' has no effect because '%1' is always available", + (DeclAttribute, AvailabilityDomain)) +GROUPED_WARNING(attr_availability_domain_always_available, + AlwaysAvailableDomain, none, + "'%0' is always available, use '*' instead", + (AvailabilityDomain)) ERROR(availability_decl_unavailable, none, "%0 is unavailable%select{ in %2|}1%select{|: %3}3", @@ -6858,12 +7065,12 @@ NOTE(note_deprecated_rename, none, "use '%0' instead", (StringRef)) ERROR(availability_decl_more_than_enclosing, none, - "%0 cannot be more available than enclosing scope", - (DescriptiveDeclKind)) + "%kindonly0 cannot be more available than enclosing scope", + (const Decl *)) NOTE(availability_implicit_decl_here, none, - "%0 implicitly declared here with availability of %1 %2 or newer", - (DescriptiveDeclKind, AvailabilityDomain, AvailabilityRange)) + "%kindonly0 implicitly declared here with availability of %1 %2 or newer", + (const Decl *, AvailabilityDomain, AvailabilityRange)) NOTE(availability_decl_more_than_enclosing_here, none, "enclosing scope requires availability of %0 %1 or newer", @@ -6893,8 +7100,8 @@ ERROR(availability_parameterized_protocol_only_version_newer, none, (AvailabilityDomain, AvailabilityRange)) ERROR(availability_isolated_any_only_version_newer, none, - "runtime support for @isolated(any) function types is only available in " - "%0 %1 or newer", + "runtime support for '@isolated(any)' function types is only available " + "in %0 %1 or newer", (AvailabilityDomain, AvailabilityRange)) ERROR(availability_copyable_generics_casting_only_version_newer, none, @@ -6920,10 +7127,7 @@ NOTE(availability_guard_with_version_check, none, "add 'if #available' version check", ()) NOTE(availability_add_attribute, none, - "add @available attribute to enclosing %0", (DescriptiveDeclKind)) -FIXIT(insert_available_attr, - "@available(%0 %1, *)\n%2", - (StringRef, StringRef, StringRef)) + "add '@available' attribute to enclosing %kindonly0", (const Decl *)) ERROR(availability_inout_accessor_only_in, none, "cannot pass as inout because %0 is only available in %1" @@ -6940,6 +7144,12 @@ WARNING(availability_query_useless_enclosing_scope, none, NOTE(availability_query_useless_enclosing_scope_here, none, "enclosing scope here", ()) +GROUPED_WARNING(availability_query_useless_always_true, + AlwaysAvailableDomain, none, + "unnecessary check for '%0'; " + "this condition will always be %select{false|true}1", + (AvailabilityDomain, unsigned)) + ERROR(availability_decl_no_potential, none, "%kindonly0 cannot be marked potentially unavailable with '@available'", (const Decl *)) @@ -6977,6 +7187,11 @@ ERROR(availability_protocol_requires_version, (const ProtocolDecl *, const ValueDecl *, AvailabilityDomain, AvailabilityRange)) +ERROR(availability_protocol_requirement_only_available_in, + none, "protocol %0 requirement %1 cannot be satisfied by %kindonly1 that " + "is only available in %2", + (const ProtocolDecl *, const ValueDecl *, AvailabilityDomain)) + NOTE(availability_protocol_requirement_here, none, "protocol requirement here", ()) @@ -7056,7 +7271,8 @@ ERROR(unavailability_query_wildcard_not_required, none, //------------------------------------------------------------------------------ WARNING(discardable_result_on_void_never_function, none, - "@discardableResult declared on a function returning %select{Never|Void}0 " + "'@discardableResult' declared on a function returning " + "'%select{Never|Void}0' " "is unnecessary", (bool)) //------------------------------------------------------------------------------ @@ -7120,6 +7336,7 @@ ERROR(inlinable_decl_ref_from_hidden_module, "%2 was imported for SPI only|" "%2 was not imported by this file|" "C++ APIs from imported module %2 do not support library evolution|" + "it was imported via the internal bridging header|" "%2 was not imported publicly}3", (const ValueDecl *, unsigned, Identifier, unsigned)) @@ -7131,6 +7348,7 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module, "%4 was imported for SPI only|" "%4 was not imported by this file|" "C++ types from imported module %4 do not support library evolution|" + "it was imported via the internal bridging header|" "%4 was not imported publicly}5", (const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned)) @@ -7176,23 +7394,25 @@ ERROR(resilient_associated_type_less_available_requires_default,none, //------------------------------------------------------------------------------ ERROR(specialize_attr_nongeneric_trailing_where,none, - "trailing 'where' clause in '_specialize' attribute of non-generic function %0", (DeclName)) + "trailing 'where' clause in %select{'_specialize'|'specialized'}1 attribute of non-generic function %0", (DeclName, bool)) ERROR(specialize_missing_where_clause,none, - "missing 'where' clause in '_specialize' attribute", ()) + "missing 'where' clause in %select{'_specialize'|'specialized'}0 attribute", (bool)) ERROR(specialize_empty_where_clause,none, - "empty 'where' clause in '_specialize' attribute", ()) + "empty 'where' clause in %select{'_specialize'|'specialized'}0 attribute", (bool)) ERROR(specialize_attr_non_concrete_same_type_req,none, - "only concrete type same-type requirements are supported by '_specialize' attribute", ()) + "only concrete type same-type requirements are supported by %select{'_specialize'|'specialized'}0 attribute", (bool)) ERROR(specialize_attr_only_generic_param_req,none, - "only requirements on generic parameters are supported by '_specialize' attribute", ()) + "only requirements on generic parameters are supported by %select{'_specialize'|'specialized'}0 attribute", (bool)) ERROR(specialize_attr_type_parameter_count_mismatch,none, "too few generic parameters are specified " - "in '_specialize' attribute (got %0, but expected %1)", - (unsigned, unsigned)) + "in %select{'_specialize'|'specialized'}2 attribute (got %0, but expected %1)", + (unsigned, unsigned, bool)) NOTE(specialize_attr_missing_constraint,none, - "missing constraint for %0 in '_specialize' attribute", (DeclName)) + "missing constraint for %0 in %select{'_specialize'|'specialized'}1 attribute", (DeclName, bool)) ERROR(specialize_attr_unsupported_kind_of_req,none, "only same-type and layout requirements are supported by '_specialize' attribute", ()) +ERROR(specialized_attr_unsupported_kind_of_req,none, + "only same-type are supported by 'specialized' 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, @@ -7239,6 +7459,14 @@ WARNING(variable_tuple_elt_never_mutated, none, "variable %0 was never mutated; " "consider changing the pattern to 'case (..., let %1, ...)'", (Identifier, StringRef)) +GROUPED_WARNING(weak_variable_never_mutated, WeakMutability, none, + "weak variable %0 was never mutated; " + "consider %select{removing 'var' to make it|changing to 'let'}1 constant", + (Identifier, bool)) +GROUPED_WARNING(weak_variable_tuple_elt_never_mutated, WeakMutability, none, + "weak variable %0 was never mutated; " + "consider changing the pattern to 'case (..., let %1, ...)'", + (Identifier, StringRef)) WARNING(variable_never_read, none, "variable %0 was written to, but never read", (Identifier)) @@ -7344,7 +7572,7 @@ GROUPED_ERROR(property_wrapper_ambiguous_value_property,PropertyWrappers,none, "named %1", (Type, Identifier)) GROUPED_ERROR(property_wrapper_wrong_initial_value_init,PropertyWrappers,none, "%0 parameter type (%1) must be the same as its " - "'wrappedValue' property type (%2) or an @autoclosure thereof", + "'wrappedValue' property type (%2) or an '@autoclosure' thereof", (DeclName, Type, Type)) GROUPED_ERROR(property_wrapper_failable_init,PropertyWrappers,none, "property wrapper initializer %0 cannot be failable", (DeclName)) @@ -7369,8 +7597,8 @@ ERROR(property_wrapper_attribute_not_on_property, none, "property wrapper attribute %0 can only be applied to a property", (Identifier)) ERROR(property_wrapper_param_not_supported,none, - "property wrapper attribute on parameter not yet supported on %0", - (DescriptiveDeclKind)) + "property wrapper attribute on parameter not yet supported on %kindonly0", + (const ValueDecl *)) NOTE(property_wrapper_declared_here,none, "property wrapper type %0 declared here", (Identifier)) ERROR(invalid_projection_argument,none, @@ -7403,12 +7631,13 @@ ERROR(property_wrapper_computed, none, "property wrapper cannot be applied to a computed property", ()) ERROR(property_wrapper_effectful,none, - "property wrapper's 'wrappedValue' property cannot define an 'async' or 'throws' %0", - (DescriptiveDeclKind)) + "property wrapper's 'wrappedValue' property cannot define an 'async' or " + "'throws' %kindonly0", + (const AccessorDecl *)) ERROR(property_with_wrapper_conflict_attribute,none, "property %0 with a wrapper cannot also be " - "%select{lazy|@NSCopying|@NSManaged|weak|unowned|unmanaged}1", + "%select{lazy|'@NSCopying'|'@NSManaged'|'@abi'|weak|unowned|unmanaged}1", (Identifier, int)) ERROR(property_wrapper_not_single_var, none, "property wrapper can only apply to a single variable", ()) @@ -7611,8 +7840,8 @@ ERROR(marker_protocol_conditional_conformance,none, //------------------------------------------------------------------------------ ERROR(differentiable_programming_attr_used_without_required_module, none, - "'@%0' attribute used without importing module %1", - (StringRef, Identifier)) + "%0 used without importing module %1", + (const TypeAttribute *, Identifier)) //------------------------------------------------------------------------------ // MARK: OSLog @@ -7647,24 +7876,28 @@ ERROR(atomics_ordering_must_be_constant, none, #define WHICH_ACCESS_NOTE(reason) "specified by access note for %" #reason +WARNING(access_notes_file_io_error,none, + "ignored access notes file at '%0' because it cannot be read: %1", + (StringRef, StringRef)) + REMARK(attr_added_by_access_note, none, - "implicitly added '%1' to this %2, as " WHICH_ACCESS_NOTE(0), - (StringRef, StringRef, DescriptiveDeclKind)) + "implicitly added '%1' to this %kindonly2, as " WHICH_ACCESS_NOTE(0), + (StringRef, StringRef, const ValueDecl *)) NOTE(fixit_attr_added_by_access_note, none, "add '%0' explicitly to silence this warning", (StringRef)) REMARK(attr_removed_by_access_note, none, - "implicitly removed '%1' from this %3, as " WHICH_ACCESS_NOTE(0), - (StringRef, StringRef, DescriptiveDeclKind)) + "implicitly removed '%1' from this %kindonly2, as " WHICH_ACCESS_NOTE(0), + (StringRef, StringRef, const ValueDecl *)) NOTE(fixit_attr_removed_by_access_note, none, "remove '%0' explicitly to silence this warning", (StringRef)) REMARK(attr_objc_name_changed_by_access_note, none, - "implicitly changed Objective-C name of this %1 to %2, as " + "implicitly changed Objective-C name of this %kindonly1 to %2, as " WHICH_ACCESS_NOTE(0), - (StringRef, DescriptiveDeclKind, ObjCSelector)) + (StringRef, const ValueDecl *, ObjCSelector)) NOTE(fixit_attr_objc_name_changed_by_access_note, none, "change Objective-C name explicitly to silence this warning", ()) @@ -7673,13 +7906,13 @@ NOTE(fixit_attr_objc_name_changed_by_access_note, none, // '-Raccess-note=all-validate' emits them as errors. ERROR(attr_objc_name_conflicts_with_access_note, none, - "ignored access note: did not change Objective-C name of this %1 from %2 " - "to %3, even though it was " WHICH_ACCESS_NOTE(0), - (StringRef, DescriptiveDeclKind, ObjCSelector, ObjCSelector)) + "ignored access note: did not change Objective-C name of this " + "%kindonly1 from %2 to %3, even though it was " WHICH_ACCESS_NOTE(0), + (StringRef, const ValueDecl *, ObjCSelector, ObjCSelector)) ERROR(wrap_invalid_attr_added_by_access_note, none, - "ignored access note: %0; did not implicitly add '%2' to this %3, even " - "though it was " WHICH_ACCESS_NOTE(1), - (DiagnosticInfo *, StringRef, StringRef, DescriptiveDeclKind)) + "ignored access note: %0; did not implicitly add '%2' to this " + "%kindonly3, even though it was " WHICH_ACCESS_NOTE(1), + (DiagnosticInfo *, StringRef, StringRef, const ValueDecl *)) #undef WHICH_ACCESS_NOTE @@ -7756,11 +7989,6 @@ ERROR(has_symbol_invalid_decl,none, ERROR(no_metadata_on_non_generic_param, none, "'@_noMetadata' must appear on a generic parameter", ()) -ERROR(experimental_no_metadata_feature_can_only_be_used_when_enabled, - none, "Can not use feature when experimental layout based" - " pre-specializations are disabled! Pass the frontend flag" - " -enable-experimental-feature LayoutPrespecialization to swift to enable " - " the usage of this language feature", ()) //------------------------------------------------------------------------------ @@ -7772,7 +8000,7 @@ ERROR(expected_macro_expansion_expr,PointsToFirstBadToken, ERROR(expected_macro_expansion_decls,PointsToFirstBadToken, "expected macro expansion to produce a declaration", ()) ERROR(macro_undefined,PointsToFirstBadToken, - "no macro named %0", (Identifier)) + "no macro named %0", (DeclName)) ERROR(external_macro_not_found,none, "external macro implementation type '%0.%1' could not be found for " "macro %2; %3", (StringRef, StringRef, DeclName, StringRef)) @@ -7798,8 +8026,9 @@ WARNING(macro_type_access_warn,none, ERROR(macro_in_nested,none, "macro %0 can only be declared at file scope", (DeclName)) ERROR(macro_without_role,none, - "macro %0 must declare its applicable roles via '@freestanding' or @attached'", - (DeclName)) + "macro %0 must declare its applicable roles via '@freestanding' or " + "'@attached'", + (DeclName)) ERROR(macro_result_type_cannot_be_used,none, "only a freestanding expression macro can produce a result of type %0", (Type)) @@ -7820,8 +8049,8 @@ ERROR(external_macro_arg_not_type_name,none, "argument to `#externalMacro` must be a string literal naming " "the external macro's %select{module|type}0", (unsigned)) ERROR(invalid_decl_in_macro_expansion,none, - "macro expansion cannot introduce %0", - (DescriptiveDeclKind)) + "macro expansion cannot introduce %kindonly0", + (const Decl *)) ERROR(let_accessor_expansion,none, "cannot expand accessor macro on variable declared with 'let'", ()) @@ -7847,8 +8076,8 @@ ERROR(macro_nonobserving_accessor_missing_from_expansion,none, "(such as 'get') as expected", (DeclName)) ERROR(macro_nonobserver_unexpected_in_expansion,none, - "expansion of macro %0 produced an unexpected %1", - (DeclName, DescriptiveDeclKind)) + "expansion of %kind0 produced an unexpected %kindonly1", + (const MacroDecl *, const AccessorDecl *)) ERROR(macro_init_accessor_not_documented,none, "expansion of macro %0 produced an unexpected 'init' accessor", (DeclName)) @@ -7863,8 +8092,8 @@ ERROR(extension_macro_invalid_conformance,none, "invalid protocol conformance %0 in extension macro", (Type)) ERROR(macro_attached_to_invalid_decl,none, - "'%0' macro cannot be attached to %1 (%base2)", - (StringRef, DescriptiveDeclKind, const Decl *)) + "'%0' macro cannot be attached to %kindonly1 (%base1)", + (StringRef, const Decl *)) ERROR(conformance_macro,none, "conformance macros are replaced by extension macros", ()) @@ -7957,7 +8186,7 @@ NOTE(note_non_bitwise_copyable_type_indirect_enum_element,none, ERROR(non_bitwise_copyable_type_suppressed,none, "cannot both conform to and suppress conformance to 'BitwiseCopyable'", ()) ERROR(non_bitwise_copyable_type_sensitive,none, - "a @sensitive type cannot conform to 'BitwiseCopyable'", ()) + "a '@sensitive' type cannot conform to 'BitwiseCopyable'", ()) ERROR(non_bitwise_copyable_type_cxx_nontrivial,none, "non-trivial C++ type cannot conform to 'BitwiseCopyable'", ()) ERROR(non_bitwise_copyable_c_type_nontrivial,none, @@ -8025,7 +8254,8 @@ NOTE(noncopyable_generics_implicit_composition, none, "'%noformat0 & %noformat1' is implicit here", (Type, Type)) ERROR(noncopyable_effectful_getter,none, - "%0 of noncopyable type cannot be 'async' or 'throws'", (DescriptiveDeclKind)) + "%kindonly0 of noncopyable type cannot be 'async' or 'throws'", + (const AccessorDecl *)) ERROR(noncopyable_enums_do_not_support_indirect,none, "noncopyable enum %0 cannot be marked indirect or have indirect cases yet", (Identifier)) ERROR(noncopyable_cast,none, @@ -8071,8 +8301,8 @@ ERROR(noimplicitcopy_attr_valid_only_on_local_let_params, ERROR(noimplicitcopy_attr_not_allowed_on_moveonlytype,none, "'@_noImplicitCopy' has no effect when applied to a noncopyable type", ()) ERROR(noncopyable_types_cannot_be_resilient, none, - "noncopyable %kind0 must be @frozen in library evolution mode; " - "non-@frozen public and @usableFromInline noncopyable types are not " + "noncopyable %kind0 must be '@frozen' in library evolution mode; " + "non-'@frozen' public and '@usableFromInline' noncopyable types are not " "supported", (const ValueDecl *)) ERROR(noncopyable_cannot_have_read_set_accessor,none, @@ -8084,12 +8314,12 @@ ERROR(noncopyable_cannot_have_read_set_accessor,none, //------------------------------------------------------------------------------ ERROR(init_accessor_can_refer_only_to_properties,none, - "init accessor cannot refer to %0 %1; init accessors can refer only" + "init accessor cannot refer to %kind0; init accessors can refer only" " to stored properties", - (DescriptiveDeclKind, DeclNameRef)) + (const ValueDecl *)) ERROR(storage_restrictions_attribute_not_on_init_accessor,none, - "@storageRestrictions attribute could only be used with init accessors", + "'@storageRestrictions' could only be used with init accessors", ()) ERROR(init_accessor_property_both_init_and_accessed,none, "property %0 cannot be both initialized and accessed", @@ -8132,6 +8362,9 @@ ERROR(pack_iteration_where_clause_not_supported, none, // MARK: Lifetime Dependence Syntax //------------------------------------------------------------------------------ +WARNING(use_lifetime_underscored, PointsToFirstBadToken, + "Unsupported use of @lifetime, use @_lifetime to specify lifetime dependencies", ()) + ERROR(lifetime_dependence_invalid_param_name, none, "invalid parameter name specified %0", (Identifier)) ERROR(lifetime_dependence_invalid_param_index, none, @@ -8166,42 +8399,48 @@ ERROR(lifetime_dependence_immortal_alone, none, "cannot specify any other dependence source along with immortal", ()) ERROR(lifetime_dependence_invalid_inherit_escapable_type, none, "cannot copy the lifetime of an Escapable type, use " - "'@lifetime(borrow %0)' instead", + "'@_lifetime(%0%1)' instead", + (StringRef, StringRef)) +ERROR(lifetime_dependence_parsed_borrow_with_ownership, none, + "invalid use of %0 dependence with %1 ownership", + (StringRef, StringRef)) +NOTE(lifetime_dependence_parsed_borrow_with_ownership_fix, none, + "use '@_lifetime(%0%1)' instead", + (StringRef, StringRef)) +ERROR(lifetime_dependence_cannot_use_default_escapable_consuming, none, + "invalid lifetime dependence on an Escapable value with %0 ownership", (StringRef)) -ERROR(lifetime_dependence_cannot_use_parsed_borrow_consuming, none, - "invalid use of borrow dependence with consuming ownership", - ()) ERROR(lifetime_dependence_cannot_use_parsed_borrow_inout, none, - "invalid use of borrow dependence on the same inout parameter", + "invalid use of inout dependence on the same inout parameter", ()) ERROR(lifetime_dependence_duplicate_target, none, "invalid duplicate target lifetime dependencies on function", ()) ERROR(lifetime_parameter_requires_inout, none, - "lifetime-dependent parameter must be 'inout'", (Identifier)) + "lifetime-dependent parameter '%0' must be 'inout'", (StringRef)) +ERROR(lifetime_target_requires_nonescapable, none, + "invalid lifetime dependence on an Escapable %0", (StringRef)) //------------------------------------------------------------------------------ // MARK: Lifetime Dependence Requirements //------------------------------------------------------------------------------ ERROR(lifetime_dependence_feature_required_return, none, - "%0 with a ~Escapable result requires " - "'-enable-experimental-feature LifetimeDependence'", (StringRef)) + "%0 cannot return a ~Escapable result", (StringRef)) ERROR(lifetime_dependence_feature_required_mutating, none, - "%0 with ~Escapable 'self' requires " - "'-enable-experimental-feature LifetimeDependence'", (StringRef)) + "%0 cannot have a ~Escapable 'self'", (StringRef)) ERROR(lifetime_dependence_feature_required_inout, none, - "%0 with a ~Escapable 'inout' parameter requires " - "'-enable-experimental-feature LifetimeDependence'", - // arg list is interchangable with lifetime_dependence_cannot_infer_inout - (StringRef, Identifier)) + "%0 cannot have a ~Escapable 'inout' parameter '%1' in addition to other ~Escapable parameters", + // this arg list must be compatible with + // lifetime_dependence_cannot_infer_inout + (StringRef, StringRef)) ERROR(lifetime_dependence_cannot_infer_return, none, - "%0 with a ~Escapable result requires '@lifetime(...)'", (StringRef)) + "%0 with a ~Escapable result requires '@_lifetime(...)'", (StringRef)) ERROR(lifetime_dependence_cannot_infer_mutating, none, - "%0 with a ~Escapable 'self' requires '@lifetime(self: ...)'", (StringRef)) + "%0 with a ~Escapable 'self' requires '@_lifetime(self: ...)'", (StringRef)) ERROR(lifetime_dependence_cannot_infer_inout, none, - "%0 with a ~Escapable 'inout' parameter requires '@lifetime(%1: ...)'", - (StringRef, Identifier)) + "%0 with a ~Escapable 'inout' parameter requires '@_lifetime(%1: ...)'", + (StringRef, StringRef)) //------------------------------------------------------------------------------ // MARK: Lifetime Dependence Inference - refinements to the requirements above @@ -8211,19 +8450,25 @@ ERROR(lifetime_dependence_cannot_infer_return_no_param, none, "%0 with a ~Escapable result needs a parameter to depend on", (StringRef)) NOTE(lifetime_dependence_cannot_infer_return_immortal, none, - "'@lifetime(immortal)' can be used to indicate that values produced by " + "'@_lifetime(immortal)' can be used to indicate that values produced by " "this initializer have no lifetime dependencies", ()) ERROR(lifetime_dependence_cannot_infer_bitwisecopyable, none, "cannot infer lifetime dependence on %0 because '%1' is BitwiseCopyable, " - "specify '@lifetime(borrow self)'", + "specify '@_lifetime(borrow self)'", (StringRef, StringRef)) ERROR(lifetime_dependence_cannot_infer_kind, none, "cannot infer the lifetime dependence scope on %0 with a ~Escapable " - "parameter, specify '@lifetime(borrow %1)' or '@lifetime(copy %1)'", + "parameter, specify '@_lifetime(borrow %1)' or '@_lifetime(copy %1)'", (StringRef, StringRef)) ERROR(lifetime_dependence_cannot_infer_scope_ownership, none, "cannot borrow the lifetime of '%0', which has consuming ownership on %1", (StringRef, StringRef)) +NOTE(lifetime_dependence_cannot_infer_inout_suggest, none, + "use '@_lifetime(%0: copy %0) to forward the inout dependency", + (StringRef)) +ERROR(lifetime_dependence_cannot_infer_implicit_init, none, + "cannot infer implicit initialization lifetime. Add an initializer with " + "'@_lifetime(...)' for each parameter the result depends on", ()) //------------------------------------------------------------------------------ // MARK: Lifetime Dependence Experimental Inference @@ -8266,6 +8511,15 @@ NOTE(note_reference_to_unsafe_decl,none, NOTE(note_reference_to_unsafe_typed_decl,none, "%select{reference|call}0 to %kind1 involves unsafe type %2", (bool, const ValueDecl *, Type)) +NOTE(note_unsafe_call_decl_argument_named,none, + "argument %1 in call to %kindbase0 has unsafe type %2", + (const ValueDecl *, Identifier, Type)) +NOTE(note_unsafe_call_decl_argument_indexed,none, + "argument #%1 in call to %kindbase0 has unsafe type %2", + (const ValueDecl *, unsigned, Type)) +NOTE(note_unsafe_call_argument_indexed,none, + "argument #%0 in call has unsafe type %1", + (unsigned, Type)) NOTE(note_reference_to_unsafe_through_typealias,none, "reference to %kind0 whose underlying type involves unsafe type %1", (const ValueDecl *, Type)) @@ -8278,9 +8532,10 @@ NOTE(note_reference_to_nonisolated_unsafe,none, NOTE(note_reference_unowned_unsafe,none, "reference to unowned(unsafe) %kind0 is unsafe", (const ValueDecl *)) NOTE(note_reference_exclusivity_unchecked,none, - "reference to @exclusivity(unchecked) %kind0 is unsafe", (const ValueDecl *)) + "reference to '@exclusivity(unchecked)' %kind0 is unsafe", + (const ValueDecl *)) NOTE(note_use_of_unsafe_conformance_is_unsafe,none, - "@unsafe conformance of %0 to %kind1 involves unsafe code", + "'@unsafe' conformance of %0 to %kind1 involves unsafe code", (Type, const ValueDecl *)) NOTE(note_unsafe_storage,none, "%kindbase1 involves unsafe type %2", (bool, const ValueDecl *, Type)) @@ -8298,7 +8553,7 @@ NOTE(decl_storage_mark_safe,none, "a safe interface", ()) ERROR(safe_and_unsafe_attr,none, - "%kindbase0 cannot be both @safe and @unsafe", (const Decl *)) + "%kindbase0 cannot be both '@safe' and '@unsafe'", (const Decl *)) GROUPED_WARNING(unsafe_superclass,StrictMemorySafety,none, "%kindbase0 has superclass involving unsafe type %1", @@ -8309,28 +8564,30 @@ GROUPED_WARNING(conformance_involves_unsafe,StrictMemorySafety,none, "indicate that the conformance is not memory-safe", (Type, const ValueDecl *)) NOTE(note_witness_unsafe,none, - "unsafe %0 %1 cannot satisfy safe requirement", - (DescriptiveDeclKind, DeclName)) + "unsafe %kind0 cannot satisfy safe requirement", + (const ValueDecl *)) NOTE(note_type_witness_unsafe,none, "unsafe type %0 cannot satisfy safe associated type %1", (Type, DeclName)) GROUPED_WARNING(override_safe_with_unsafe,StrictMemorySafety,none, - "override of safe %0 with unsafe %0", (DescriptiveDeclKind)) + "override of safe %kindonly0 with unsafe %kindonly0", + (const Decl *)) GROUPED_WARNING(preconcurrency_import_unsafe,StrictMemorySafety,none, - "@preconcurrency import is not memory-safe because it can silently " - "introduce data races", ()) + "'@preconcurrency' import is not memory-safe because it can silently " + "introduce data races; add '@unsafe' to indicate that this is unsafe", ()) GROUPED_WARNING(unsafe_without_unsafe,StrictMemorySafety,none, "expression uses unsafe constructs but is not marked with 'unsafe'", ()) GROUPED_WARNING(for_unsafe_without_unsafe,StrictMemorySafety,none, "for-in loop uses unsafe constructs but is not marked with 'unsafe'", ()) -GROUPED_WARNING(no_unsafe_in_unsafe,StrictMemorySafety,none, +WARNING(no_unsafe_in_unsafe,none, "no unsafe operations occur within 'unsafe' expression", ()) -GROUPED_WARNING(no_unsafe_in_unsafe_for,StrictMemorySafety,none, +WARNING(no_unsafe_in_unsafe_for,none, "no unsafe operations occur within 'unsafe' for-in loop", ()) NOTE(make_subclass_unsafe,none, - "make class %0 @unsafe to allow unsafe overrides of safe superclass methods", + "make class %0 '@unsafe' to allow unsafe overrides of safe superclass " + "methods", (DeclName)) //===----------------------------------------------------------------------===// @@ -8343,8 +8600,8 @@ ERROR(value_type_used_in_type_parameter,none, "cannot use value type %0 for generic argument %1", (Type, Type)) ERROR(invalid_value_type_value_generic,none, "%0 is not a supported value type for %1", (Type, Type)) -ERROR(invalid_value_generic_conformance,none, - "value generic type %0 cannot conform to protocol %1", (Type, Type)) +ERROR(invalid_value_generic_constraint,none, + "cannot use type constraint with generic value parameter %0", (Type)) ERROR(invalid_value_generic_same_type,none, "cannot constrain value parameter %0 to be type %1", (Type, Type)) ERROR(integer_type_not_accepted,none, @@ -8369,13 +8626,37 @@ ERROR(inlinearray_literal_incorrect_count,none, ERROR(inline_array_type_backwards,none, "element count must precede inline array element type", ()) +//===----------------------------------------------------------------------===// +// MARK: Embedded Swift +//===----------------------------------------------------------------------===// + +GROUPED_ERROR(weak_unowned_in_embedded_swift, EmbeddedRestrictions, none, + "attribute %0 cannot be used in Embedded Swift", + (ReferenceOwnership)) + +GROUPED_WARNING(untyped_throws_in_embedded_swift, EmbeddedRestrictions, + DefaultIgnore, + "untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'", ()) +GROUPED_WARNING(generic_nonfinal_in_embedded_swift, EmbeddedRestrictions, + DefaultIgnore, + "generic %kind0 in a class %select{must be 'final'|cannot be 'required'}1 in Embedded Swift", + (const Decl *, bool)) +GROUPED_WARNING(use_generic_member_of_existential_in_embedded_swift, + EmbeddedRestrictions, DefaultIgnore, + "cannot use generic %kind0 on a value of type %1 in Embedded Swift", + (const Decl *, Type)) +GROUPED_WARNING(dynamic_cast_involving_protocol_in_embedded_swift, + EmbeddedRestrictions, DefaultIgnore, + "cannot perform a dynamic cast to a type involving %kind0 in Embedded Swift", + (const Decl *)) + //===----------------------------------------------------------------------===// // MARK: @abi Attribute //===----------------------------------------------------------------------===// ERROR(attr_abi_mismatched_kind,none, - "cannot give %kind0 the ABI of a %1", - (Decl *, DescriptiveDeclKind)) + "cannot give %kind0 the ABI of a %kindonly1", + (const Decl *, const Decl *)) ERROR(attr_abi_mismatched_arity,none, "cannot give %kind0 the ABI of a %kindonly0 with a different number of " @@ -8390,19 +8671,15 @@ ERROR(attr_abi_mismatched_async,none, "cannot give %0 the ABI of %select{a non-async|an async}1 %kindonly0", (Decl *, /*abiIsAsync=*/bool)) -ERROR(attr_abi_mismatched_pbd_size,none, - "cannot give pattern binding the ABI of a binding with " - "%select{more|fewer}0 patterns", - (/*abiHasExtra=*/bool)) - -ERROR(attr_abi_mismatched_var,none, - "no match for %select{%kind0 in the ABI|ABI %kind0}1", - (Decl *, /*isABI=*/bool)) +ERROR(attr_abi_multiple_vars,none, + "'abi' attribute can only be applied to a single %0; declare each " + "%0 separately", + (DescriptiveDeclKind)) ERROR(attr_abi_incompatible_with_silgen_name,none, - "cannot use '@_silgen_name' and '@abi' on the same %0 because they serve " - "the same purpose", - (DescriptiveDeclKind)) + "cannot use '@_silgen_name' and '@abi' on the same %kindonly0 because " + "they serve the same purpose", + (const Decl *)) ERROR(attr_abi_missing_attr,none, "missing '%0' %select{attribute|modifier}1 in '@abi'", @@ -8413,9 +8690,6 @@ ERROR(attr_abi_extra_attr,none, ERROR(attr_abi_forbidden_attr,none, "unused '%0' %select{attribute|modifier}1 in '@abi'", (StringRef, bool)) -REMARK(abi_attr_inferred_attribute,none, - "inferred '%0' in '@abi' to match %select{attribute|modifier}1 on API", - (StringRef, bool)) ERROR(attr_abi_mismatched_attr,none, "'%0' %select{attribute|modifier}1 in '@abi' should match '%2'", @@ -8455,6 +8729,13 @@ ERROR(attr_abi_no_default_arguments,none, "affect the parameter's ABI", (Decl *)) +ERROR(attr_abi_no_macros,none, + "%kind0 cannot be expanded in '@abi' attribute", + (Decl *)) +ERROR(attr_abi_no_lazy,none, + "'lazy' is not compatible with '@abi' attribute", + ()) + // These macros insert 'final', 'non-final', or nothing depending on both the // current decl and its counterpart, such that 'non-final' is used if the // counterpart would be described as 'final' or 'static'. They must be kept in @@ -8483,10 +8764,6 @@ ERROR(attr_abi_failable_mismatch,none, //===----------------------------------------------------------------------===// // MARK: Isolated conformances //===----------------------------------------------------------------------===// -GROUPED_ERROR(isolated_conformance_experimental_feature,IsolatedConformances, - none, - "isolated conformances require experimental feature " - " 'IsolatedConformances'", ()) NOTE(note_isolate_conformance_to_global_actor,none, "isolate this conformance to the %select{global actor %0|main actor}1 " "with '@%2'", (Type, bool, StringRef)) @@ -8505,45 +8782,99 @@ GROUPED_ERROR(isolated_conformance_with_sendable_simple,IsolatedConformances, GROUPED_ERROR(isolated_conformance_wrong_domain,IsolatedConformances,none, "%0 conformance of %1 to %2 cannot be used in %3 context", (ActorIsolation, Type, DeclName, ActorIsolation)) +GROUPED_WARNING(isolated_conformance_will_become_nonisolated,IsolatedConformances,none, + "conformance of %0 to %1 should be marked 'nonisolated' to retain its behavior with upcoming feature 'InferIsolatedConformances'", + (const ValueDecl *, const ValueDecl *)) +GROUPED_ERROR(isolated_conformance_to_sendable_metatype,IsolatedConformances,none, + "cannot form %0 conformance of %1 to SendableMetatype-inheriting %kind2", + (ActorIsolation, Type, const ValueDecl *)) //===----------------------------------------------------------------------===// -// MARK: @execution Attribute +// MARK: @_inheritActorContext //===----------------------------------------------------------------------===// +ERROR(inherit_actor_context_only_on_func_types,none, + "%0 only applies to parameters with function types (got: %1)", + (DeclAttribute, Type)) -ERROR(attr_execution_only_on_async,none, - "cannot use '@execution' on non-async %kind0", - (ValueDecl *)) +ERROR(inherit_actor_context_only_on_sending_or_Sendable_params,none, + "%0 only applies to 'sending' parameters or parameters with " + "'@Sendable' function types", + (DeclAttribute)) -ERROR(attr_execution_only_on_async_closure,none, - "cannot use '@execution' on non-async closure", - ()) +ERROR(inherit_actor_context_only_on_async_or_isolation_erased_params,none, + "%0 only applies to '@isolated(any)' parameters or parameters with " + "asynchronous function types", + (DeclAttribute)) -ERROR(attr_execution_type_attr_only_on_async,none, - "cannot use '@execution' on non-async function type", - ()) +//===----------------------------------------------------------------------===// +// MARK: @concurrent and nonisolated(nonsending) attributes +//===----------------------------------------------------------------------===// -ERROR(attr_execution_incompatible_isolated_parameter,none, - "cannot use '@execution' on %kind0 because it has " - "an isolated parameter: %1", - (ValueDecl *, ValueDecl *)) +ERROR(execution_behavior_only_on_async,none, + "cannot use %0 on non-async %kind1", + (DeclAttribute, ValueDecl *)) -ERROR(attr_execution_incompatible_dynamically_isolated_parameter,none, - "cannot use '@execution' on %kind0 because it has " - "a dynamically isolated parameter: %1", - (ValueDecl *, ValueDecl *)) +ERROR(cannot_specify_execution_behavior_for_decl,none, + "%0 is only applicable to asynchronous functions, " + "initializers, subscripts and computed properties", + (DeclAttribute)) -ERROR(attr_execution_type_attr_incompatible_with_global_isolation,none, - "cannot use '@execution' because function type is " - "isolated to a global actor %0", - (Type)) +ERROR(execution_behavior_only_on_async_closure,none, + "cannot use %0 on non-async closure", + (DeclAttribute)) -ERROR(attr_execution_type_attr_incompatible_with_isolated_param,none, - "cannot use '@execution' together with an isolated parameter", - ()) +ERROR(execution_behavior_type_attr_only_on_async,none, + "cannot use '@%0' on non-async function type", + (StringRef)) -ERROR(attr_execution_type_attr_incompatible_with_isolated_any,none, - "cannot use '@execution' together with @isolated(any)", - ()) +ERROR(nonisolated_nonsending_only_on_function_types, none, + "%0 may only be used on function types", + (TypeRepr *)) + +ERROR(nonisolated_nonsending_only_on_async,none, + "cannot use %0 on non-async function type", + (TypeRepr *)) + +ERROR(cannot_use_nonisolated_nonsending_together_with_concurrent,none, + "cannot use %0 together with '@concurrent'", + (TypeRepr *)) + +ERROR(execution_behavior_incompatible_isolated_parameter,none, + "cannot use %0 on %kind1 because it has " + "an isolated parameter: %2", + (DeclAttribute, ValueDecl *, ValueDecl *)) + +ERROR(execution_behavior_attr_incompatible_with_global_isolation,none, + "cannot use %0 because function type is isolated to a global actor %1", + (DeclAttribute, Type)) + +ERROR(execution_behavior_attr_incompatible_with_isolated_param,none, + "cannot use %0 together with an isolated parameter", + (DeclAttribute)) + +ERROR(execution_behavior_type_attr_incompatible_with_global_isolation,none, + "cannot use '@%0' because function type is isolated to a global actor %1", + (StringRef, Type)) + +ERROR(nonisolated_nonsending_incompatible_with_global_isolation,none, + "cannot use %0 because function type is isolated to a global actor %1", + (TypeRepr *, Type)) + +ERROR(execution_behavior_type_attr_incompatible_with_isolated_param,none, + "cannot use '@%0' together with an isolated parameter", + (StringRef)) + +ERROR(nonisolated_nonsending_incompatible_with_isolated_param,none, + "cannot use %0 together with an isolated parameter", + (TypeRepr *)) + +ERROR(execution_behavior_type_attr_incompatible_with_isolated_any,none, + "cannot use '@%0' together with '@isolated(any)'", + (StringRef)) + +ERROR(nonisolated_nonsending_incompatible_with_isolated_any,none, + "cannot use %0 together with '@isolated(any)'", + (TypeRepr *)) ERROR(invalid_function_conversion_with_non_sendable,none, "cannot convert %0 to %1 because crossing of an isolation boundary " @@ -8553,41 +8884,102 @@ NOTE(type_does_not_conform_to_Sendable,none, "type %0 does not conform to 'Sendable' protocol", (Type)) GROUPED_WARNING( - attr_execution_nonisolated_behavior_will_change_decl, AsyncCallerExecution, + attr_execution_nonisolated_behavior_will_change_decl, NonisolatedNonsendingByDefault, none, "feature '%0' will cause nonisolated async %kindbase1 to run on the " - "caller's actor; use %2 to preserve behavior", - (StringRef, const AbstractFunctionDecl *, DeclAttribute)) + "caller's actor; use '@concurrent' to preserve behavior", + (StringRef, const AbstractFunctionDecl *)) GROUPED_WARNING( attr_execution_nonisolated_behavior_will_change_closure, - AsyncCallerExecution, none, + NonisolatedNonsendingByDefault, none, "feature '%0' will cause nonisolated async closure to run on the caller's " - "actor; use %1 to preserve behavior", - (StringRef, DeclAttribute)) + "actor; use '@concurrent' to preserve behavior", + (StringRef)) GROUPED_WARNING( attr_execution_nonisolated_behavior_will_change_typerepr, - AsyncCallerExecution, none, + NonisolatedNonsendingByDefault, none, "feature '%0' will cause nonisolated async function type to be treated as " - "specified to run on the caller's actor; use %1 to preserve " + "specified to run on the caller's actor; use '@concurrent' to preserve " "behavior", - (StringRef, DeclAttribute)) + (StringRef)) //===----------------------------------------------------------------------===// -// MARK: SwiftSettings +// MARK: @nonexhaustive and @preEnumExtensibility Attributes //===----------------------------------------------------------------------===// -ERROR(swift_settings_invalid_setting, none, - "Unrecognized setting passed to #SwiftSettings", ()) +ERROR(nonexhaustive_attr_on_frozen_type,none, + "cannot use '@nonexhaustive' together with '@frozen'", ()) -ERROR(swift_settings_must_be_top_level, none, - "#SwiftSettings can only be invoked as a top level declaration", ()) +ERROR(nonexhaustive_attr_on_internal_type,none, + "'@nonexhaustive' attribute can only be applied to public or package " + "declarations, but %0 is " + "%select{private|fileprivate|internal|%error|%error|%error}1", + (DeclName, AccessLevel)) + +//===----------------------------------------------------------------------===// +// MARK: `using` declaration +//===----------------------------------------------------------------------===// +ERROR(invalid_redecl_of_file_isolation,none, + "invalid redeclaration of file-level default actor isolation", ()) +NOTE(invalid_redecl_of_file_isolation_prev,none, + "default isolation was previously declared here", ()) + +//===----------------------------------------------------------------------===// +// MARK: `@inline(always)` attribute +//===----------------------------------------------------------------------===// +ERROR(attr_inline_always_experimental,none, + "'@inline(always)' is an experimental feature", ()) +ERROR(attr_inline_always_requires_inlinable,none, + "'@inline(always)' on public or package declarations must be used " + "together with '@inlinable'", ()) +ERROR(attr_inline_always_no_usable_from_inline,none, + "cannot use '@inline(always)' together with '@usableFromInline'", ()) +ERROR(attr_inline_always_requires_final_method,none, + "'@inline(always)' on class %select{methods|vars}1 requires %0 to be " + "marked 'final'", (DeclName, bool)) +ERROR(attr_inline_always_on_accessor,none, + "'@inline(always)' on class variable accessors whose variable declaration" + " is not final are not allowed", ()) + +//===----------------------------------------------------------------------===// +// MARK: Swift Performance hints +//===----------------------------------------------------------------------===// -ERROR(swift_settings_duplicate_setting, none, - "duplicate setting passed to #SwiftSettings", ()) -NOTE(swift_settings_duplicate_setting_original_loc, none, - "setting originally passed here", ()) +GROUPED_WARNING(perf_hint_function_returns_array,ReturnTypeImplicitCopy,DefaultIgnore, + "Performance: %0 returns a%select{ dictionary|n array}1, leading to implicit copies. " + "Consider using an 'inout' parameter instead.", (const FuncDecl *, bool)) +GROUPED_WARNING(perf_hint_closure_returns_array,ReturnTypeImplicitCopy,DefaultIgnore, + "Performance: closure returns a%select{ dictionary|n array}0, leading to implicit copies. " + "Consider using an 'inout' parameter instead.", (bool)) + +GROUPED_WARNING(perf_hint_param_expects_existential,ExistentialType,DefaultIgnore, + "Performance: %0 expects an existential, leading to heap allocation, reference counting, " + "and dynamic dispatch. Consider using generic constraints or concrete types instead.", + (const ParamDecl*)) +GROUPED_WARNING(perf_hint_func_returns_existential,ExistentialType,DefaultIgnore, + "Performance: %0 returns an existential, leading to heap allocation, reference counting, " + "and dynamic dispatch. Consider using generic constraints or concrete types instead.", + (const FuncDecl*)) +GROUPED_WARNING(perf_hint_closure_returns_existential,ExistentialType,DefaultIgnore, + "Performance: closure returns an existential, leading to heap allocation, reference counting, " + "and dynamic dispatch. Consider using generic constraints or concrete types instead.", ()) +GROUPED_WARNING(perf_hint_var_uses_existential,ExistentialType,DefaultIgnore, + "Performance: %0 uses an existential, leading to heap allocation, reference counting, " + "and dynamic dispatch. Consider using generic constraints or concrete types instead.", + (const VarDecl *)) +GROUPED_WARNING(perf_hint_any_pattern_uses_existential,ExistentialType,DefaultIgnore, + "Performance: declaration uses an existential, leading to heap allocation, reference counting, " + "and dynamic dispatch. Consider using generic constraints or concrete types instead.", + ()) +GROUPED_WARNING(perf_hint_typealias_uses_existential,ExistentialType,DefaultIgnore, + "Performance: %0 aliases an existential type, leading to heap allocation, reference counting, " + "and dynamic dispatch. Consider using generic constraints or concrete types instead.", + (const TypeAliasDecl *)) + +ERROR(unsafe_self_dependent_result_attr_on_invalid_decl,none, + "invalid use of @_unsafeSelfDependentResult", ()) #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsSema.h b/include/swift/AST/DiagnosticsSema.h index 90c9ac89107a5..44c6893da0df3 100644 --- a/include/swift/AST/DiagnosticsSema.h +++ b/include/swift/AST/DiagnosticsSema.h @@ -21,7 +21,6 @@ #include "swift/AST/DiagnosticsCommon.h" namespace swift { - class SwitchStmt; namespace diag { /// Describes the kind of requirement in a protocol. diff --git a/include/swift/AST/Evaluator.h b/include/swift/AST/Evaluator.h index f3f56f0f85ae2..5d7e47f29e65c 100644 --- a/include/swift/AST/Evaluator.h +++ b/include/swift/AST/Evaluator.h @@ -22,6 +22,7 @@ #include "swift/AST/EvaluatorDependencies.h" #include "swift/AST/RequestCache.h" #include "swift/Basic/AnyValue.h" +#include "swift/Basic/Assertions.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/Statistic.h" @@ -163,6 +164,9 @@ class Evaluator { /// is treated as a stack and is used to detect cycles. llvm::SetVector activeRequests; + /// A set of active requests that have been diagnosed for a cycle. + llvm::DenseSet diagnosedActiveCycles; + /// A cache that stores the results of requests. evaluator::RequestCache cache; @@ -208,6 +212,7 @@ class Evaluator { void enumerateReferencesInFile( const SourceFile *SF, evaluator::DependencyRecorder::ReferenceEnumerator f) const { + ASSERT(recorder.isRecordingEnabled() && "Dep recording should be enabled"); return recorder.enumerateReferencesInFile(SF, f); } @@ -276,9 +281,7 @@ class Evaluator { typename std::enable_if::type* = nullptr> void cacheNonEmptyOutput(const Request &request, typename Request::OutputType &&output) { - bool inserted = cache.insert(request, std::move(output)); - assert(inserted && "Request result was already cached"); - (void) inserted; + (void)cache.insert(request, std::move(output)); } /// Consults the request evaluator's cache for a split-cached request. @@ -344,7 +347,17 @@ class Evaluator { recorder.beginRequest(); - auto result = getRequestFunction()(request, *this); + auto result = [&]() -> typename Request::OutputType { + auto reqResult = getRequestFunction()(request, *this); + + // If we diagnosed a cycle for this request, we want to only use the + // default value to ensure we return a consistent result. + if (!diagnosedActiveCycles.empty() && + diagnosedActiveCycles.erase(activeReq)) { + return defaultValueFn(); + } + return reqResult; + }(); recorder.endRequest(request); @@ -416,6 +429,8 @@ class Evaluator { typename std::enable_if::type * = nullptr> void handleDependencySinkRequest(const Request &r, const typename Request::OutputType &o) { + if (!recorder.isRecordingEnabled()) + return; evaluator::DependencyCollector collector(recorder); r.writeDependencySink(collector, o); } @@ -427,6 +442,8 @@ class Evaluator { template ::type * = nullptr> void handleDependencySourceRequest(const Request &r) { + if (!recorder.isRecordingEnabled()) + return; auto source = r.readDependencySource(recorder); if (!source.isNull() && source.get()->isPrimary()) { recorder.handleDependencySourceRequest(r, source.get()); diff --git a/include/swift/AST/EvaluatorDependencies.h b/include/swift/AST/EvaluatorDependencies.h index 89fc27441c888..91fcf8cd9d1b1 100644 --- a/include/swift/AST/EvaluatorDependencies.h +++ b/include/swift/AST/EvaluatorDependencies.h @@ -94,6 +94,9 @@ class DependencyRecorder { public: DependencyRecorder(bool shouldRecord) : shouldRecord(shouldRecord) {} + /// Whether dependency recording is enabled. + bool isRecordingEnabled() const { return shouldRecord; } + /// Push a new empty set onto the activeRequestReferences stack. template void beginRequest(); diff --git a/include/swift/AST/ExistentialLayout.h b/include/swift/AST/ExistentialLayout.h index 706e6341379df..ad45c33a5e379 100644 --- a/include/swift/AST/ExistentialLayout.h +++ b/include/swift/AST/ExistentialLayout.h @@ -105,6 +105,9 @@ struct ExistentialLayout { /// calling this on a temporary is likely to be incorrect. ArrayRef getProtocols() const && = delete; + /// Determine whether this refers to any non-marker protocols. + bool containsNonMarkerProtocols() const; + ArrayRef getParameterizedProtocols() const & { return parameterized; } @@ -114,9 +117,24 @@ struct ExistentialLayout { LayoutConstraint getLayoutConstraint() const; + /// Whether this layout has any inverses within its signature. + bool hasInverses() const { + return !inverses.empty(); + } + + /// Whether this existential needs to have an extended existential shape. This + /// is relevant for the mangler to mangle as a symbolic link where possible + /// and for IRGen directly emitting some existentials. + /// + /// If 'allowInverses' is false, then regardless of if this existential layout + /// has inverse requirements those will not influence the need for having a + /// shape. + bool needsExtendedShape(bool allowInverses = true) const; + private: SmallVector protocols; SmallVector parameterized; + InvertibleProtocolSet inverses; }; } diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 7515022f3dc91..bb370c0b332c8 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -154,12 +154,12 @@ class alignas(8) Expr : public ASTAllocated { Implicit : 1 ); - SWIFT_INLINE_BITFIELD_FULL(CollectionExpr, Expr, 64-NumExprBits, + SWIFT_INLINE_BITFIELD_FULL(CollectionExpr, Expr, 64-NumberOfExprBits, /// True if the type of this collection expr was inferred by the collection /// fallback type, like [Any]. IsTypeDefaulted : 1, /// Number of comma source locations. - NumCommas : 32 - 1 - NumExprBits, + NumCommas : 32 - 1 - NumberOfExprBits, /// Number of entries in the collection. If this is a DictionaryExpr, /// each entry is a Tuple with the key and value pair. NumSubExprs : 32 @@ -256,8 +256,8 @@ class alignas(8) Expr : public ASTAllocated { LitKind : 3 ); - SWIFT_INLINE_BITFIELD(AbstractClosureExpr, Expr, (16-NumExprBits)+16, - : 16 - NumExprBits, // Align and leave room for subclasses + SWIFT_INLINE_BITFIELD(AbstractClosureExpr, Expr, (16-NumberOfExprBits)+16, + : 16 - NumberOfExprBits, // Align and leave room for subclasses Discriminator : 16 ); @@ -267,7 +267,7 @@ class alignas(8) Expr : public ASTAllocated { Kind : 2 ); - SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1+1, /// True if closure parameters were synthesized from anonymous closure /// variables. HasAnonymousClosureVars : 1, @@ -276,9 +276,11 @@ class alignas(8) Expr : public ASTAllocated { /// on each member reference. ImplicitSelfCapture : 1, - /// True if this @Sendable async closure parameter should implicitly - /// inherit the actor context from where it was formed. + /// True if this closure parameter should implicitly inherit the actor + /// context from where it was formed. InheritActorContext : 1, + /// The kind for inheritance - none or always at the moment. + InheritActorContextKind : 1, /// True if this closure's actor isolation behavior was determined by an /// \c \@preconcurrency declaration. @@ -295,7 +297,12 @@ class alignas(8) Expr : public ASTAllocated { /// isolation checks when it either specifies or inherits isolation /// and was passed as an argument to a function that is not fully /// concurrency checked. - RequiresDynamicIsolationChecking : 1 + RequiresDynamicIsolationChecking : 1, + + /// Whether this closure was type-checked as an argument to a macro. This + /// is only populated after type-checking, and only exists for diagnostic + /// logic. Do not add more uses of this. + IsMacroArgument : 1 ); SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16, @@ -603,7 +610,8 @@ class ErrorExpr : public Expr { SourceRange getSourceRange() const { return Range; } Expr *getOriginalExpr() const { return OriginalExpr; } - + void setOriginalExpr(Expr *newExpr) { OriginalExpr = newExpr; } + static bool classof(const Expr *E) { return E->getKind() == ExprKind::Error; } @@ -689,9 +697,7 @@ class BuiltinLiteralExpr : public LiteralExpr { /// Set the builtin initializer that will be used to construct the /// literal. - void setBuiltinInitializer(ConcreteDeclRef builtinInitializer) { - BuiltinInitializer = builtinInitializer; - } + void setBuiltinInitializer(ConcreteDeclRef builtinInitializer); }; /// The 'nil' literal. @@ -1449,23 +1455,25 @@ class TypeExpr : public Expr { }; class TypeValueExpr : public Expr { - GenericTypeParamDecl *paramDecl; - DeclNameLoc loc; + TypeRepr *repr; Type paramType; - /// Create a \c TypeValueExpr from a given generic value param decl. - TypeValueExpr(DeclNameLoc loc, GenericTypeParamDecl *paramDecl) : - Expr(ExprKind::TypeValue, /*implicit*/ false), paramDecl(paramDecl), - loc(loc), paramType(nullptr) {} + /// Create a \c TypeValueExpr from a given type representation. + TypeValueExpr(TypeRepr *repr) : + Expr(ExprKind::TypeValue, /*implicit*/ false), repr(repr), + paramType(nullptr) {} public: - /// Create a \c TypeValueExpr for a given \c GenericTypeParamDecl. + /// Create a \c TypeValueExpr for a given \c TypeDecl. /// /// The given location must be valid. - static TypeValueExpr *createForDecl(DeclNameLoc Loc, GenericTypeParamDecl *D); + static TypeValueExpr *createForDecl(DeclNameLoc loc, TypeDecl *d, + DeclContext *dc); + + GenericTypeParamDecl *getParamDecl() const; - GenericTypeParamDecl *getParamDecl() const { - return paramDecl; + TypeRepr *getRepr() const { + return repr; } /// Retrieves the corresponding parameter type of the value referenced by this @@ -1480,9 +1488,7 @@ class TypeValueExpr : public Expr { this->paramType = paramType; } - SourceRange getSourceRange() const { - return loc.getSourceRange(); - } + SourceRange getSourceRange() const; static bool classof(const Expr *E) { return E->getKind() == ExprKind::TypeValue; @@ -3287,8 +3293,7 @@ class DestructureTupleExpr final : public ImplicitConversionExpr, DstExpr(dstExpr) { Bits.DestructureTupleExpr.NumElements = destructuredElements.size(); std::uninitialized_copy(destructuredElements.begin(), - destructuredElements.end(), - getTrailingObjects()); + destructuredElements.end(), getTrailingObjects()); } public: @@ -3300,8 +3305,8 @@ class DestructureTupleExpr final : public ImplicitConversionExpr, Expr *srcExpr, Expr *dstExpr, Type ty); ArrayRef getDestructuredElements() const { - return {getTrailingObjects(), - static_cast(Bits.DestructureTupleExpr.NumElements)}; + return getTrailingObjects( + static_cast(Bits.DestructureTupleExpr.NumElements)); } Expr *getResultExpr() const { @@ -3342,24 +3347,9 @@ class ABISafeConversionExpr : public ImplicitConversionExpr { } }; -/// This is a conversion from an expression of UnresolvedType to an arbitrary -/// other type, and from an arbitrary type to UnresolvedType. This node does -/// not appear in valid code, only in code involving diagnostics. -class UnresolvedTypeConversionExpr : public ImplicitConversionExpr { -public: - UnresolvedTypeConversionExpr(Expr *subExpr, Type type) - : ImplicitConversionExpr(ExprKind::UnresolvedTypeConversion, subExpr, type) {} - - static bool classof(const Expr *E) { - return E->getKind() == ExprKind::UnresolvedTypeConversion; - } -}; - /// FunctionConversionExpr - Convert a function to another function type, /// which might involve renaming the parameters or handling substitutions /// of subtypes (in the return) or supertypes (in the input). -/// -/// FIXME: This should be a CapturingExpr. class FunctionConversionExpr : public ImplicitConversionExpr { public: FunctionConversionExpr(Expr *subExpr, Type type) @@ -3721,7 +3711,7 @@ class UnresolvedSpecializeExpr final : public Expr, SubExpr(SubExpr), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc) { Bits.UnresolvedSpecializeExpr.NumUnresolvedParams = UnresolvedParams.size(); std::uninitialized_copy(UnresolvedParams.begin(), UnresolvedParams.end(), - getTrailingObjects()); + getTrailingObjects()); } public: @@ -3735,8 +3725,8 @@ class UnresolvedSpecializeExpr final : public Expr, /// Retrieve the list of type parameters. These parameters have not yet /// been bound to archetypes of the entity to be specialized. ArrayRef getUnresolvedParams() const { - return {getTrailingObjects(), - static_cast(Bits.UnresolvedSpecializeExpr.NumUnresolvedParams)}; + return getTrailingObjects( + static_cast(Bits.UnresolvedSpecializeExpr.NumUnresolvedParams)); } SourceLoc getLoc() const { return LAngleLoc; } @@ -3993,7 +3983,7 @@ class SequenceExpr final : public Expr, Bits.SequenceExpr.NumElements = elements.size(); assert(Bits.SequenceExpr.NumElements > 0 && "zero-length sequence!"); std::uninitialized_copy(elements.begin(), elements.end(), - getTrailingObjects()); + getTrailingObjects()); } public: @@ -4009,11 +3999,13 @@ class SequenceExpr final : public Expr, unsigned getNumElements() const { return Bits.SequenceExpr.NumElements; } MutableArrayRef getElements() { - return {getTrailingObjects(), static_cast(Bits.SequenceExpr.NumElements)}; + return getTrailingObjects( + static_cast(Bits.SequenceExpr.NumElements)); } ArrayRef getElements() const { - return {getTrailingObjects(), static_cast(Bits.SequenceExpr.NumElements)}; + return getTrailingObjects( + static_cast(Bits.SequenceExpr.NumElements)); } Expr *getElement(unsigned i) const { @@ -4313,9 +4305,11 @@ class ClosureExpr : public AbstractClosureExpr { Bits.ClosureExpr.HasAnonymousClosureVars = false; Bits.ClosureExpr.ImplicitSelfCapture = false; Bits.ClosureExpr.InheritActorContext = false; + Bits.ClosureExpr.InheritActorContextKind = 0; Bits.ClosureExpr.IsPassedToSendingParameter = false; Bits.ClosureExpr.NoGlobalActorAttribute = false; Bits.ClosureExpr.RequiresDynamicIsolationChecking = false; + Bits.ClosureExpr.IsMacroArgument = false; } SourceRange getSourceRange() const; @@ -4360,8 +4354,29 @@ class ClosureExpr : public AbstractClosureExpr { return Bits.ClosureExpr.InheritActorContext; } - void setInheritsActorContext(bool value = true) { + /// Whether this closure should _always_ implicitly inherit the actor context + /// regardless of whether the isolation parameter is captured or not. + bool alwaysInheritsActorContext() const { + if (!inheritsActorContext()) + return false; + return getInheritActorIsolationModifier() == + InheritActorContextModifier::Always; + } + + void setInheritsActorContext(bool value = true, + InheritActorContextModifier modifier = + InheritActorContextModifier::None) { Bits.ClosureExpr.InheritActorContext = value; + Bits.ClosureExpr.InheritActorContextKind = uint8_t(modifier); + assert((static_cast( + Bits.ClosureExpr.InheritActorContextKind) == modifier) && + "not enough bits for modifier"); + } + + InheritActorContextModifier getInheritActorIsolationModifier() const { + assert(inheritsActorContext()); + return static_cast( + Bits.ClosureExpr.InheritActorContextKind); } /// Whether the closure's concurrency behavior was determined by an @@ -4394,6 +4409,17 @@ class ClosureExpr : public AbstractClosureExpr { Bits.ClosureExpr.RequiresDynamicIsolationChecking = value; } + /// Whether this closure was type-checked as an argument to a macro. This + /// is only populated after type-checking, and only exists for diagnostic + /// logic. Do not add more uses of this. + bool isMacroArgument() const { + return Bits.ClosureExpr.IsMacroArgument; + } + + void setIsMacroArgument(bool value = true) { + Bits.ClosureExpr.IsMacroArgument = value; + } + /// Determine whether this closure expression has an /// explicitly-specified result type. bool hasExplicitResultType() const { return ArrowLoc.isValid(); } @@ -4616,7 +4642,7 @@ class CaptureListExpr final : public Expr, assert(closureBody); Bits.CaptureListExpr.NumCaptures = captureList.size(); std::uninitialized_copy(captureList.begin(), captureList.end(), - getTrailingObjects()); + getTrailingObjects()); } public: @@ -4625,8 +4651,8 @@ class CaptureListExpr final : public Expr, AbstractClosureExpr *closureBody); ArrayRef getCaptureList() { - return {getTrailingObjects(), - static_cast(Bits.CaptureListExpr.NumCaptures)}; + return getTrailingObjects( + static_cast(Bits.CaptureListExpr.NumCaptures)); } AbstractClosureExpr *getClosureBody() { return closureBody; } const AbstractClosureExpr *getClosureBody() const { return closureBody; } @@ -6472,16 +6498,20 @@ class KeyPathDotExpr : public Expr { } }; +struct ForCollectionInit { + VarDecl *ForAccumulatorDecl; + PatternBindingDecl *ForAccumulatorBinding; +}; + /// An expression that may wrap a statement which produces a single value. class SingleValueStmtExpr : public Expr { public: - enum class Kind { - If, Switch, Do, DoCatch - }; + enum class Kind { If, Switch, Do, DoCatch, For }; private: Stmt *S; DeclContext *DC; + std::optional ForExpressionPreamble; SingleValueStmtExpr(Stmt *S, DeclContext *DC) : Expr(ExprKind::SingleValueStmt, /*isImplicit*/ true), S(S), DC(DC) {} @@ -6546,6 +6576,14 @@ class SingleValueStmtExpr : public Expr { SourceRange getSourceRange() const; + std::optional getForExpressionPreamble() const { + return this->ForExpressionPreamble; + } + + void setForExpressionPreamble(ForCollectionInit newPreamble) { + this->ForExpressionPreamble = newPreamble; + } + static bool classof(const Expr *E) { return E->getKind() == ExprKind::SingleValueStmt; } @@ -6566,7 +6604,7 @@ class TypeJoinExpr final : public Expr, } MutableArrayRef getMutableElements() { - return { getTrailingObjects(), getNumElements() }; + return getTrailingObjects(getNumElements()); } TypeJoinExpr(llvm::PointerUnion result, @@ -6609,7 +6647,7 @@ class TypeJoinExpr final : public Expr, } ArrayRef getElements() const { - return { getTrailingObjects(), getNumElements() }; + return getTrailingObjects(getNumElements()); } Expr *getElement(unsigned i) const { diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index 6a2e250d865fb..a193b8d699ec0 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -161,7 +161,6 @@ ABSTRACT_EXPR(ImplicitConversion, Expr) EXPR(Load, ImplicitConversionExpr) EXPR(ABISafeConversion, ImplicitConversionExpr) EXPR(DestructureTuple, ImplicitConversionExpr) - EXPR(UnresolvedTypeConversion, ImplicitConversionExpr) EXPR(FunctionConversion, ImplicitConversionExpr) EXPR(CovariantFunctionConversion, ImplicitConversionExpr) EXPR(CovariantReturnConversion, ImplicitConversionExpr) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 3662a99c61aef..28f30af1d870b 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -67,17 +67,17 @@ class FunctionTypeIsolation { /// Inherits isolation from the caller. This is only applicable /// to asynchronous function types. /// - /// NOTE: The difference in between NonIsolatedCaller and - /// NonIsolated is that NonIsolatedCaller is a strictly + /// NOTE: The difference in between NonIsolatedNonsending and + /// NonIsolated is that NonIsolatedNonsending is a strictly /// weaker form of nonisolation. While both in their bodies cannot - /// access isolated state directly, NonIsolatedCaller functions + /// access isolated state directly, NonIsolatedNonsending functions /// /are/ allowed to access state isolated to their caller via /// function arguments since we know that the callee will stay /// in the caller's isolation domain. In contrast, NonIsolated /// is strongly nonisolated and is not allowed to access /any/ /// isolated state (even via function parameters) since it is /// considered safe to run on /any/ actor. - NonIsolatedCaller, + NonIsolatedNonsending, }; static constexpr size_t NumBits = 3; // future-proof this slightly @@ -103,7 +103,7 @@ class FunctionTypeIsolation { return { Kind::Erased }; } static FunctionTypeIsolation forNonIsolatedCaller() { - return { Kind::NonIsolatedCaller }; + return { Kind::NonIsolatedNonsending }; } Kind getKind() const { return value.getInt(); } @@ -124,7 +124,27 @@ class FunctionTypeIsolation { return getKind() == Kind::Erased; } bool isNonIsolatedCaller() const { - return getKind() == Kind::NonIsolatedCaller; + return getKind() == Kind::NonIsolatedNonsending; + } + + /// Two function type isolations are equal if they have the same kind and + /// (when applicable) the same global actor types. + /// + /// Exact equality is the right thing to ask about when deciding whether + /// two isolations are the same statically, because we have to treat + /// different specializations of the same generic global actor type + /// as potentially different isolations. (Of course, you must be comparing + /// types that have been mapped into the same context.) + /// + /// Exact equality is *not* the right thing to ask about when deciding + /// whether two isolations might be the same dynamically, because two + /// different specializations of the same generic global actor type + /// could absolutely end up being the same in concrete specialization. + bool operator==(FunctionTypeIsolation other) const { + return value == other.value; + } + bool operator!=(FunctionTypeIsolation other) const { + return value != other.value; } // The opaque accessors below are just for the benefit of ExtInfoBuilder, @@ -517,7 +537,8 @@ class ASTExtInfoBuilder { DifferentiabilityMaskOffset = 11, DifferentiabilityMask = 0x7 << DifferentiabilityMaskOffset, SendingResultMask = 1 << 14, - NumMaskBits = 15 + InOutResultMask = 1 << 15, + NumMaskBits = 16 }; static_assert(FunctionTypeIsolation::Mask == 0x7, "update mask manually"); @@ -549,7 +570,7 @@ class ASTExtInfoBuilder { : ASTExtInfoBuilder(Representation::Swift, false, false, Type(), DifferentiabilityKind::NonDifferentiable, nullptr, FunctionTypeIsolation::forNonIsolated(), - std::nullopt /* LifetimeDependenceInfo */, + {} /* LifetimeDependenceInfo */, false /*sendingResult*/) {} // Constructor for polymorphic type. @@ -557,7 +578,7 @@ class ASTExtInfoBuilder { : ASTExtInfoBuilder(rep, false, throws, thrownError, DifferentiabilityKind::NonDifferentiable, nullptr, FunctionTypeIsolation::forNonIsolated(), - std::nullopt /* LifetimeDependenceInfo */, + {} /* LifetimeDependenceInfo */, false /*sendingResult*/) {} // Constructor with no defaults. @@ -640,6 +661,8 @@ class ASTExtInfoBuilder { globalActor); } + constexpr bool hasInOutResult() const { return bits & InOutResultMask; } + constexpr bool hasSelfParam() const { switch (getSILRepresentation()) { case SILFunctionTypeRepresentation::Thick: @@ -740,12 +763,19 @@ class ASTExtInfoBuilder { globalActor, thrownError, lifetimeDependencies); } + /// \p lifetimeDependencies should be arena allocated and not a temporary + /// Function types are allocated on the are arena and their ExtInfo should be + /// valid throughout their lifetime. [[nodiscard]] ASTExtInfoBuilder withLifetimeDependencies( llvm::ArrayRef lifetimeDependencies) const { return ASTExtInfoBuilder(bits, clangTypeInfo, globalActor, thrownError, lifetimeDependencies); } + [[nodiscard]] ASTExtInfoBuilder withLifetimeDependencies( + SmallVectorImpl lifetimeDependencies) const = + delete; + [[nodiscard]] ASTExtInfoBuilder withIsolation(FunctionTypeIsolation isolation) const { return ASTExtInfoBuilder( @@ -755,6 +785,11 @@ class ASTExtInfoBuilder { lifetimeDependencies); } + [[nodiscard]] ASTExtInfoBuilder withHasInOutResult() const { + return ASTExtInfoBuilder((bits | InOutResultMask), clangTypeInfo, + globalActor, thrownError, lifetimeDependencies); + } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(bits); ID.AddPointer(clangTypeInfo.getType()); @@ -850,6 +885,8 @@ class ASTExtInfo { FunctionTypeIsolation getIsolation() const { return builder.getIsolation(); } + constexpr bool hasInOutResult() const { return builder.hasInOutResult(); } + /// Helper method for changing the representation. /// /// Prefer using \c ASTExtInfoBuilder::withRepresentation for chaining. @@ -920,11 +957,18 @@ class ASTExtInfo { .build(); } + /// \p lifetimeDependencies should be arena allocated and not a temporary + /// Function types are allocated on the are arena and their ExtInfo should be + /// valid throughout their lifetime. [[nodiscard]] ASTExtInfo withLifetimeDependencies( ArrayRef lifetimeDependencies) const { return builder.withLifetimeDependencies(lifetimeDependencies).build(); } + [[nodiscard]] ASTExtInfo withLifetimeDependencies( + SmallVectorImpl lifetimeDependencies) const = + delete; + void Profile(llvm::FoldingSetNodeID &ID) const { builder.Profile(ID); } bool isEqualTo(ASTExtInfo other, bool useClangTypes) const { @@ -1033,7 +1077,7 @@ class SILExtInfoBuilder { makeBits(SILFunctionTypeRepresentation::Thick, false, false, false, false, false, SILFunctionTypeIsolation::forUnknown(), DifferentiabilityKind::NonDifferentiable), - ClangTypeInfo(nullptr), /*LifetimeDependenceInfo*/ std::nullopt) {} + ClangTypeInfo(nullptr), /*LifetimeDependenceInfo*/ {}) {} SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape, bool isSendable, bool isAsync, bool isUnimplementable, @@ -1227,11 +1271,19 @@ class SILExtInfoBuilder { return SILExtInfoBuilder(bits, ClangTypeInfo(type).getCanonical(), lifetimeDependencies); } + + /// \p lifetimeDependencies should be arena allocated and not a temporary + /// Function types are allocated on the are arena and their ExtInfo should be + /// valid throughout their lifetime. [[nodiscard]] SILExtInfoBuilder withLifetimeDependencies( ArrayRef lifetimeDependenceInfo) const { return SILExtInfoBuilder(bits, clangTypeInfo, lifetimeDependenceInfo); } + [[nodiscard]] ASTExtInfoBuilder withLifetimeDependencies( + SmallVectorImpl lifetimeDependencies) const = + delete; + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(bits); ID.AddPointer(clangTypeInfo.getType()); @@ -1368,11 +1420,17 @@ class SILExtInfo { return builder.withUnimplementable(isUnimplementable).build(); } + /// \p lifetimeDependencies should be arena allocated and not a temporary + /// Function types are allocated on the are arena and their ExtInfo should be + /// valid throughout their lifetime. SILExtInfo withLifetimeDependencies( ArrayRef lifetimeDependencies) const { return builder.withLifetimeDependencies(lifetimeDependencies); } + SILExtInfo withLifetimeDependencies(SmallVectorImpl + lifetimeDependencies) const = delete; + void Profile(llvm::FoldingSetNodeID &ID) const { builder.Profile(ID); } bool isEqualTo(SILExtInfo other, bool useClangTypes) const { diff --git a/include/swift/AST/FeatureAvailability.def b/include/swift/AST/FeatureAvailability.def index 1321362c5c1b2..71413c18a11d8 100644 --- a/include/swift/AST/FeatureAvailability.def +++ b/include/swift/AST/FeatureAvailability.def @@ -82,6 +82,7 @@ FEATURE(ValueGenericType, (6, 2)) FEATURE(InitRawStructMetadata2, (6, 2)) FEATURE(CustomGlobalExecutors, (6, 2)) FEATURE(TaskExecutor, (6, 2)) +FEATURE(TypedCoroAlloc, (6, 2)) FEATURE(Differentiation, FUTURE) FEATURE(ClearSensitive, FUTURE) diff --git a/include/swift/AST/FileUnit.h b/include/swift/AST/FileUnit.h index 512e6b04f00a8..59f93166ac3d4 100644 --- a/include/swift/AST/FileUnit.h +++ b/include/swift/AST/FileUnit.h @@ -126,6 +126,13 @@ class FileUnit : public DeclContext, public ASTAllocated { const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const {}; + /// Returns true if any import of \p importedModule has the `@preconcurrency` + /// attribute. + virtual bool + isModuleImportedPreconcurrency(const ModuleDecl *importedModule) const { + return false; + }; + /// Find all availability domains defined in this module with the given /// identifier. /// diff --git a/include/swift/AST/FunctionRefInfo.h b/include/swift/AST/FunctionRefInfo.h index a298f08e8c274..254179529d930 100644 --- a/include/swift/AST/FunctionRefInfo.h +++ b/include/swift/AST/FunctionRefInfo.h @@ -21,6 +21,8 @@ #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" +#include + namespace swift { class DeclNameLoc; diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index 6af9e538a2297..b5578631bb535 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -61,13 +61,13 @@ struct OpaqueEnvironmentData { }; /// Extra data in a generic environment for an opened existential. -struct OpenedExistentialEnvironmentData { +struct ExistentialEnvironmentData { Type existential; UUID uuid; }; /// Extra data in a generic environment for an opened pack element. -struct OpenedElementEnvironmentData { +struct ElementEnvironmentData { UUID uuid; CanGenericTypeParamType shapeClass; }; @@ -85,37 +85,38 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final GenericEnvironment, SubstitutionMap, OpaqueEnvironmentData, - OpenedExistentialEnvironmentData, - OpenedElementEnvironmentData, + ExistentialEnvironmentData, + ElementEnvironmentData, Type> { public: - enum class Kind { + enum class Kind: uint8_t { /// A normal generic environment, determined only by its generic /// signature. Primary, /// A generic environment describing an opaque type archetype. Opaque, /// A generic environment describing an opened existential archetype. - OpenedExistential, + Existential, /// A generic environment describing an opened element type of a /// pack archetype inside a pack expansion expression. - OpenedElement, + Element, }; class NestedTypeStorage; private: - mutable llvm::PointerIntPair SignatureAndKind{ - GenericSignature(), Kind::Primary}; + GenericSignature sig; NestedTypeStorage *nestedTypeStorage = nullptr; + Kind kind; + bool canonical; friend TrailingObjects; friend OpaqueTypeArchetypeType; size_t numTrailingObjects(OverloadToken) const; size_t numTrailingObjects(OverloadToken) const; - size_t numTrailingObjects(OverloadToken) const; - size_t numTrailingObjects(OverloadToken) const; + size_t numTrailingObjects(OverloadToken) const; + size_t numTrailingObjects(OverloadToken) const; size_t numTrailingObjects(OverloadToken) const; /// Retrieve the array containing the context types associated with the @@ -168,10 +169,13 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final public: GenericSignature getGenericSignature() const { - return SignatureAndKind.getPointer(); + return sig; } - Kind getKind() const { return SignatureAndKind.getInt(); } + Kind getKind() const { return kind; } + + /// Returns if the archetypes from this environment are canonical types. + bool isCanonical() const { return canonical; } ArrayRef getGenericParams() const; @@ -325,19 +329,6 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final /// abstraction level of their associated type requirements. SILType mapTypeIntoContext(SILModule &M, SILType type) const; - /// Map an interface type's protocol conformance into the corresponding - /// conformance for the contextual type. - static std::pair - mapConformanceRefIntoContext(GenericEnvironment *genericEnv, - Type conformingType, - ProtocolConformanceRef conformance); - - /// Map an interface type's protocol conformance into the corresponding - /// conformance for the contextual type. - std::pair - mapConformanceRefIntoContext(Type conformingType, - ProtocolConformanceRef conformance) const; - /// Returns a substitution map that sends every generic parameter to its /// corresponding archetype in this generic environment. SubstitutionMap getForwardingSubstitutionMap() const; diff --git a/include/swift/AST/GenericParamList.h b/include/swift/AST/GenericParamList.h index fc3ebe33941cf..3c29a0793a453 100644 --- a/include/swift/AST/GenericParamList.h +++ b/include/swift/AST/GenericParamList.h @@ -277,11 +277,11 @@ class GenericParamList final : SourceLoc RAngleLoc); MutableArrayRef getParams() { - return {getTrailingObjects(), NumParams}; + return getTrailingObjects(NumParams); } ArrayRef getParams() const { - return {getTrailingObjects(), NumParams}; + return getTrailingObjects(NumParams); } using iterator = GenericTypeParamDecl **; @@ -387,12 +387,12 @@ class alignas(RequirementRepr) TrailingWhereClause final : /// Retrieve the set of requirements. MutableArrayRef getRequirements() { - return {getTrailingObjects(), NumRequirements}; + return getTrailingObjects(NumRequirements); } /// Retrieve the set of requirements. ArrayRef getRequirements() const { - return {getTrailingObjects(), NumRequirements}; + return getTrailingObjects(NumRequirements); } /// Compute the source range containing this trailing where clause. diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index cca6904965974..352af26fe472f 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -120,6 +120,10 @@ class GenericSignature { ArrayRef requirements, bool isKnownCanonical = false); + /// Create a new placeholder generic signature from a set of generic + /// parameters. This is necessary for recovery in invalid cases. + static GenericSignature forInvalid(ArrayRef params); + /// Produce a new generic signature which drops all of the marker /// protocol conformance requirements associated with this one. GenericSignature withoutMarkerProtocols() const; @@ -376,6 +380,19 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final /// the given protocol. bool requiresProtocol(Type type, ProtocolDecl *proto) const; + /// Determine whether a conformance requirement of the given type to the + /// given protocol prohibits the use of an isolated conformance. + /// + /// The use of an isolated conformance to satisfy a requirement T: P is + /// prohibited when T is a type parameter and T, or some type that can be + /// used to reach T, also conforms to Sendable or SendableMetatype. In that + /// case, the conforming type and the protocol (Sendable or SendableMetatype) + /// is returned. + /// + /// If there is no such requirement, returns std::nullopt. + std::optional> + prohibitsIsolatedConformance(Type type) const; + /// Determine whether the given dependent type is equal to a concrete type. bool isConcreteType(Type type) const; @@ -489,8 +506,8 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final ArrayRef genericParams, ArrayRef requirements); - void print(raw_ostream &OS, PrintOptions Options = PrintOptions()) const; - void print(ASTPrinter &Printer, PrintOptions Opts = PrintOptions()) const; + void print(raw_ostream &OS, const PrintOptions &Options = PrintOptions()) const; + void print(ASTPrinter &Printer, const PrintOptions &Opts = PrintOptions()) const; SWIFT_DEBUG_DUMP; std::string getAsString() const; @@ -593,7 +610,11 @@ enum class GenericSignatureErrorFlags { /// The Knuth-Bendix completion procedure failed to construct a confluent /// rewrite system. - CompletionFailed = (1<<2) + CompletionFailed = (1<<2), + + /// A request evaluator cycle prevented us from computing this generic + /// signature. + CircularReference = (1<<3), }; using GenericSignatureErrors = OptionSet; @@ -604,6 +625,23 @@ using GenericSignatureErrors = OptionSet; using GenericSignatureWithError = llvm::PointerIntPair; +/// Build a generic signature from the given requirements, which are not +/// required to be minimal or canonical, and may contain unresolved +/// DependentMemberTypes. The generic signature is returned with the +/// error flags (if any) that were raised while building the signature. +/// +/// \param baseSignature if non-null, the new parameters and requirements +///// are added on; existing requirements of the base signature might become +///// redundant. Otherwise if null, build a new signature from scratch. +/// \param allowInverses if true, default requirements to Copyable/Escapable are +/// expanded for generic parameters. +GenericSignatureWithError buildGenericSignatureWithError( + ASTContext &ctx, + GenericSignature baseSignature, + SmallVector addedParameters, + SmallVector addedRequirements, + bool allowInverses); + } // end namespace swift namespace llvm { diff --git a/include/swift/AST/GenericTypeParamKind.h b/include/swift/AST/GenericTypeParamKind.h index dff51f1b48e2a..13b5caf3a9e8d 100644 --- a/include/swift/AST/GenericTypeParamKind.h +++ b/include/swift/AST/GenericTypeParamKind.h @@ -2,31 +2,38 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Copyright (c) 2024 - 2025 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 GenericTypeParamKind enum. -// +/// +/// This file defines the `GenericTypeParamKind` enum. +/// //===----------------------------------------------------------------------===// #ifndef SWIFT_AST_GENERICTYPEPARAMKIND_H #define SWIFT_AST_GENERICTYPEPARAMKIND_H +/// `GenericTypeParamKind.h` is imported into Swift. Be *very* careful with +/// what you include here and keep these includes minimal! +/// +/// See include caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" +#include + namespace swift { /// Describes the kind of a generic type parameter that occurs within generic /// parameter lists. -enum class GenericTypeParamKind: uint8_t { +enum class ENUM_EXTENSIBILITY_ATTR(closed) GenericTypeParamKind : uint8_t { /// A regular generic type parameter: 'T' - Type, + Type SWIFT_NAME("type"), /// A generic parameter pack: 'each T' - Pack, + Pack SWIFT_NAME("pack"), /// A generic value parameter: 'let T' - Value + Value SWIFT_NAME("value") }; } // namespace swift diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 21e868a03c52e..eddbc98292bb3 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -29,9 +29,9 @@ #include "llvm/IR/CallingConv.h" // FIXME: This include is just for llvm::SanitizerCoverageOptions. We should // split the header upstream so we don't include so much. -#include "llvm/Transforms/Instrumentation.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/VersionTuple.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/Instrumentation.h" #include #include #include @@ -498,8 +498,6 @@ class IRGenOptions { /// Internalize symbols (static library) - do not export any public symbols. unsigned InternalizeSymbols : 1; - unsigned MergeableSymbols : 1; - /// Emit a section with references to class_ro_t* in generic class patterns. unsigned EmitGenericRODatas : 1; @@ -566,6 +564,9 @@ class IRGenOptions { /// Pointer authentication. PointerAuthOptions PointerAuth; + // If not 0, this overrides the value defined by the target. + uint64_t CustomLeastValidPointerValue = 0; + /// The different modes for dumping IRGen type info. enum class TypeInfoDumpFilter { All, @@ -604,6 +605,9 @@ class IRGenOptions { /// Paths to the pass plugins registered via -load-pass-plugin. std::vector LLVMPassPlugins; + /// Set to true if we support AArch64TBI. + bool HasAArch64TBI = false; + IRGenOptions() : OutputKind(IRGenOutputKind::LLVMAssemblyAfterOptimization), Verify(true), VerifyEach(false), OptMode(OptimizationMode::NotSet), @@ -640,22 +644,20 @@ class IRGenOptions { DisableStandardSubstitutionsInReflectionMangling(false), EnableGlobalISel(false), VirtualFunctionElimination(false), WitnessMethodElimination(false), ConditionalRuntimeRecords(false), - AnnotateCondFailMessage(false), - InternalizeAtLink(false), InternalizeSymbols(false), - MergeableSymbols(false), EmitGenericRODatas(true), + AnnotateCondFailMessage(false), InternalizeAtLink(false), + InternalizeSymbols(false), EmitGenericRODatas(true), NoPreallocatedInstantiationCaches(false), DisableReadonlyStaticObjects(false), CollocatedMetadataFunctions(false), ColocateTypeDescriptors(true), UseRelativeProtocolWitnessTables(false), UseFragileResilientProtocolWitnesses(false), EnableHotColdSplit(false), - EmitAsyncFramePushPopMetadata(true), EmitTypeMallocForCoroFrame(false), + EmitAsyncFramePushPopMetadata(true), EmitTypeMallocForCoroFrame(true), AsyncFramePointerAll(false), UseProfilingMarkerThunks(false), - UseCoroCCX8664(false), UseCoroCCArm64(false), - MergeableTraps(false), + UseCoroCCX8664(false), UseCoroCCArm64(false), MergeableTraps(false), DebugInfoForProfiling(false), CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()), TypeInfoFilter(TypeInfoDumpFilter::All), PlatformCCallingConvention(llvm::CallingConv::C), UseCASBackend(false), - CASObjMode(llvm::CASBackendMode::Native) { + CASObjMode(llvm::CASBackendMode::Native), HasAArch64TBI(false) { DisableRoundTripDebugTypes = !CONDITIONAL_ASSERT_enabled(); } diff --git a/include/swift/AST/IRGenRequests.h b/include/swift/AST/IRGenRequests.h index a9819b2726105..7285a7370f95f 100644 --- a/include/swift/AST/IRGenRequests.h +++ b/include/swift/AST/IRGenRequests.h @@ -68,6 +68,7 @@ class GeneratedModule final { std::unique_ptr Context; std::unique_ptr Module; std::unique_ptr Target; + std::unique_ptr RemarkStream; GeneratedModule() : Context(nullptr), Module(nullptr), Target(nullptr) {} @@ -81,13 +82,14 @@ class GeneratedModule final { /// needed, use \c GeneratedModule::null() instead. explicit GeneratedModule(std::unique_ptr &&Context, std::unique_ptr &&Module, - std::unique_ptr &&Target) - : Context(std::move(Context)), Module(std::move(Module)), - Target(std::move(Target)) { - assert(getModule() && "Use GeneratedModule::null() instead"); - assert(getContext() && "Use GeneratedModule::null() instead"); - assert(getTargetMachine() && "Use GeneratedModule::null() instead"); - } + std::unique_ptr &&Target, + std::unique_ptr &&RemarkStream) + : Context(std::move(Context)), Module(std::move(Module)), + Target(std::move(Target)), RemarkStream(std::move(RemarkStream)) { + assert(getModule() && "Use GeneratedModule::null() instead"); + assert(getContext() && "Use GeneratedModule::null() instead"); + assert(getTargetMachine() && "Use GeneratedModule::null() instead"); + } GeneratedModule(GeneratedModule &&) = default; GeneratedModule& operator=(GeneratedModule &&) = default; diff --git a/include/swift/AST/Identifier.h b/include/swift/AST/Identifier.h index 036685873da73..68ee30595bc77 100644 --- a/include/swift/AST/Identifier.h +++ b/include/swift/AST/Identifier.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 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -17,6 +17,19 @@ #ifndef SWIFT_AST_IDENTIFIER_H #define SWIFT_AST_IDENTIFIER_H +/// `Identifier.h` is imported into Swift. Be *very* careful with what you +/// include here and keep these includes minimal! +/// If you don't need to import a header into Swift, include it in the `#ifdef` +/// block below instead. +/// +/// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" +#include +#include + +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + #include "swift/Basic/EditorPlaceholder.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" @@ -25,6 +38,10 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/Support/TrailingObjects.h" +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + +SWIFT_BEGIN_NULLABILITY_ANNOTATIONS + namespace llvm { class raw_ostream; } @@ -56,8 +73,8 @@ class Identifier { friend class ASTContext; friend class DeclBaseName; - const char *Pointer; - + const char *_Nullable Pointer; + public: enum : size_t { NumLowBitsAvailable = 3, @@ -67,7 +84,7 @@ class Identifier { private: /// Constructor, only accessible by ASTContext, which handles the uniquing. - explicit Identifier(const char *Ptr) : Pointer(Ptr) { + explicit Identifier(const char *_Nullable Ptr) : Pointer(Ptr) { assert(((uintptr_t)Ptr & SpareBitMask) == 0 && "Identifier pointer does not use any spare bits"); } @@ -80,25 +97,35 @@ class Identifier { public: explicit Identifier() : Pointer(nullptr) {} - - const char *get() const { return Pointer; } - - StringRef str() const { return Pointer; } - explicit operator std::string() const { return std::string(Pointer); } + SWIFT_UNAVAILABLE("Use 'init(raw:)' instead") + static Identifier getFromOpaquePointer(const void *_Nullable P) { + return Identifier((const char *)P); + } +#ifdef COMPILED_WITH_SWIFT + SWIFT_NAME("init(raw:)") + Identifier(const void *_Nullable raw) : Pointer((const char *)raw) {} +#endif - unsigned getLength() const { - assert(Pointer != nullptr && "Tried getting length of empty identifier"); - return ::strlen(Pointer); + SWIFT_UNAVAILABLE("Use 'raw' instead") + const void *_Nullable getAsOpaquePointer() const { + return static_cast(Pointer); } - +#ifdef COMPILED_WITH_SWIFT + SWIFT_COMPUTED_PROPERTY + SWIFT_IMPORT_UNSAFE + const void *_Nullable getRaw() const { return getAsOpaquePointer(); } +#endif + + SWIFT_UNAVAILABLE("Use 'isValid' instead") bool empty() const { return Pointer == nullptr; } + SWIFT_UNAVAILABLE("Use 'isValid' instead") bool nonempty() const { return !empty(); } +#ifdef COMPILED_WITH_SWIFT + SWIFT_COMPUTED_PROPERTY + bool getIsValid() const { return nonempty(); } +#endif - LLVM_ATTRIBUTE_USED bool is(StringRef string) const { - return str() == string; - } - /// isOperator - Return true if this identifier is an operator, false if it is /// a normal identifier. bool isOperator() const { @@ -111,6 +138,23 @@ class Identifier { return isOperatorSlow(); } + SWIFT_UNAVAILABLE("Unavailable in Swift") + bool isEditorPlaceholder() const; + +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + + const char *_Nullable get() const { return Pointer; } + + unsigned getLength() const { + assert(Pointer != nullptr && "Tried getting length of empty identifier"); + return ::strlen(Pointer); + } + + static bool isEditorPlaceholder(StringRef name) { + return swift::isEditorPlaceholder(name); + } + /// Returns true if this identifier contains non-identifier characters and /// must always be escaped with backticks, even in contexts were other /// escaped identifiers could omit backticks (like keywords as argument @@ -140,6 +184,10 @@ class Identifier { return is("??"); } + // Returns whether this is a standard infix logical operator, + // such as '&&', '||'. + bool isStandardInfixLogicalOperator() const { return is("&&") || is("||"); } + /// isOperatorStartCodePoint - Return true if the specified code point is a /// valid start of an operator. static bool isOperatorStartCodePoint(uint32_t C) { @@ -176,13 +224,7 @@ class Identifier { || (C >= 0xE0100 && C <= 0xE01EF); } - static bool isEditorPlaceholder(StringRef name) { - return swift::isEditorPlaceholder(name); - } - - bool isEditorPlaceholder() const { - return !empty() && isEditorPlaceholder(str()); - } + StringRef str() const { return Pointer; } bool hasDollarPrefix() const { return str().starts_with("$") && !(getLength() == 1); @@ -191,13 +233,9 @@ class Identifier { bool hasUnderscoredNaming() const { return str().starts_with("_"); } - - const void *getAsOpaquePointer() const { - return static_cast(Pointer); - } - - static Identifier getFromOpaquePointer(const void *P) { - return Identifier((const char*)P); + + LLVM_ATTRIBUTE_USED bool is(StringRef string) const { + return str() == string; } /// Compare two identifiers, producing -1 if \c *this comes before \c other, @@ -206,6 +244,8 @@ class Identifier { /// Null identifiers come after all other identifiers. int compare(Identifier other) const; + explicit operator std::string() const { return std::string(Pointer); } + friend llvm::hash_code hash_value(Identifier ident) { return llvm::hash_value(ident.getAsOpaquePointer()); } @@ -227,16 +267,21 @@ class Identifier { return Identifier((const char*)Val); } +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + private: bool isOperatorSlow() const; }; - + class DeclName; class DeclNameRef; class ObjCSelector; } // end namespace swift +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + namespace llvm { raw_ostream &operator<<(raw_ostream &OS, swift::Identifier I); raw_ostream &operator<<(raw_ostream &OS, swift::DeclName I); @@ -264,10 +309,10 @@ namespace llvm { template<> struct PointerLikeTypeTraits { public: - static inline void *getAsVoidPointer(swift::Identifier I) { + static inline void *_Nullable getAsVoidPointer(swift::Identifier I) { return const_cast(I.getAsOpaquePointer()); } - static inline swift::Identifier getFromVoidPointer(void *P) { + static inline swift::Identifier getFromVoidPointer(void *_Nullable P) { return swift::Identifier::getFromOpaquePointer(P); } enum { NumLowBitsAvailable = swift::Identifier::NumLowBitsAvailable }; @@ -275,15 +320,13 @@ namespace llvm { } // end namespace llvm -class BridgedDeclBaseName; +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE namespace swift { /// Wrapper that may either be an Identifier or a special name /// (e.g. for subscripts) class DeclBaseName { - friend class ::BridgedDeclBaseName; - public: enum class Kind: uint8_t { Normal, @@ -322,6 +365,9 @@ class DeclBaseName { return DeclBaseName(Identifier((const char *)&DestructorIdentifierData)); } +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + Kind getKind() const { if (Ident.get() == (const char *)&SubscriptIdentifierData) { return Kind::Subscript; @@ -362,7 +408,7 @@ class DeclBaseName { } bool hasDollarPrefix() const { - return getIdentifier().hasDollarPrefix(); + return !isSpecial() && getIdentifier().hasDollarPrefix(); } /// A representation of the name to be displayed to users. May be ambiguous @@ -404,15 +450,20 @@ class DeclBaseName { return Ident.get() < RHS.Ident.get(); } - const void *getAsOpaquePointer() const { return Ident.get(); } + const void *_Nullable getAsOpaquePointer() const { return Ident.get(); } - static DeclBaseName getFromOpaquePointer(void *P) { + static DeclBaseName getFromOpaquePointer(void *_Nullable P) { return Identifier::getFromOpaquePointer(P); } + +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE }; } // end namespace swift +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + namespace llvm { raw_ostream &operator<<(raw_ostream &OS, swift::DeclBaseName D); @@ -437,10 +488,10 @@ template<> struct DenseMapInfo { template struct PointerLikeTypeTraits; template <> struct PointerLikeTypeTraits { public: - static inline void *getAsVoidPointer(swift::DeclBaseName D) { + static inline void *_Nullable getAsVoidPointer(swift::DeclBaseName D) { return const_cast(D.getAsOpaquePointer()); } - static inline swift::DeclBaseName getFromVoidPointer(void *P) { + static inline swift::DeclBaseName getFromVoidPointer(void *_Nullable P) { return swift::DeclBaseName::getFromOpaquePointer(P); } enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable }; @@ -467,10 +518,10 @@ class DeclName { : BaseName(BaseName), NumArgs(NumArgs) { } ArrayRef getArgumentNames() const { - return {getTrailingObjects(), NumArgs}; + return getTrailingObjects(NumArgs); } MutableArrayRef getArgumentNames() { - return {getTrailingObjects(), NumArgs}; + return getTrailingObjects(NumArgs); } /// Uniquing for the ASTContext. @@ -486,9 +537,9 @@ class DeclName { /// compound declaration name. llvm::PointerUnion BaseNameOrCompound; - explicit DeclName(void *Opaque) - : BaseNameOrCompound(decltype(BaseNameOrCompound)::getFromOpaqueValue(Opaque)) - {} + explicit DeclName(void *_Nullable Opaque) + : BaseNameOrCompound( + decltype(BaseNameOrCompound)::getFromOpaqueValue(Opaque)) {} void initialize(ASTContext &C, DeclBaseName baseName, ArrayRef argumentNames); @@ -512,7 +563,8 @@ class DeclName { /// Build a compound value name given a base name and a set of argument names /// extracted from a parameter list. - DeclName(ASTContext &C, DeclBaseName baseName, ParameterList *paramList); + DeclName(ASTContext &C, DeclBaseName baseName, + ParameterList *_Nonnull paramList); /// Retrieve the 'base' name, i.e., the name that follows the introducer, /// such as the 'foo' in 'func foo(x:Int, y:Int)' or the 'bar' in @@ -521,7 +573,7 @@ class DeclName { if (auto compound = BaseNameOrCompound.dyn_cast()) return compound->BaseName; - return BaseNameOrCompound.get(); + return cast(BaseNameOrCompound); } /// Assert that the base name is not special and return its identifier. @@ -545,13 +597,11 @@ class DeclName { explicit operator bool() const { if (BaseNameOrCompound.dyn_cast()) return true; - return !BaseNameOrCompound.get().empty(); + return !cast(BaseNameOrCompound).empty(); } /// True if this is a simple one-component name. - bool isSimpleName() const { - return BaseNameOrCompound.is(); - } + bool isSimpleName() const { return isa(BaseNameOrCompound); } /// True if this is a compound name. bool isCompoundName() const { @@ -649,8 +699,10 @@ class DeclName { return lhs.compare(rhs) >= 0; } - void *getOpaqueValue() const { return BaseNameOrCompound.getOpaqueValue(); } - static DeclName getFromOpaqueValue(void *p) { return DeclName(p); } + void *_Nullable getOpaqueValue() const { + return BaseNameOrCompound.getOpaqueValue(); + } + static DeclName getFromOpaqueValue(void *_Nullable p) { return DeclName(p); } /// Get a string representation of the name, /// @@ -663,8 +715,12 @@ class DeclName { /// /// \param skipEmptyArgumentNames When true, don't print the argument labels /// if they are all empty. + /// + /// \param escapeIfNeeded When true, escape identifiers with backticks + /// when required. llvm::raw_ostream &print(llvm::raw_ostream &os, - bool skipEmptyArgumentNames = false) const; + bool skipEmptyArgumentNames = false, + bool escapeIfNeeded = false) const; /// Print a "pretty" representation of this declaration name to the given /// stream. @@ -690,68 +746,76 @@ class DeclNameRef { DeclNameRef() : FullName() { } - void *getOpaqueValue() const { return FullName.getOpaqueValue(); } - static DeclNameRef getFromOpaqueValue(void *p); + void *_Nullable getOpaqueValue() const { return FullName.getOpaqueValue(); } + static DeclNameRef getFromOpaqueValue(void *_Nullable p); + + explicit DeclNameRef(ASTContext &C, Identifier moduleSelector, + DeclName fullName) + : FullName(fullName) { } + + explicit DeclNameRef(ASTContext &C, Identifier moduleSelector, + DeclBaseName baseName, ArrayRef argLabels) + : FullName(C, baseName, argLabels) { } explicit DeclNameRef(DeclName FullName) : FullName(FullName) { } - explicit DeclNameRef(DeclBaseName BaseName) - : FullName(BaseName) { } + bool hasModuleSelector() const { + return false; + } - explicit DeclNameRef(Identifier BaseName) - : FullName(BaseName) { } + Identifier getModuleSelector() const { + return Identifier(); + } /// The name of the declaration being referenced. DeclName getFullName() const { return FullName; } - DeclName &getFullName() { - return FullName; - } - /// The base name of the declaration being referenced. DeclBaseName getBaseName() const { - return FullName.getBaseName(); + return getFullName().getBaseName(); } Identifier getBaseIdentifier() const { - return FullName.getBaseIdentifier(); + return getFullName().getBaseIdentifier(); } ArrayRef getArgumentNames() const { - return FullName.getArgumentNames(); + return getFullName().getArgumentNames(); } bool isSimpleName() const { - return FullName.isSimpleName(); + return getFullName().isSimpleName(); } bool isSimpleName(DeclBaseName name) const { - return FullName.isSimpleName(name); + return getFullName().isSimpleName(name); } bool isSimpleName(StringRef name) const { - return FullName.isSimpleName(name); + return getFullName().isSimpleName(name); } bool isSpecial() const { - return FullName.isSpecial(); + return getFullName().isSpecial(); } bool isOperator() const { - return FullName.isOperator(); + return getFullName().isOperator(); } - bool mustAlwaysBeEscaped() const { return FullName.mustAlwaysBeEscaped(); } + bool mustAlwaysBeEscaped() const { + return getFullName().mustAlwaysBeEscaped(); + } bool isCompoundName() const { - return FullName.isCompoundName(); + return getFullName().isCompoundName(); } explicit operator bool() const { - return (bool)FullName; + return (bool)getFullName(); } /// Compare two declaration names, producing -1 if \c *this comes before @@ -791,7 +855,7 @@ class DeclNameRef { return lhs.compare(rhs) >= 0; } - DeclNameRef withoutArgumentLabels() const; + DeclNameRef withoutArgumentLabels(ASTContext &C) const; DeclNameRef withArgumentLabels(ASTContext &C, ArrayRef argumentNames) const; @@ -820,20 +884,19 @@ class DeclNameRef { SWIFT_DEBUG_DUMP; }; -inline DeclNameRef DeclNameRef::getFromOpaqueValue(void *p) { +inline DeclNameRef DeclNameRef::getFromOpaqueValue(void *_Nullable p) { return DeclNameRef(DeclName::getFromOpaqueValue(p)); } -inline DeclNameRef DeclNameRef::withoutArgumentLabels() const { - return DeclNameRef(getBaseName()); +inline DeclNameRef DeclNameRef::withoutArgumentLabels(ASTContext &C) const { + return DeclNameRef(C, getModuleSelector(), getBaseName()); } inline DeclNameRef DeclNameRef::withArgumentLabels( ASTContext &C, ArrayRef argumentNames) const { - return DeclNameRef(DeclName(C, getBaseName(), argumentNames)); + return DeclNameRef(C, getModuleSelector(), getBaseName(), argumentNames); } - inline DeclNameRef DeclNameRef::createSubscript() { return DeclNameRef(DeclBaseName::createSubscript()); } @@ -923,8 +986,8 @@ class ObjCSelector { ObjCSelectorFamily getSelectorFamily() const; - void *getOpaqueValue() const { return Storage.getOpaqueValue(); } - static ObjCSelector getFromOpaqueValue(void *p) { + void *_Nullable getOpaqueValue() const { return Storage.getOpaqueValue(); } + static ObjCSelector getFromOpaqueValue(void *_Nullable p) { return ObjCSelector(DeclName::getFromOpaqueValue(p)); } @@ -970,10 +1033,10 @@ namespace llvm { template<> struct PointerLikeTypeTraits { public: - static inline void *getAsVoidPointer(swift::DeclName name) { + static inline void *_Nullable getAsVoidPointer(swift::DeclName name) { return name.getOpaqueValue(); } - static inline swift::DeclName getFromVoidPointer(void *ptr) { + static inline swift::DeclName getFromVoidPointer(void *_Nullable ptr) { return swift::DeclName::getFromOpaqueValue(ptr); } enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable - 1 }; @@ -1000,10 +1063,10 @@ namespace llvm { template<> struct PointerLikeTypeTraits { public: - static inline void *getAsVoidPointer(swift::DeclNameRef name) { + static inline void *_Nullable getAsVoidPointer(swift::DeclNameRef name) { return name.getOpaqueValue(); } - static inline swift::DeclNameRef getFromVoidPointer(void *ptr) { + static inline swift::DeclNameRef getFromVoidPointer(void *_Nullable ptr) { return swift::DeclNameRef::getFromOpaqueValue(ptr); } enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable }; @@ -1031,10 +1094,10 @@ namespace llvm { template<> struct PointerLikeTypeTraits { public: - static inline void *getAsVoidPointer(swift::ObjCSelector name) { + static inline void *_Nullable getAsVoidPointer(swift::ObjCSelector name) { return name.getOpaqueValue(); } - static inline swift::ObjCSelector getFromVoidPointer(void *ptr) { + static inline swift::ObjCSelector getFromVoidPointer(void *_Nullable ptr) { return swift::ObjCSelector::getFromOpaqueValue(ptr); } enum { NumLowBitsAvailable = 0 }; @@ -1058,4 +1121,8 @@ namespace llvm { }; } // end namespace llvm -#endif +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + +SWIFT_END_NULLABILITY_ANNOTATIONS + +#endif // #ifndef SWIFT_AST_IDENTIFIER_H diff --git a/include/swift/AST/Import.h b/include/swift/AST/Import.h index 0e014d5e24036..7d1f0d89c7c3b 100644 --- a/include/swift/AST/Import.h +++ b/include/swift/AST/Import.h @@ -457,6 +457,20 @@ class ImportPath : public detail::ImportPathBase { Access getAccessPath(ImportKind importKind) const { return getAccessPath(isScopedImportKind(importKind)); } + +private: + struct UnsafePrivateConstructorTag {}; + + // Doesn't require a module name like the public constructor. + // Only used for getEmptyKey() and getTombstoneKey(). + ImportPath(Raw raw, UnsafePrivateConstructorTag tag) : ImportPathBase(raw) {} +public: + static ImportPath getEmptyKey() { + return swift::ImportPath(llvm::DenseMapInfo::getEmptyKey(), UnsafePrivateConstructorTag{}); + } + static ImportPath getTombstoneKey() { + return swift::ImportPath(llvm::DenseMapInfo::getTombstoneKey(), UnsafePrivateConstructorTag{}); + } }; // MARK: - Abstractions of imports @@ -810,6 +824,27 @@ struct DenseMapInfo> { a.accessLevelRange == b.accessLevelRange; } }; + +template <> +class DenseMapInfo { + using ImportPath = swift::ImportPath; +public: + static ImportPath getEmptyKey() { + return swift::ImportPath::getEmptyKey(); + } + static ImportPath getTombstoneKey() { + return swift::ImportPath::getTombstoneKey(); + } + + static unsigned getHashValue(const ImportPath &val) { + return llvm::DenseMapInfo::getHashValue(val.getRaw()); + } + + static bool isEqual(const ImportPath &lhs, + const ImportPath &rhs) { + return lhs == rhs; + } +}; } #endif diff --git a/include/swift/AST/ImportCache.h b/include/swift/AST/ImportCache.h index 7c573d94b5ef8..8c9059f14d5fe 100644 --- a/include/swift/AST/ImportCache.h +++ b/include/swift/AST/ImportCache.h @@ -87,25 +87,20 @@ class ImportSet final : } ArrayRef getTopLevelImports() const { - return {getTrailingObjects(), - NumTopLevelImports}; + return getTrailingObjects(NumTopLevelImports); } ArrayRef getTransitiveImports() const { - return {getTrailingObjects() + - NumTopLevelImports, - NumTransitiveImports}; + return {getTrailingObjects() + NumTopLevelImports, NumTransitiveImports}; } ArrayRef getTransitiveSwiftOnlyImports() const { - return {getTrailingObjects() + - NumTopLevelImports + NumTransitiveImports, + return {getTrailingObjects() + NumTopLevelImports + NumTransitiveImports, NumTransitiveSwiftOnlyImports}; } ArrayRef getAllImports() const { - return {getTrailingObjects(), - NumTopLevelImports + NumTransitiveImports}; + return getTrailingObjects(NumTopLevelImports + NumTransitiveImports); } SWIFT_DEBUG_DUMP; diff --git a/include/swift/AST/InFlightSubstitution.h b/include/swift/AST/InFlightSubstitution.h index f4188366f6f62..e4bd9cc0b1104 100644 --- a/include/swift/AST/InFlightSubstitution.h +++ b/include/swift/AST/InFlightSubstitution.h @@ -23,21 +23,36 @@ #define SWIFT_AST_INFLIGHTSUBSTITUTION_H #include "swift/AST/SubstitutionMap.h" +#include "llvm/ADT/DenseMap.h" namespace swift { class SubstitutionMap; class InFlightSubstitution { - SubstOptions Options; + friend class SubstitutionMap; + TypeSubstitutionFn BaselineSubstType; LookupConformanceFn BaselineLookupConformance; + SubstOptions Options; RecursiveTypeProperties Props; + unsigned RemainingCount : 31; + unsigned InitLimit : 1; + unsigned RemainingDepth : 31; + unsigned LimitReached : 1; struct ActivePackExpansion { bool isSubstExpansion = false; unsigned expansionIndex = 0; }; - SmallVector ActivePackExpansions; + llvm::SmallVector ActivePackExpansions; + llvm::SmallDenseMap SubMaps; + + Type projectLaneFromPackType( + Type substType, unsigned level); + ProtocolConformanceRef projectLaneFromPackConformance( + PackConformance *substPackConf, unsigned level); + + bool checkLimits(Type ty); public: InFlightSubstitution(TypeSubstitutionFn substType, @@ -52,8 +67,7 @@ class InFlightSubstitution { Type substType(SubstitutableType *origType, unsigned level); /// Perform primitive conformance lookup on the given type. - ProtocolConformanceRef lookupConformance(CanType dependentType, - Type conformingReplacementType, + ProtocolConformanceRef lookupConformance(Type dependentType, ProtocolDecl *conformedProtocol, unsigned level); @@ -146,6 +160,10 @@ class InFlightSubstitution { /// Is the given type invariant to substitution? bool isInvariant(Type type) const; + + bool wasLimitReached() const { + return LimitReached; + } }; /// A helper classes that provides stable storage for the query diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 73e3b44485598..02ef359f568e3 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -29,11 +29,13 @@ IDENTIFIER(alloc) IDENTIFIER(allocWithZone) IDENTIFIER(allZeros) IDENTIFIER(accumulated) +IDENTIFIER(append) IDENTIFIER(ActorType) IDENTIFIER(Any) IDENTIFIER(ArrayLiteralElement) IDENTIFIER(asLocalActor) IDENTIFIER(atIndexedSubscript) +IDENTIFIER(basic_string) IDENTIFIER_(bridgeToObjectiveC) IDENTIFIER(buildArray) IDENTIFIER(buildBlock) @@ -127,6 +129,7 @@ IDENTIFIER(main) IDENTIFIER_WITH_NAME(MainEntryPoint, "$main") IDENTIFIER(message) IDENTIFIER(next) +IDENTIFIER(nonsending) IDENTIFIER_(nsErrorDomain) IDENTIFIER(objectAtIndexedSubscript) IDENTIFIER(objectForKeyedSubscript) @@ -168,6 +171,7 @@ IDENTIFIER(value) IDENTIFIER_WITH_NAME(value_, "_value") IDENTIFIER(Void) IDENTIFIER(WinSDK) +IDENTIFIER(warn) IDENTIFIER(with) IDENTIFIER(withArguments) IDENTIFIER(withKeywordArguments) @@ -324,6 +328,7 @@ IDENTIFIER(SerializationRequirement) IDENTIFIER_WITH_NAME(builderSelf, "$builderSelf") // Attribute options +IDENTIFIER(always) IDENTIFIER_(_always) IDENTIFIER_(assumed) IDENTIFIER(checked) diff --git a/include/swift/AST/KnownProtocols.h b/include/swift/AST/KnownProtocols.h index df8247325b7ae..09b1f03be2ffd 100644 --- a/include/swift/AST/KnownProtocols.h +++ b/include/swift/AST/KnownProtocols.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 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -14,6 +14,7 @@ #define SWIFT_AST_KNOWNPROTOCOLS_H #include "swift/ABI/InvertibleProtocols.h" +#include "swift/Basic/InlineBitfield.h" #include "swift/Config.h" namespace llvm { diff --git a/include/swift/AST/KnownStdlibTypes.def b/include/swift/AST/KnownStdlibTypes.def index fda6ed7b37439..77b9d5b84e463 100644 --- a/include/swift/AST/KnownStdlibTypes.def +++ b/include/swift/AST/KnownStdlibTypes.def @@ -70,6 +70,7 @@ KNOWN_STDLIB_TYPE_DECL(WritableKeyPath, NominalTypeDecl, 2) KNOWN_STDLIB_TYPE_DECL(ReferenceWritableKeyPath, NominalTypeDecl, 2) KNOWN_STDLIB_TYPE_DECL(Optional, EnumDecl, 1) +KNOWN_STDLIB_TYPE_DECL(_OptionalNilComparisonType, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(OptionSet, NominalTypeDecl, 1) @@ -101,6 +102,6 @@ KNOWN_STDLIB_TYPE_DECL(Result, NominalTypeDecl, 2) KNOWN_STDLIB_TYPE_DECL(InlineArray, NominalTypeDecl, 2) -KNOWN_STDLIB_TYPE_DECL(SwiftSetting, NominalTypeDecl, 0) +KNOWN_STDLIB_TYPE_DECL(MutableSpan, NominalTypeDecl, 1) #undef KNOWN_STDLIB_TYPE_DECL diff --git a/include/swift/AST/LayoutConstraintKind.h b/include/swift/AST/LayoutConstraintKind.h index 8eeb9fb0796fa..f2bfe10516f78 100644 --- a/include/swift/AST/LayoutConstraintKind.h +++ b/include/swift/AST/LayoutConstraintKind.h @@ -2,48 +2,49 @@ // // 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 - 2025 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 APIs for layout constraints. -// -//===----------------------------------------------------------------------===// - -#include #ifndef SWIFT_LAYOUT_CONSTRAINTKIND_H #define SWIFT_LAYOUT_CONSTRAINTKIND_H +/// `LayoutConstraintKind.h` is imported into Swift. Be *very* careful with +/// what you include here and keep these includes minimal! +/// +/// See include caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" +#include + namespace swift { /// Describes a layout constraint information. -enum class LayoutConstraintKind : uint8_t { +enum class ENUM_EXTENSIBILITY_ATTR(closed) LayoutConstraintKind : uint8_t { // It is not a known layout constraint. - UnknownLayout, + UnknownLayout SWIFT_NAME("unknownLayout"), // It is a layout constraint representing a trivial type of a known size. - TrivialOfExactSize, + TrivialOfExactSize SWIFT_NAME("trivialOfExactSize"), // It is a layout constraint representing a trivial type of a size known to // be no larger than a given size. - TrivialOfAtMostSize, + TrivialOfAtMostSize SWIFT_NAME("trivialOfAtMostSize"), // It is a layout constraint representing a trivial type of an unknown size. - Trivial, + Trivial SWIFT_NAME("trivial"), // It is a layout constraint representing a reference counted class instance. - Class, + Class SWIFT_NAME("class"), // It is a layout constraint representing a reference counted native class // instance. - NativeClass, + NativeClass SWIFT_NAME("nativeClass"), // It is a layout constraint representing a reference counted object. - RefCountedObject, + RefCountedObject SWIFT_NAME("refCountedObject"), // It is a layout constraint representing a native reference counted object. - NativeRefCountedObject, + NativeRefCountedObject SWIFT_NAME("nativeRefCountedObject"), // It is a layout constraint representing a bridge object - BridgeObject, + BridgeObject SWIFT_NAME("bridgeObject"), // It is a layout constraint representing a trivial type of a known stride. - TrivialStride, + TrivialStride SWIFT_NAME("trivialStride"), LastLayout = TrivialStride, }; } // namespace swift diff --git a/include/swift/AST/LazyResolver.h b/include/swift/AST/LazyResolver.h index 074ed8cc74665..1db9f2fd947b9 100644 --- a/include/swift/AST/LazyResolver.h +++ b/include/swift/AST/LazyResolver.h @@ -136,7 +136,7 @@ class alignas(void*) LazyMemberLoader { uint64_t contextData) = 0; // Returns the target parameter of the `@_specialize` attribute or null. - virtual ValueDecl *loadTargetFunctionDecl(const SpecializeAttr *attr, + virtual ValueDecl *loadTargetFunctionDecl(const AbstractSpecializeAttr *attr, uint64_t contextData) = 0; }; diff --git a/include/swift/AST/LifetimeDependence.h b/include/swift/AST/LifetimeDependence.h index 0d8cd14029bbb..7b501920d6cc0 100644 --- a/include/swift/AST/LifetimeDependence.h +++ b/include/swift/AST/LifetimeDependence.h @@ -38,8 +38,9 @@ class SILResultInfo; enum class ParsedLifetimeDependenceKind : uint8_t { Default = 0, - Scope, - Inherit // Only used with deserialized decls + Borrow, + Inherit, // Only used with deserialized decls + Inout }; enum class LifetimeDependenceKind : uint8_t { Inherit = 0, Scope }; @@ -140,17 +141,7 @@ struct LifetimeDescriptor { return getName().str() == "immortal"; } - std::string getString() const { - switch (kind) { - case DescriptorKind::Named: - return getName().str().str(); - case DescriptorKind::Ordered: - return std::to_string(getIndex()); - case DescriptorKind::Self: - return "self"; - } - llvm_unreachable("Invalid DescriptorKind"); - } + std::string getString() const; }; class LifetimeEntry final @@ -169,7 +160,7 @@ class LifetimeEntry final : startLoc(startLoc), endLoc(endLoc), numSources(sources.size()), targetDescriptor(targetDescriptor) { std::uninitialized_copy(sources.begin(), sources.end(), - getTrailingObjects()); + getTrailingObjects()); } size_t numTrailingObjects(OverloadToken) const { @@ -182,46 +173,18 @@ class LifetimeEntry final ArrayRef sources, std::optional targetDescriptor = std::nullopt); + std::string getString() const; SourceLoc getLoc() const { return startLoc; } SourceLoc getStartLoc() const { return startLoc; } SourceLoc getEndLoc() const { return endLoc; } ArrayRef getSources() const { - return {getTrailingObjects(), numSources}; + return getTrailingObjects(numSources); } std::optional getTargetDescriptor() const { return targetDescriptor; } - - std::string getString() const { - std::string result = "@lifetime("; - if (targetDescriptor.has_value()) { - result += targetDescriptor->getString(); - result += ": "; - } - - bool firstElem = true; - for (auto source : getSources()) { - if (!firstElem) { - result += ", "; - } - switch (source.getParsedLifetimeDependenceKind()) { - case ParsedLifetimeDependenceKind::Scope: - result += "borrow "; - break; - case ParsedLifetimeDependenceKind::Inherit: - result += "copy "; - break; - default: - break; - } - result += source.getString(); - firstElem = false; - } - result += ")"; - return result; - } }; class LifetimeDependenceInfo { @@ -245,19 +208,34 @@ class LifetimeDependenceInfo { addressableParamIndicesAndImmortal(addressableParamIndices, isImmortal), conditionallyAddressableParamIndices(conditionallyAddressableParamIndices), targetIndex(targetIndex) { - assert(this->isImmortal() || inheritLifetimeParamIndices || + ASSERT(this->isImmortal() || inheritLifetimeParamIndices || scopeLifetimeParamIndices); - // FIXME: This assert can trigger when Optional/Result support ~Escapable use (rdar://147765187) - // assert(!inheritLifetimeParamIndices || - // !inheritLifetimeParamIndices->isEmpty()); - if (inheritLifetimeParamIndices && inheritLifetimeParamIndices->isEmpty()) { - inheritLifetimeParamIndices = nullptr; - } - assert(!scopeLifetimeParamIndices || !scopeLifetimeParamIndices->isEmpty()); + ASSERT(!inheritLifetimeParamIndices || + !inheritLifetimeParamIndices->isEmpty()); + ASSERT(!scopeLifetimeParamIndices || !scopeLifetimeParamIndices->isEmpty()); assert((!addressableParamIndices || !conditionallyAddressableParamIndices || conditionallyAddressableParamIndices->isDisjointWith( addressableParamIndices))); + + if (CONDITIONAL_ASSERT_enabled()) { + // Ensure inherit/scope/addressable param indices are of the same length + // or 0. + unsigned paramIndicesLength = 0; + if (inheritLifetimeParamIndices) { + paramIndicesLength = inheritLifetimeParamIndices->getCapacity(); + } + if (scopeLifetimeParamIndices) { + ASSERT(paramIndicesLength == 0 || + paramIndicesLength == scopeLifetimeParamIndices->getCapacity()); + paramIndicesLength = scopeLifetimeParamIndices->getCapacity(); + } + if (addressableParamIndices) { + ASSERT(paramIndicesLength == 0 || + paramIndicesLength == addressableParamIndices->getCapacity()); + paramIndicesLength = addressableParamIndices->getCapacity(); + } + } } operator bool() const { return !empty(); } @@ -281,6 +259,19 @@ class LifetimeDependenceInfo { return addressableParamIndicesAndImmortal.getPointer() != nullptr; } + unsigned getParamIndicesLength() const { + if (hasInheritLifetimeParamIndices()) { + return getInheritIndices()->getCapacity(); + } + if (hasScopeLifetimeParamIndices()) { + return getScopeIndices()->getCapacity(); + } + if (hasAddressableParamIndices()) { + return getAddressableIndices()->getCapacity(); + } + return 0; + } + IndexSubset *getInheritIndices() const { return inheritLifetimeParamIndices; } IndexSubset *getScopeIndices() const { return scopeLifetimeParamIndices; } @@ -316,11 +307,6 @@ class LifetimeDependenceInfo { && scopeLifetimeParamIndices->contains(index); } - bool checkAddressable(int index) const { - return hasAddressableParamIndices() - && getAddressableIndices()->contains(index); - } - std::string getString() const; void Profile(llvm::FoldingSetNodeID &ID) const; void getConcatenatedData(SmallVectorImpl &concatenatedData) const; @@ -328,8 +314,7 @@ class LifetimeDependenceInfo { /// Builds LifetimeDependenceInfo from a swift decl, either from the explicit /// lifetime dependence specifiers or by inference based on types and /// ownership modifiers. - static std::optional> - get(AbstractFunctionDecl *decl); + static std::optional> get(ValueDecl *decl); /// Builds LifetimeDependenceInfo from SIL static std::optional> @@ -366,6 +351,9 @@ filterEscapableLifetimeDependencies(GenericSignature sig, SmallVectorImpl &outputs, llvm::function_ref getSubstTargetType); +StringRef +getNameForParsedLifetimeDependenceKind(ParsedLifetimeDependenceKind kind); + } // namespace swift #endif diff --git a/include/swift/AST/MacroDefinition.h b/include/swift/AST/MacroDefinition.h index 17937a9bba82d..7c7538948c43f 100644 --- a/include/swift/AST/MacroDefinition.h +++ b/include/swift/AST/MacroDefinition.h @@ -77,9 +77,6 @@ enum class BuiltinMacroKind : uint8_t { ExternalMacro, /// #isolation, which produces the isolation of the current context IsolationMacro, - /// #SwiftSettings, which allows for the user to set a compiler setting at - /// the file level - SwiftSettingsMacro, }; /// A single replacement diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index bf1a462f308a8..39091dcd3ad78 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -345,8 +345,6 @@ class ModuleDecl /// \see EntryPointInfoTy EntryPointInfoTy EntryPointInfo; - AccessNotesFile accessNotes; - /// Used by the debugger to bypass resilient access to fields. bool BypassResilience = false; @@ -415,8 +413,9 @@ class ModuleDecl /// imports. ImplicitImportList getImplicitImports() const; - AccessNotesFile &getAccessNotes() { return accessNotes; } - const AccessNotesFile &getAccessNotes() const { return accessNotes; } + /// Retrieve the access notes to apply for the module, or \c nullptr if there + /// are no access notes. + const AccessNotesFile *getAccessNotes() const; /// Return whether the module was imported with resilience disabled. The /// debugger does this to access private fields. @@ -434,11 +433,6 @@ class ModuleDecl /// \c nullptr if the source location isn't in this module. SourceFile *getSourceFileContainingLocation(SourceLoc loc); - // Retrieve the buffer ID and source location of the outermost location that - // caused the generation of the buffer containing \p loc. \p loc and its - // buffer if it isn't in a generated buffer or has no original location. - std::pair getOriginalLocation(SourceLoc loc) const; - /// Creates a map from \c #filePath strings to corresponding \c #fileID /// strings, diagnosing any conflicts. /// @@ -823,7 +817,7 @@ class ModuleDecl Bits.ModuleDecl.IsConcurrencyChecked = value; } - /// Whether this module has enable strict memory safety checking. + /// Whether this module has enabled strict memory safety checking. bool strictMemorySafety() const { return Bits.ModuleDecl.StrictMemorySafety; } @@ -832,20 +826,21 @@ class ModuleDecl Bits.ModuleDecl.StrictMemorySafety = value; } - bool isObjCNameLookupCachePopulated() const { - return Bits.ModuleDecl.ObjCNameLookupCachePopulated; + /// Whether this module uses deferred code generation. + bool deferredCodeGen() const { + return Bits.ModuleDecl.DeferredCodeGen; } - void setIsObjCNameLookupCachePopulated(bool value) { - Bits.ModuleDecl.ObjCNameLookupCachePopulated = value; + void setDeferredCodeGen(bool value = true) { + Bits.ModuleDecl.DeferredCodeGen = value; } - bool supportsExtensibleEnums() const { - return Bits.ModuleDecl.ExtensibleEnums; + bool isObjCNameLookupCachePopulated() const { + return Bits.ModuleDecl.ObjCNameLookupCachePopulated; } - void setSupportsExtensibleEnums(bool value = true) { - Bits.ModuleDecl.ExtensibleEnums = value; + void setIsObjCNameLookupCachePopulated(bool value) { + Bits.ModuleDecl.ObjCNameLookupCachePopulated = value; } /// For the main module, retrieves the list of primary source files being @@ -970,6 +965,10 @@ class ModuleDecl const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const; + /// Returns true if any import of \p importedModule has the `@preconcurrency` + /// attribute. + bool isModuleImportedPreconcurrency(const ModuleDecl *importedModule) const; + /// Finds the custom availability domain defined by this module with the /// given identifier and if one exists adds it to results. void @@ -977,7 +976,7 @@ class ModuleDecl SmallVectorImpl &results) const; // Is \p attr accessible as an explicitly imported SPI from this module? - bool isImportedAsSPI(const SpecializeAttr *attr, + bool isImportedAsSPI(const AbstractSpecializeAttr *attr, const ValueDecl *targetDecl) const; // Is \p spiGroup accessible as an explicitly imported SPI from this module? @@ -1158,6 +1157,9 @@ class ModuleDecl /// \returns true if this module is the "swift" standard library module. bool isStdlibModule() const; + /// \returns true if this module is the "Cxx" module. + bool isCxxModule() const; + /// \returns true if this module is the "_Concurrency" standard library module. bool isConcurrencyModule() const; diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 391b2acc0b4a0..14aaf91b4fe3e 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -20,8 +20,10 @@ #include "swift/AST/Import.h" #include "swift/AST/LinkLibrary.h" +#include "swift/Basic/Assertions.h" #include "swift/Basic/CXXStdlibKind.h" #include "swift/Basic/LLVM.h" +#include "swift/Serialization/Validation.h" #include "clang/CAS/CASOptions.h" #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" @@ -30,16 +32,8 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringSet.h" -#include "llvm/CAS/ActionCache.h" -#include "llvm/CAS/CASProvidingFileSystem.h" -#include "llvm/CAS/CASReference.h" #include "llvm/CAS/CachingOnDiskFileSystem.h" -#include "llvm/CAS/ObjectStore.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Mutex.h" -#include "llvm/Support/PrefixMapper.h" -#include "llvm/Support/VirtualFileSystem.h" #include #include #include @@ -55,6 +49,7 @@ class Identifier; class CompilerInstance; class IRGenOptions; class CompilerInvocation; +class DiagnosticEngine; /// Which kind of module dependencies we are looking for. enum class ModuleDependencyKind : int8_t { @@ -67,30 +62,7 @@ enum class ModuleDependencyKind : int8_t { Clang, // Used to model the translation unit's source module SwiftSource, - // Placeholder dependencies are a kind of dependencies used only by the - // dependency scanner. They are swift modules that the scanner will not be - // able to locate in its search paths and which are the responsibility of the - // scanner's client to ensure are provided. - // - // Placeholder dependencies will be specified in the scanner's output - // dependency graph where it is the responsibility of the scanner's client to - // ensure required post-processing takes place to "resolve" them. In order to - // do so, the client (swift driver, or any other client build system) is - // expected to have access to a full dependency graph of all placeholder - // dependencies and be able to replace placeholder nodes in the dependency - // graph with their full dependency trees, `uniquing` common dependency module - // nodes in the process. - // - // One example where placeholder dependencies are employed is when using - // SwiftPM in Explicit Module Build mode. SwiftPM constructs a build plan for - // all targets ahead-of-time. When planning a build for a target that depends - // on other targets, the dependency scanning action is not able to locate - // dependency target modules, because they have not yet been built. Instead, - // the build system treats them as placeholder dependencies and resolves them - // with `actual` dependencies in a post-processing step once dependency graphs - // of all targets, individually, have been computed. - SwiftPlaceholder, - LastKind = SwiftPlaceholder + 1 + LastKind = SwiftSource + 1 }; /// This is used to idenfity a specific macro plugin dependency. @@ -123,6 +95,69 @@ struct ModuleDependencyIDHash { } }; +// An iterable view over multiple joined ArrayRefs +// FIXME: std::ranges::join_view +template +class JoinedArrayRefView { +public: + class Iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = const T*; + using reference = const T&; + Iterator(const JoinedArrayRefView *parent, size_t memberIndex, + size_t elementIndex) + : parentView(parent), collectionIndex(memberIndex), + elementIndex(elementIndex) { + checkAdvance(); + } + const T &operator*() const { + return (parentView->memberCollections[collectionIndex])[elementIndex]; + } + const T *operator->() const { + return &(parentView->memberCollections[collectionIndex])[elementIndex]; + } + Iterator &operator++() { + ++elementIndex; + checkAdvance(); + return *this; + } + bool operator==(const Iterator &other) const { + return collectionIndex == other.collectionIndex && + elementIndex == other.elementIndex; + } + bool operator!=(const Iterator &other) const { return !(*this == other); } + + private: + const JoinedArrayRefView *parentView; + size_t collectionIndex; + size_t elementIndex; + + void checkAdvance() { + while (collectionIndex < parentView->memberCollections.size() && + elementIndex >= parentView->memberCollections[collectionIndex].size()) { + ++collectionIndex; + elementIndex = 0; + } + } + }; + + Iterator begin() const { return Iterator(this, 0, 0); } + Iterator end() const { return Iterator(this, memberCollections.size(), 0); } + + template + JoinedArrayRefView(Arrays ...arrs) { + memberCollections.reserve(sizeof...(arrs)); + (memberCollections.push_back(arrs), ...); + } + +private: + std::vector> memberCollections; +}; +using ModuleDependencyIDCollectionView = JoinedArrayRefView; + using ModuleDependencyIDSet = std::unordered_set; using ModuleDependencyIDSetVector = @@ -154,25 +189,39 @@ struct ScannerImportStatementInfo { uint32_t columnNumber; }; - ScannerImportStatementInfo(std::string importIdentifier, bool isExported) - : importLocations(), importIdentifier(importIdentifier), - isExported(isExported) {} + ScannerImportStatementInfo(std::string importIdentifier) + : importIdentifier(importIdentifier), importLocations(), + isExported(false), accessLevel(AccessLevel::Public) {} + + ScannerImportStatementInfo(std::string importIdentifier, bool isExported, + AccessLevel accessLevel) + : importIdentifier(importIdentifier), importLocations(), + isExported(isExported), accessLevel(accessLevel) {} ScannerImportStatementInfo(std::string importIdentifier, bool isExported, + AccessLevel accessLevel, ImportDiagnosticLocationInfo location) - : importLocations({location}), importIdentifier(importIdentifier), - isExported(isExported) {} + : importIdentifier(importIdentifier), importLocations({location}), + isExported(isExported), accessLevel(accessLevel) {} + + ScannerImportStatementInfo(std::string importIdentifier, bool isExported, + AccessLevel accessLevel, + SmallVector locations) + : importIdentifier(importIdentifier), importLocations(locations), + isExported(isExported), accessLevel(accessLevel) {} void addImportLocation(ImportDiagnosticLocationInfo location) { importLocations.push_back(location); } - /// Buffer, line & column number of the import statement - SmallVector importLocations; /// Imported module string. e.g. "Foo.Bar" in 'import Foo.Bar' std::string importIdentifier; + /// Buffer, line & column number of the import statement + SmallVector importLocations; /// Is this an @_exported import bool isExported; + /// Access level of this dependency + AccessLevel accessLevel; }; /// Base class for the variant storage of ModuleDependencyInfo. @@ -228,6 +277,11 @@ class ModuleDependencyInfoStorageBase { /// The macro dependencies. std::map macroDependencies; + /// A list of Clang modules that are visible to this Swift module. This + /// includes both direct Clang modules as well as transitive Clang + /// module dependencies when they are exported + llvm::StringSet<> visibleClangModules; + /// ModuleDependencyInfo is finalized (with all transitive dependencies /// and inputs). bool finalized; @@ -400,8 +454,10 @@ class SwiftBinaryModuleDependencyStorage StringRef sourceInfoPath, ArrayRef moduleImports, ArrayRef optionalModuleImports, - ArrayRef linkLibraries, StringRef headerImport, - StringRef definingModuleInterface, bool isFramework, bool isStatic, + ArrayRef linkLibraries, + ArrayRef serializedSearchPaths, + StringRef headerImport, StringRef definingModuleInterface, + bool isFramework, bool isStatic, bool isBuiltWithCxxInterop, StringRef moduleCacheKey, StringRef userModuleVersion) : ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftBinary, moduleImports, optionalModuleImports, @@ -409,7 +465,9 @@ class SwiftBinaryModuleDependencyStorage compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath), sourceInfoPath(sourceInfoPath), headerImport(headerImport), definingModuleInterfacePath(definingModuleInterface), + serializedSearchPaths(serializedSearchPaths), isFramework(isFramework), isStatic(isStatic), + isBuiltWithCxxInterop(isBuiltWithCxxInterop), userModuleVersion(userModuleVersion) {} ModuleDependencyInfoStorageBase *clone() const override { @@ -435,6 +493,9 @@ class SwiftBinaryModuleDependencyStorage /// Source files on which the header inputs depend. std::vector headerSourceFiles; + /// Search paths this module was built with which got serialized + std::vector serializedSearchPaths; + /// (Clang) modules on which the header inputs depend. std::vector headerModuleDependencies; @@ -444,6 +505,10 @@ class SwiftBinaryModuleDependencyStorage /// A flag that indicates this dependency is associated with a static archive const bool isStatic; + /// A flag that indicates this dependency module was built + /// with C++ interop enabled + const bool isBuiltWithCxxInterop; + /// The user module version of this binary module. const std::string userModuleVersion; @@ -524,39 +589,6 @@ class ClangModuleDependencyStorage : public ModuleDependencyInfoStorageBase { } }; -/// Describes an placeholder Swift module dependency module stub. -/// -/// This class is mostly an implementation detail for \c ModuleDependencyInfo. - -class SwiftPlaceholderModuleDependencyStorage - : public ModuleDependencyInfoStorageBase { -public: - SwiftPlaceholderModuleDependencyStorage(StringRef compiledModulePath, - StringRef moduleDocPath, - StringRef sourceInfoPath) - : ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftPlaceholder, {}, {}, - {}), - compiledModulePath(compiledModulePath), moduleDocPath(moduleDocPath), - sourceInfoPath(sourceInfoPath) {} - - ModuleDependencyInfoStorageBase *clone() const override { - return new SwiftPlaceholderModuleDependencyStorage(*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; - - static bool classof(const ModuleDependencyInfoStorageBase *base) { - return base->dependencyKind == ModuleDependencyKind::SwiftPlaceholder; - } -}; - // MARK: Module Dependency Info /// Describes the dependencies of a given module. /// @@ -607,15 +639,17 @@ class ModuleDependencyInfo { StringRef sourceInfoPath, ArrayRef moduleImports, ArrayRef optionalModuleImports, - ArrayRef linkLibraries, StringRef headerImport, - StringRef definingModuleInterface, bool isFramework, - bool isStatic, StringRef moduleCacheKey, StringRef userModuleVer) { + ArrayRef linkLibraries, + ArrayRef serializedSearchPaths, + StringRef headerImport, StringRef definingModuleInterface, + bool isFramework, bool isStatic, bool isBuiltWithCxxInterop, + StringRef moduleCacheKey, StringRef userModuleVer) { return ModuleDependencyInfo( std::make_unique( compiledModulePath, moduleDocPath, sourceInfoPath, moduleImports, - optionalModuleImports, linkLibraries, headerImport, - definingModuleInterface,isFramework, isStatic, moduleCacheKey, - userModuleVer)); + optionalModuleImports, linkLibraries, serializedSearchPaths, + headerImport, definingModuleInterface,isFramework, isStatic, + isBuiltWithCxxInterop, moduleCacheKey, userModuleVer)); } /// Describe the main Swift module. @@ -655,16 +689,6 @@ class ModuleDependencyInfo { CASFileSystemRootID, clangIncludeTreeRoot, moduleCacheKey, IsSystem)); } - /// Describe a placeholder dependency swift module. - static ModuleDependencyInfo - forPlaceholderSwiftModuleStub(StringRef compiledModulePath, - StringRef moduleDocPath, - StringRef sourceInfoPath) { - return ModuleDependencyInfo( - std::make_unique( - compiledModulePath, moduleDocPath, sourceInfoPath)); - } - /// Retrieve the module-level imports. ArrayRef getModuleImports() const { return storage->moduleImports; @@ -689,7 +713,7 @@ class ModuleDependencyInfo { storage->importedSwiftModules.assign(dependencyIDs.begin(), dependencyIDs.end()); } - const ArrayRef getImportedSwiftDependencies() const { + ArrayRef getImportedSwiftDependencies() const { return storage->importedSwiftModules; } @@ -698,7 +722,7 @@ class ModuleDependencyInfo { storage->importedClangModules.assign(dependencyIDs.begin(), dependencyIDs.end()); } - const ArrayRef getImportedClangDependencies() const { + ArrayRef getImportedClangDependencies() const { return storage->importedClangModules; } @@ -732,7 +756,7 @@ class ModuleDependencyInfo { } } } - const ArrayRef getHeaderClangDependencies() const { + ArrayRef getHeaderClangDependencies() const { switch (getKind()) { case swift::ModuleDependencyKind::SwiftInterface: { auto swiftInterfaceStorage = @@ -760,21 +784,20 @@ class ModuleDependencyInfo { storage->swiftOverlayDependencies.assign(dependencyIDs.begin(), dependencyIDs.end()); } - const ArrayRef getSwiftOverlayDependencies() const { + ArrayRef getSwiftOverlayDependencies() const { return storage->swiftOverlayDependencies; } - - void - setCrossImportOverlayDependencies(const ArrayRef dependencyIDs) { + void setCrossImportOverlayDependencies( + const ModuleDependencyIDCollectionView dependencyIDs) { assert(isSwiftModule()); storage->crossImportOverlayModules.assign(dependencyIDs.begin(), dependencyIDs.end()); } - const ArrayRef getCrossImportOverlayDependencies() const { + ArrayRef getCrossImportOverlayDependencies() const { return storage->crossImportOverlayModules; } - const ArrayRef getLinkLibraries() const { + ArrayRef getLinkLibraries() const { return storage->linkLibraries; } @@ -783,15 +806,10 @@ class ModuleDependencyInfo { storage->linkLibraries.assign(linkLibraries.begin(), linkLibraries.end()); } - const ArrayRef getAuxiliaryFiles() const { + ArrayRef getAuxiliaryFiles() const { return storage->auxiliaryFiles; } - void - setAuxiliaryFiles(const ArrayRef auxiliaryFiles) { - storage->auxiliaryFiles.assign(auxiliaryFiles.begin(), auxiliaryFiles.end()); - } - bool isStaticLibrary() const { if (auto *detail = getAsSwiftInterfaceModule()) return detail->isStatic; @@ -800,7 +818,7 @@ class ModuleDependencyInfo { return false; } - const ArrayRef getHeaderInputSourceFiles() const { + ArrayRef getHeaderInputSourceFiles() const { if (auto *detail = getAsSwiftInterfaceModule()) return detail->textualModuleDetails.bridgingSourceFiles; if (auto *detail = getAsSwiftSourceModule()) @@ -810,7 +828,7 @@ class ModuleDependencyInfo { return {}; } - std::vector getCommandline() const { + ArrayRef getCommandline() const { if (auto *detail = getAsClangModule()) return detail->buildCommandLine; if (auto *detail = getAsSwiftInterfaceModule()) @@ -833,7 +851,7 @@ class ModuleDependencyInfo { llvm_unreachable("Unexpected type"); } - std::vector getBridgingHeaderCommandline() const { + ArrayRef getBridgingHeaderCommandline() const { if (auto *detail = getAsSwiftSourceModule()) return detail->bridgingHeaderBuildCommandLine; return {}; @@ -872,7 +890,17 @@ class ModuleDependencyInfo { cast(storage.get())->CASFileSystemRootID = rootID; else - llvm_unreachable("Unexpected type"); + llvm_unreachable("Unexpected module dependency kind"); + } + + llvm::StringSet<> &getVisibleClangModules() const { + return storage->visibleClangModules; + } + + void + addVisibleClangModules(const std::vector &moduleNames) const { + storage->visibleClangModules.insert(moduleNames.begin(), + moduleNames.end()); } /// Whether explicit input paths of all the module dependencies @@ -888,7 +916,7 @@ class ModuleDependencyInfo { bool isTestableImport(StringRef moduleName) const; /// Whether the dependencies are for a Swift module: either Textual, Source, - /// Binary, or Placeholder. + /// or Binary bool isSwiftModule() const; /// Whether the dependencies are for a textual interface Swift module or a @@ -904,9 +932,6 @@ class ModuleDependencyInfo { /// Whether the dependencies are for a binary Swift module. bool isSwiftBinaryModule() const; - /// Whether this represents a placeholder module stub - bool isSwiftPlaceholderModule() const; - /// Whether the dependencies are for a Clang module. bool isClangModule() const; @@ -925,13 +950,16 @@ class ModuleDependencyInfo { /// Retrieve the dependencies for a Clang module. const ClangModuleDependencyStorage *getAsClangModule() const; - /// Retrieve the dependencies for a placeholder dependency module stub. - const SwiftPlaceholderModuleDependencyStorage * - getAsPlaceholderDependencyModule() const; + /// Get the path to the module-defining file: + /// `SwiftInterface`: Textual Interface path + /// `SwiftBinary`: Binary module path + /// `Clang`: Module map path + std::string getModuleDefiningPath() const; /// Add a dependency on the given module, if it was not already in the set. void addOptionalModuleImport(StringRef module, bool isExported, + AccessLevel accessLevel, llvm::StringSet<> *alreadyAddedModules = nullptr); /// Add all of the module imports in the given source @@ -942,12 +970,14 @@ class ModuleDependencyInfo { /// Add a dependency on the given module, if it was not already in the set. void addModuleImport(ImportPath::Module module, bool isExported, + AccessLevel accessLevel, llvm::StringSet<> *alreadyAddedModules = nullptr, const SourceManager *sourceManager = nullptr, SourceLoc sourceLocation = SourceLoc()); /// Add a dependency on the given module, if it was not already in the set. void addModuleImport(StringRef module, bool isExported, + AccessLevel accessLevel, llvm::StringSet<> *alreadyAddedModules = nullptr, const SourceManager *sourceManager = nullptr, SourceLoc sourceLocation = SourceLoc()); @@ -999,33 +1029,8 @@ using ModuleNameToDependencyMap = llvm::StringMap; using ModuleDependenciesKindMap = std::unordered_map; - -// MARK: SwiftDependencyTracker -/// Track swift dependency -class SwiftDependencyTracker { -public: - SwiftDependencyTracker(llvm::cas::CachingOnDiskFileSystem &FS, - llvm::PrefixMapper *Mapper, - const CompilerInvocation &CI); - - void startTracking(bool includeCommonDeps = true); - void trackFile(const Twine &path); - llvm::Expected createTreeFromDependencies(); - -private: - llvm::IntrusiveRefCntPtr FS; - llvm::PrefixMapper *Mapper; - - struct FileEntry { - llvm::cas::ObjectRef FileRef; - size_t Size; - - FileEntry(llvm::cas::ObjectRef FileRef, size_t Size) - : FileRef(FileRef), Size(Size) {} - }; - llvm::StringMap CommonFiles; - std::map TrackedFiles; -}; +using BridgeClangDependencyCallback = llvm::function_ref; // MARK: SwiftDependencyScanningService /// A carrier of state shared among possibly multiple invocations of the @@ -1038,24 +1043,6 @@ class SwiftDependencyScanningService { std::optional ClangScanningService; - /// CachingOnDiskFileSystem for dependency tracking. - llvm::IntrusiveRefCntPtr CacheFS; - - /// If use clang include tree. - bool UseClangIncludeTree = false; - - /// CAS Instance. - std::shared_ptr CAS; - std::shared_ptr ActionCache; - - /// File prefix mapper. - std::unique_ptr Mapper; - - /// The global file system cache. - std::optional< - clang::tooling::dependencies::DependencyScanningFilesystemSharedCache> - SharedFilesystemCache; - /// Shared state mutual-exclusivity lock mutable llvm::sys::SmartMutex ScanningServiceGlobalLock; @@ -1067,60 +1054,6 @@ class SwiftDependencyScanningService { operator=(const SwiftDependencyScanningService &) = delete; virtual ~SwiftDependencyScanningService() {} - /// Query the service's filesystem cache - clang::tooling::dependencies::DependencyScanningFilesystemSharedCache &getSharedCache() { - assert(SharedFilesystemCache && "Expected a shared cache"); - return *SharedFilesystemCache; - } - - /// Query the service's filesystem cache - clang::tooling::dependencies::DependencyScanningFilesystemSharedCache & - getSharedFilesystemCache() { - assert(SharedFilesystemCache && "Expected a shared cache"); - return *SharedFilesystemCache; - } - - llvm::cas::CachingOnDiskFileSystem &getSharedCachingFS() const { - assert(CacheFS && "Expect CachingOnDiskFileSystem"); - return *CacheFS; - } - - llvm::cas::ObjectStore &getCAS() const { - assert(CAS && "Expect CAS available"); - return *CAS; - } - - std::optional - createSwiftDependencyTracker(const CompilerInvocation &CI) { - if (!CacheFS) - return std::nullopt; - - return SwiftDependencyTracker(*CacheFS, Mapper.get(), CI); - } - - llvm::IntrusiveRefCntPtr getClangScanningFS() const { - if (UseClangIncludeTree) - return llvm::cas::createCASProvidingFileSystem( - CAS, llvm::vfs::createPhysicalFileSystem()); - - if (CacheFS) - return CacheFS->createProxyFS(); - - return llvm::vfs::createPhysicalFileSystem(); - } - - bool hasPathMapping() const { - return Mapper && !Mapper->getMappings().empty(); - } - llvm::PrefixMapper *getPrefixMapper() const { return Mapper.get(); } - std::string remapPath(StringRef Path) const { - if (!Mapper) - return Path.str(); - return Mapper->mapToString(Path); - } - - bool hasCAS() const { return (bool)CAS; } - /// Setup caching service. bool setupCachingDependencyScanningService(CompilerInstance &Instance); @@ -1134,13 +1067,10 @@ class SwiftDependencyScanningService { // MARK: ModuleDependenciesCache /// This "local" dependencies cache persists only for the duration of a given -/// scanning action, and wraps an instance of a `SwiftDependencyScanningService` -/// which may carry cached scanning information from prior scanning actions. -/// This cache maintains a store of references to all dependencies found within -/// the current scanning action (with their values stored in the global Cache). +/// scanning action, and maintains a store of references to all dependencies +/// found within the current scanning action. class ModuleDependenciesCache { private: - SwiftDependencyScanningService &globalScanningService; /// Discovered dependencies ModuleDependenciesKindMap ModuleDependenciesMap; /// Set containing all of the Clang modules that have already been seen. @@ -1149,10 +1079,6 @@ class ModuleDependenciesCache { std::string mainScanModuleName; /// The context hash of the current scanning invocation std::string scannerContextHash; - /// The location of where the explicitly-built modules will be output to - std::string moduleOutputPath; - /// The location of where the explicitly-built SDK modules will be output to - std::string sdkModuleOutputPath; /// The timestamp of the beginning of the scanning query action /// using this cache const llvm::sys::TimePoint<> scanInitializationTime; @@ -1165,10 +1091,7 @@ class ModuleDependenciesCache { getDependencyReferencesMap(ModuleDependencyKind kind) const; public: - ModuleDependenciesCache(SwiftDependencyScanningService &globalScanningService, - const std::string &mainScanModuleName, - const std::string &moduleOutputPath, - const std::string &sdkModuleOutputPath, + ModuleDependenciesCache(const std::string &mainScanModuleName, const std::string &scanningContextHash); ModuleDependenciesCache(const ModuleDependenciesCache &) = delete; ModuleDependenciesCache &operator=(const ModuleDependenciesCache &) = delete; @@ -1189,12 +1112,6 @@ class ModuleDependenciesCache { /// Whether we have cached dependency information for the given Swift module. bool hasSwiftDependency(StringRef moduleName) const; - SwiftDependencyScanningService &getScanService() { - return globalScanningService; - } - const SwiftDependencyScanningService &getScanService() const { - return globalScanningService; - } const llvm::DenseSet & getAlreadySeenClangModules() const { return alreadySeenClangModules; @@ -1202,16 +1119,18 @@ class ModuleDependenciesCache { void addSeenClangModule(clang::tooling::dependencies::ModuleID newModule) { alreadySeenClangModules.insert(newModule); } - StringRef getModuleOutputPath() const { return moduleOutputPath; } - StringRef getSDKModuleOutputPath() const { return sdkModuleOutputPath; } /// Query all dependencies - ModuleDependencyIDSetVector + ModuleDependencyIDCollectionView getAllDependencies(const ModuleDependencyID &moduleID) const; + /// Query all directly-imported dependencies + ModuleDependencyIDCollectionView + getDirectImportedDependencies(const ModuleDependencyID &moduleID) const; + /// Query all Clang module dependencies. - ModuleDependencyIDSetVector - getClangDependencies(const ModuleDependencyID &moduleID) const; + ModuleDependencyIDCollectionView + getAllClangDependencies(const ModuleDependencyID &moduleID) const; /// Query all directly-imported Swift dependencies llvm::ArrayRef @@ -1228,6 +1147,9 @@ class ModuleDependenciesCache { /// Query all cross-import overlay dependencies llvm::ArrayRef getCrossImportOverlayDependencies(const ModuleDependencyID &moduleID) const; + /// Query all visible Clang modules for a given Swift dependency + llvm::StringSet<>& + getVisibleClangModules(ModuleDependencyID moduleID) const; /// Look for module dependencies for a module with the given ID /// @@ -1258,8 +1180,11 @@ class ModuleDependenciesCache { void recordDependency(StringRef moduleName, ModuleDependencyInfo dependencies); - /// Record dependencies for the given module collection. - void recordDependencies(ModuleDependencyVector moduleDependencies); + /// Record dependencies for the given collection of Clang modules. + void recordClangDependencies( + const clang::tooling::dependencies::ModuleDepsGraph &dependencies, + DiagnosticEngine &diags, + BridgeClangDependencyCallback bridgeClangModule); /// Update stored dependencies for the given module. void updateDependency(ModuleDependencyID moduleID, @@ -1289,9 +1214,13 @@ class ModuleDependenciesCache { setHeaderClangDependencies(ModuleDependencyID moduleID, const ArrayRef dependencyIDs); /// Resolve this module's cross-import overlay dependencies + void setCrossImportOverlayDependencies( + ModuleDependencyID moduleID, + const ModuleDependencyIDCollectionView dependencyIDs); + /// Add to this module's set of visible Clang modules void - setCrossImportOverlayDependencies(ModuleDependencyID moduleID, - const ArrayRef dependencyIDs); + addVisibleClangModules(ModuleDependencyID moduleID, + const std::vector &moduleNames); StringRef getMainModuleName() const { return mainScanModuleName; } diff --git a/include/swift/AST/ModuleLoader.h b/include/swift/AST/ModuleLoader.h index 3cde841b0b65f..8411c80dd2169 100644 --- a/include/swift/AST/ModuleLoader.h +++ b/include/swift/AST/ModuleLoader.h @@ -24,8 +24,6 @@ #include "swift/Basic/LLVM.h" #include "swift/Basic/Located.h" #include "swift/Basic/SourceLoc.h" -#include "clang/Basic/FileManager.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/TinyPtrVector.h" @@ -38,11 +36,8 @@ class FileCollectorBase; class PrefixMapper; namespace vfs { class OutputBackend; -} -namespace cas { -class CachingOnDiskFileSystem; -} -} +} // namespace vfs +} // namespace llvm namespace clang { class DependencyCollector; @@ -367,17 +362,6 @@ class ModuleLoader { /// Discover overlays declared alongside this file and add information about /// them to it. void findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module, FileUnit *file); - - /// Retrieve the dependencies for the given, named module, or \c None - /// if no such module exists. - virtual llvm::SmallVector, 1> - getModuleDependencies(Identifier moduleName, - StringRef moduleOutputPath, StringRef sdkModuleOutputPath, - const llvm::DenseSet &alreadySeenClangModules, - clang::tooling::dependencies::DependencyScanningTool &clangScanningTool, - InterfaceSubContextDelegate &delegate, - llvm::PrefixMapper *mapper = nullptr, - bool isTestableImport = false) = 0; }; } // namespace swift diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index d678f9a7c3254..262f41f7c5bf1 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -604,6 +604,11 @@ void forEachPotentialAttachedMacro( /// Describes an inherited nominal entry. struct InheritedNominalEntry : Located { + /// The `TypeRepr` of the inheritance clause entry from which this nominal was + /// sourced, if any. For example, if this is a conformance to `Y` declared as + /// `struct S: X, Y & Z {}`, this is the `TypeRepr` for `Y & Z`. + TypeRepr *inheritedTypeRepr; + ConformanceAttributes attributes; /// Whether this inherited entry was suppressed via "~". @@ -612,10 +617,10 @@ struct InheritedNominalEntry : Located { InheritedNominalEntry() { } InheritedNominalEntry(NominalTypeDecl *item, SourceLoc loc, - ConformanceAttributes attributes, - bool isSuppressed) - : Located(item, loc), attributes(attributes), - isSuppressed(isSuppressed) {} + TypeRepr *inheritedTypeRepr, + ConformanceAttributes attributes, bool isSuppressed) + : Located(item, loc), inheritedTypeRepr(inheritedTypeRepr), + attributes(attributes), isSuppressed(isSuppressed) {} }; /// Retrieve the set of nominal type declarations that are directly diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index 36fe7b6974dbe..0ce342507dfc7 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -267,7 +267,7 @@ class HasMissingDesignatedInitializersRequest : /// Request the nominal declaration extended by a given extension declaration. class ExtendedNominalRequest : public SimpleRequest< - ExtendedNominalRequest, NominalTypeDecl *(ExtensionDecl *), + ExtendedNominalRequest, NominalTypeDecl *(const ExtensionDecl *), RequestFlags::SeparatelyCached | RequestFlags::DependencySink> { public: using SimpleRequest::SimpleRequest; @@ -276,8 +276,7 @@ class ExtendedNominalRequest friend SimpleRequest; // Evaluation. - NominalTypeDecl * - evaluate(Evaluator &evaluator, ExtensionDecl *ext) const; + NominalTypeDecl * evaluate(Evaluator &evaluator, const ExtensionDecl *ext) const; public: // Separate caching. @@ -651,7 +650,7 @@ class OperatorLookupDescriptor final { DeclContext *getDC() const { if (auto *module = getModule()) return module; - return fileOrModule.get(); + return cast(fileOrModule); } friend llvm::hash_code hash_value(const OperatorLookupDescriptor &desc) { diff --git a/include/swift/AST/NameLookupTypeIDZone.def b/include/swift/AST/NameLookupTypeIDZone.def index a025901ece813..584e39f862771 100644 --- a/include/swift/AST/NameLookupTypeIDZone.def +++ b/include/swift/AST/NameLookupTypeIDZone.def @@ -32,7 +32,7 @@ SWIFT_REQUEST(NameLookup, DirectPrecedenceGroupLookupRequest, TinyPtrVector(OperatorLookupDescriptor), Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, ExtendedNominalRequest, - NominalTypeDecl *(ExtensionDecl *), SeparatelyCached, + NominalTypeDecl *(const ExtensionDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(NameLookup, GenericParamListRequest, GenericParamList *(GenericContext *), SeparatelyCached, diff --git a/include/swift/AST/ParameterList.h b/include/swift/AST/ParameterList.h index 344db0c3f045d..0ed8c758d5a6f 100644 --- a/include/swift/AST/ParameterList.h +++ b/include/swift/AST/ParameterList.h @@ -87,10 +87,10 @@ class alignas(ParamDecl *) ParameterList final : ParamDecl *back() const { return getArray().back(); } MutableArrayRef getArray() { - return {getTrailingObjects(), numParameters}; + return getTrailingObjects(numParameters); } ArrayRef getArray() const { - return {getTrailingObjects(), numParameters}; + return getTrailingObjects(numParameters); } size_t size() const { diff --git a/include/swift/AST/Pattern.h b/include/swift/AST/Pattern.h index 4d827607c85c9..dc2a836ed0bc5 100644 --- a/include/swift/AST/Pattern.h +++ b/include/swift/AST/Pattern.h @@ -349,10 +349,10 @@ class TuplePattern final : public Pattern, } MutableArrayRef getElements() { - return {getTrailingObjects(), getNumElements()}; + return getTrailingObjects(getNumElements()); } ArrayRef getElements() const { - return {getTrailingObjects(), getNumElements()}; + return getTrailingObjects(getNumElements()); } const TuplePatternElt &getElement(unsigned i) const {return getElements()[i];} @@ -617,10 +617,10 @@ class EnumElementPattern : public Pattern { } Expr *getUnresolvedOriginalExpr() const { - return ElementDeclOrUnresolvedOriginalExpr.get(); + return cast(ElementDeclOrUnresolvedOriginalExpr); } bool hasUnresolvedOriginalExpr() const { - return ElementDeclOrUnresolvedOriginalExpr.is(); + return isa(ElementDeclOrUnresolvedOriginalExpr); } void setUnresolvedOriginalExpr(Expr *e) { ElementDeclOrUnresolvedOriginalExpr = e; diff --git a/include/swift/AST/PlatformKind.h b/include/swift/AST/PlatformKind.h index a8a869a36777a..3b3f98581c464 100644 --- a/include/swift/AST/PlatformKind.h +++ b/include/swift/AST/PlatformKind.h @@ -1,103 +1,38 @@ -//===--- PlatformKind.h - Swift Language Platform Kinds ---------*- C++ -*-===// +//===--- AST/PlatformKind.h - Swift Language Platform Kinds -----*- C++ -*-===// // // 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) 2025 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 platform kinds for API availability. -// +/// +/// This file defines the platform kinds for API availability. +/// //===----------------------------------------------------------------------===// #ifndef SWIFT_AST_PLATFORM_KIND_H #define SWIFT_AST_PLATFORM_KIND_H -#include "swift/Basic/LLVM.h" -#include "swift/Config.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/VersionTuple.h" -#include +/// `PlatformKind.h` is imported into Swift. Be *very* careful with what you +/// include here and keep these includes minimal! +/// +/// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" +#include namespace swift { -class LangOptions; - /// Available platforms for the availability attribute. -enum class PlatformKind: uint8_t { +enum class ENUM_EXTENSIBILITY_ATTR(closed) PlatformKind : uint8_t { none, #define AVAILABILITY_PLATFORM(X, PrettyName) X, #include "swift/AST/PlatformKinds.def" }; -/// Returns the short string representing the platform, suitable for -/// use in availability specifications (e.g., "OSX"). -StringRef platformString(PlatformKind platform); - -/// Returns the platform kind corresponding to the passed-in short platform name -/// or None if such a platform kind does not exist. -std::optional platformFromString(StringRef Name); - -/// Safely converts the given unsigned value to a valid \c PlatformKind value or -/// \c nullopt otherwise. -std::optional platformFromUnsigned(unsigned value); - -/// Returns a valid platform string that is closest to the candidate string -/// based on edit distance. Returns \c None if the closest valid platform -/// distance is not within a minimum threshold. -std::optional closestCorrectedPlatformString(StringRef candidate); - -/// Returns a human-readable version of the platform name as a string, suitable -/// for emission in diagnostics (e.g., "macOS"). -StringRef prettyPlatformString(PlatformKind platform); - -/// Returns the base platform for an application-extension platform. For example -/// `iOS` would be returned for `iOSApplicationExtension`. Returns `None` for -/// platforms which are not application extension platforms. -std::optional -basePlatformForExtensionPlatform(PlatformKind Platform); - -/// Returns true if \p Platform represents and application extension platform, -/// e.g. `iOSApplicationExtension`. -inline bool isApplicationExtensionPlatform(PlatformKind Platform) { - return basePlatformForExtensionPlatform(Platform).has_value(); -} - -/// Returns whether the passed-in platform is active, given the language -/// options. A platform is active if either it is the target platform or its -/// AppExtension variant is the target platform. For example, OS X is -/// considered active when the target operating system is OS X and app extension -/// restrictions are enabled, but OSXApplicationExtension is not considered -/// active when the target platform is OS X and app extension restrictions are -/// disabled. PlatformKind::none is always considered active. -/// If ForTargetVariant is true then for zippered builds the target-variant -/// triple will be used rather than the target to determine whether the -/// platform is active. -bool isPlatformActive(PlatformKind Platform, const LangOptions &LangOpts, - bool ForTargetVariant = false, bool ForRuntimeQuery = false); - -/// Returns the target platform for the given language options. -PlatformKind targetPlatform(const LangOptions &LangOpts); - -/// Returns the target variant platform for the given language options. -PlatformKind targetVariantPlatform(const LangOptions &LangOpts); - -/// Returns true when availability attributes from the "parent" platform -/// should also apply to the "child" platform for declarations without -/// an explicit attribute for the child. -bool inheritsAvailabilityFromPlatform(PlatformKind Child, PlatformKind Parent); - -llvm::VersionTuple canonicalizePlatformVersion( - PlatformKind platform, const llvm::VersionTuple &version); - -/// Returns true if \p Platform should be considered to be SPI and therefore not -/// printed in public `.swiftinterface` files, for example. -bool isPlatformSPI(PlatformKind Platform); - } // end namespace swift -#endif +#endif // SWIFT_AST_PLATFORM_KIND_H diff --git a/include/swift/AST/PlatformKindUtils.h b/include/swift/AST/PlatformKindUtils.h new file mode 100644 index 0000000000000..e0f3f240a0e9e --- /dev/null +++ b/include/swift/AST/PlatformKindUtils.h @@ -0,0 +1,101 @@ +//===--- AST/PlatformKindUtils.h --------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 declares operations for working with `PlatformKind`. +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_PLATFORM_KIND_UTILS_H +#define SWIFT_AST_PLATFORM_KIND_UTILS_H + +#include "swift/AST/PlatformKind.h" +#include "swift/Basic/LLVM.h" +#include "swift/Config.h" +#include "llvm/Support/VersionTuple.h" +#include "llvm/TargetParser/Triple.h" +#include + +namespace swift { + +class LangOptions; + +/// Returns the short string representing the platform, suitable for +/// use in availability specifications (e.g., "OSX"). +StringRef platformString(PlatformKind platform); + +/// Returns the platform kind corresponding to the passed-in short platform name +/// or None if such a platform kind does not exist. +std::optional platformFromString(StringRef Name); + +/// Safely converts the given unsigned value to a valid \c PlatformKind value or +/// \c nullopt otherwise. +std::optional platformFromUnsigned(unsigned value); + +/// Returns a valid platform string that is closest to the candidate string +/// based on edit distance. Returns \c None if the closest valid platform +/// distance is not within a minimum threshold. +std::optional closestCorrectedPlatformString(StringRef candidate); + +/// Returns a human-readable version of the platform name as a string, suitable +/// for emission in diagnostics (e.g., "macOS"). +StringRef prettyPlatformString(PlatformKind platform); + +/// Returns the base platform for an application-extension platform. For example +/// `iOS` would be returned for `iOSApplicationExtension`. Returns `None` for +/// platforms which are not application extension platforms. +std::optional +basePlatformForExtensionPlatform(PlatformKind Platform); + +/// Returns true if \p Platform represents and application extension platform, +/// e.g. `iOSApplicationExtension`. +inline bool isApplicationExtensionPlatform(PlatformKind Platform) { + return basePlatformForExtensionPlatform(Platform).has_value(); +} + +/// Returns whether the passed-in platform is active, given the language +/// options. A platform is active if either it is the target platform or its +/// AppExtension variant is the target platform. For example, OS X is +/// considered active when the target operating system is OS X and app extension +/// restrictions are enabled, but OSXApplicationExtension is not considered +/// active when the target platform is OS X and app extension restrictions are +/// disabled. PlatformKind::none is always considered active. +/// If ForTargetVariant is true then for zippered builds the target-variant +/// triple will be used rather than the target to determine whether the +/// platform is active. +bool isPlatformActive(PlatformKind Platform, const LangOptions &LangOpts, + bool ForTargetVariant = false, bool ForRuntimeQuery = false); + +/// Returns the target platform for the given language options. +PlatformKind targetPlatform(const LangOptions &LangOpts); + +/// Returns the target variant platform for the given language options. +PlatformKind targetVariantPlatform(const LangOptions &LangOpts); + +/// Returns true when availability attributes from the "parent" platform +/// should also apply to the "child" platform for declarations without +/// an explicit attribute for the child. +bool inheritsAvailabilityFromPlatform(PlatformKind Child, PlatformKind Parent); + +/// Returns the LLVM triple OS type for the given platform, if there is one. +std::optional +tripleOSTypeForPlatform(PlatformKind platform); + +llvm::VersionTuple canonicalizePlatformVersion( + PlatformKind platform, const llvm::VersionTuple &version); + +/// Returns true if \p Platform should be considered to be SPI and therefore not +/// printed in public `.swiftinterface` files, for example. +bool isPlatformSPI(PlatformKind Platform); + +} // end namespace swift + +#endif // SWIFT_AST_PLATFORM_KIND_UTILS_H diff --git a/include/swift/AST/PlatformKinds.def b/include/swift/AST/PlatformKinds.def index dd10bf495b65b..9b2fcdadd554a 100644 --- a/include/swift/AST/PlatformKinds.def +++ b/include/swift/AST/PlatformKinds.def @@ -34,6 +34,7 @@ AVAILABILITY_PLATFORM(visionOSApplicationExtension, "application extensions for AVAILABILITY_PLATFORM(macOSApplicationExtension, "application extensions for macOS") AVAILABILITY_PLATFORM(macCatalyst, "Mac Catalyst") AVAILABILITY_PLATFORM(macCatalystApplicationExtension, "application extensions for Mac Catalyst") +AVAILABILITY_PLATFORM(FreeBSD, "FreeBSD") AVAILABILITY_PLATFORM(OpenBSD, "OpenBSD") AVAILABILITY_PLATFORM(Windows, "Windows") diff --git a/include/swift/AST/PluginLoader.h b/include/swift/AST/PluginLoader.h index fdcd73f13ba97..50f46a678d2fb 100644 --- a/include/swift/AST/PluginLoader.h +++ b/include/swift/AST/PluginLoader.h @@ -52,10 +52,18 @@ class PluginLoader { /// Get or lazily create and populate 'PluginMap'. llvm::DenseMap &getPluginMap(); + /// Resolved plugin path remappings. + std::vector> PathRemap; + public: PluginLoader(ASTContext &Ctx, DependencyTracker *DepTracker, + std::optional>> + Remap = std::nullopt, bool disableSandbox = false) - : Ctx(Ctx), DepTracker(DepTracker), disableSandbox(disableSandbox) {} + : Ctx(Ctx), DepTracker(DepTracker), disableSandbox(disableSandbox) { + if (Remap) + PathRemap = std::move(*Remap); + } void setRegistry(PluginRegistry *newValue); PluginRegistry *getRegistry(); diff --git a/include/swift/AST/PluginRegistry.h b/include/swift/AST/PluginRegistry.h index f624a20b80d2d..02af09e0f808d 100644 --- a/include/swift/AST/PluginRegistry.h +++ b/include/swift/AST/PluginRegistry.h @@ -152,6 +152,9 @@ class LoadedExecutablePlugin : public CompilerPlugin { : process(process), input(input), output(output) {} ~PluginProcess(); + PluginProcess(const PluginProcess &) = delete; + PluginProcess &operator=(const PluginProcess &) = delete; + ssize_t write(const void *buf, size_t nbyte) const; ssize_t read(void *buf, size_t nbyte) const; }; diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 8c2773f282e71..504ef198311e4 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -16,9 +16,11 @@ #include "swift/AST/AttrKind.h" #include "swift/AST/Identifier.h" #include "swift/AST/TypeOrExtensionDecl.h" +#include "swift/Basic/OptionSet.h" #include "swift/Basic/STLExtras.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/STLExtras.h" #include #include #include @@ -72,15 +74,15 @@ class BracketOptions { CloseExtension(CloseExtension), CloseNominal(CloseNominal) {} - bool shouldOpenExtension(const Decl *D) { + bool shouldOpenExtension(const Decl *D) const { return D != Target || OpenExtension; } - bool shouldCloseExtension(const Decl *D) { + bool shouldCloseExtension(const Decl *D) const { return D != Target || CloseExtension; } - bool shouldCloseNominal(const Decl *D) { + bool shouldCloseNominal(const Decl *D) const { return D != Target || CloseNominal; } }; @@ -123,11 +125,51 @@ struct ShouldPrintChecker { virtual ~ShouldPrintChecker() = default; }; +/// Type-printing options which should only be applied to the outermost +/// type. +enum class NonRecursivePrintOption: uint32_t { + /// Print `Optional` as `T!`. + ImplicitlyUnwrappedOptional = 1 << 0, +}; +using NonRecursivePrintOptions = OptionSet; + /// Options for printing AST nodes. /// /// A default-constructed PrintOptions is suitable for printing to users; /// there are also factory methods for specific use cases. +/// +/// The value semantics of PrintOptions are a little messed up. We generally +/// pass around options by const reference in order to (1) make it +/// easier to pass in temporaries and (2) discourage direct local mutation +/// in favor of the OverrideScope system below. However, that override +/// system assumes that PrintOptions objects are always actually mutable. struct PrintOptions { + + /// Explicitly copy these print options. You should generally aim to + /// avoid doing this, especially in deeply-embedded code, because + /// PrintOptions is a relatively heavyweight type (and is likely to + /// only get more heavyweight). Instead, try to use OverrideScope. + PrintOptions clone() const { return *this; } + + /// Allow move construction and assignment. We don't expect to + /// actually use these much, but there isn't too much harm from + /// them. + PrintOptions(PrintOptions &&) = default; + PrintOptions &operator=(PrintOptions &&) = default; + +private: + /// Disallow implicit copying, but make it available privately for the + /// use of clone(). + PrintOptions(const PrintOptions &) = default; + + /// Disallow copy assignment completely, which we don't even need + /// privately. + PrintOptions &operator=(const PrintOptions &) = delete; + +public: + // defined later in this file + class OverrideScope; + /// The indentation width. unsigned Indent = 2; @@ -143,6 +185,9 @@ struct PrintOptions { /// Whether to print *any* accessors on properties. bool PrintPropertyAccessors = true; + /// Use \c let for a read-only computed property. + bool InferPropertyIntroducerFromAccessors = false; + /// Whether to print *any* accessors on subscript. bool PrintSubscriptAccessors = true; @@ -172,6 +217,10 @@ struct PrintOptions { /// Whether to print the bodies of accessors in protocol context. bool PrintAccessorBodiesInProtocols = false; + /// Whether to print the parameter list of accessors like \c set . (Even when + /// \c true , parameters marked implicit still won't be printed.) + bool PrintExplicitAccessorParameters = true; + /// Whether to print type definitions. bool TypeDefinitions = false; @@ -234,6 +283,12 @@ struct PrintOptions { /// \see FileUnit::getExportedModuleName bool UseExportedModuleNames = false; + /// If true, printed module names will use the "public" (for documentation) + /// name, which may be different from the regular name. + /// + /// \see FileUnit::getPublicModuleName + bool UsePublicModuleNames = false; + /// Use the original module name to qualify a symbol. bool UseOriginallyDefinedInModuleNames = false; @@ -345,9 +400,11 @@ struct PrintOptions { /// Whether to print the internal layout name instead of AnyObject, etc. bool PrintInternalLayoutName = false; - /// Suppress emitting isolated or async deinit, and emit open containing class - /// as public - bool SuppressIsolatedDeinit = false; + /// Suppress @_lifetime attribute and emit @lifetime instead. + bool SuppressLifetimes = false; + + /// Suppress @inline(always) attribute and emit @inline(__always) instead. + bool SuppressInlineAlways = false; /// Whether to print the \c{/*not inherited*/} comment on factory initializers. bool PrintFactoryInitializerComment = true; @@ -379,27 +436,15 @@ struct PrintOptions { /// Whether to suppress printing of custom attributes that are expanded macros. bool SuppressExpandedMacros = true; - /// Suppress the @isolated(any) attribute. - bool SuppressIsolatedAny = false; - /// Suppress 'isolated' and '#isolation' on isolated parameters with optional type. bool SuppressOptionalIsolatedParams = false; - /// Suppress 'sending' on arguments and results. - bool SuppressSendingArgsAndResults = false; - /// Suppress printing of '~Proto' for suppressible, non-invertible protocols. bool SuppressConformanceSuppression = false; - /// Replace BitwiseCopyable with _BitwiseCopyable. - bool SuppressBitwiseCopyable = false; - /// Suppress modify/read accessors. bool SuppressCoroutineAccessors = false; - /// Suppress the @execution attribute - bool SuppressExecutionAttribute = false; - /// List of attribute kinds that should not be printed. std::vector ExcludeAttrList = { DeclAttrKind::Transparent, DeclAttrKind::Effects, @@ -540,13 +585,6 @@ struct PrintOptions { /// yet. llvm::SmallSet *AliasModuleNamesTargets = nullptr; - /// When printing an Optional, rather than printing 'T?', print - /// 'T!'. Used as a modifier only when we know we're printing - /// something that was declared as an implicitly unwrapped optional - /// at the top level. This is stripped out of the printing options - /// for optionals that are nested within other optionals. - bool PrintOptionalAsImplicitlyUnwrapped = false; - /// Replaces the name of private and internal properties of types with '_'. bool OmitNameOfInaccessibleProperties = false; @@ -591,7 +629,7 @@ struct PrintOptions { bool AlwaysDesugarArraySliceTypes = false; /// Whether to always desugar inline array types from - /// `[ x ]` to `InlineArray` + /// `[ of ]` to `InlineArray` bool AlwaysDesugarInlineArrayTypes = false; /// Whether to always desugar dictionary types @@ -659,6 +697,13 @@ struct PrintOptions { return result; } + /// Print options suitable for debug printing. + static PrintOptions forDebugging() { + PrintOptions result; + result.PrintTypesForDebugging = true; + return result; + } + /// Retrieve the set of options suitable for diagnostics printing. static PrintOptions printForDiagnostics(AccessLevel accessFilter, bool printFullConvention) { @@ -707,6 +752,7 @@ struct PrintOptions { result.MapCrossImportOverlaysToDeclaringModule = true; result.PrintCurrentMembersOnly = false; result.SuppressExpandedMacros = true; + result.UsePublicModuleNames = true; return result; } @@ -738,6 +784,8 @@ struct PrintOptions { void setBaseType(Type T); void initForSynthesizedExtension(TypeOrExtensionDecl D); + void initForSynthesizedExtensionInScope(TypeOrExtensionDecl D, + OverrideScope &scope) const; void clearSynthesizedExtension(); @@ -812,7 +860,87 @@ struct PrintOptions { PO.AlwaysTryPrintParameterLabels = true; return PO; } + + /// An RAII scope for performing temporary adjustments to a PrintOptions + /// object. Even with the abstraction inherent in this design, this can + /// be significantly cheaper than copying the options just to modify a few + /// fields. + /// + /// At its core, this is just a stack of arbitrary functions to run + /// when the scope is destroyed. + class OverrideScope { + public: + /// The mutable options exposed by the scope. Generally, you should not + /// access this directly. + PrintOptions &Options; + + private: + /// A stack of finalizer functions, each of which generally undoes some + /// change that was made to the options. + SmallVector, 4> Finalizers; + + public: + OverrideScope(const PrintOptions &options) + : Options(const_cast(options)) {} + + // Disallow all copies and moves. + OverrideScope(const OverrideScope &scope) = delete; + OverrideScope &operator=(const OverrideScope &scope) = delete; + + ~OverrideScope() { + // Run the finalizers in the opposite order that they were added. + for (auto &finalizer : llvm::reverse(Finalizers)) { + finalizer(Options); + } + } + + template + void addFinalizer(Fn &&fn) { + Finalizers.emplace_back(std::move(fn)); + } + + void addExcludedAttr(AnyAttrKind kind) { + Options.ExcludeAttrList.push_back(kind); + addFinalizer([](PrintOptions &options) { + options.ExcludeAttrList.pop_back(); + }); + } + }; }; -} + +/// Override a print option within an OverrideScope. Does a check to see if +/// the new value is the same as the old before actually doing anything, so +/// it only works if the type provides ==. +/// +/// Signature is: +/// void (OverrideScope &scope, , T &&newValue) +#define OVERRIDE_PRINT_OPTION(SCOPE, FIELD_NAME, VALUE) \ + do { \ + auto _newValue = (VALUE); \ + if ((SCOPE).Options.FIELD_NAME != _newValue) { \ + auto finalizer = \ + [_oldValue=(SCOPE).Options.FIELD_NAME](PrintOptions &opts) { \ + opts.FIELD_NAME = _oldValue; \ + }; \ + (SCOPE).Options.FIELD_NAME = std::move(_newValue); \ + (SCOPE).addFinalizer(std::move(finalizer)); \ + } \ + } while(0) + +/// Override a print option within an OverrideScope. Works for any type. +/// +/// Signature is: +/// void (OverrideScope &scope, , T &&newValue) +#define OVERRIDE_PRINT_OPTION_UNCONDITIONAL(SCOPE, FIELD_NAME, VALUE) \ + do { \ + auto finalizer = \ + [_oldValue=(SCOPE).Options.FIELD_NAME](PrintOptions &opts) { \ + opts.FIELD_NAME = _oldValue; \ + }; \ + (SCOPE).Options.FIELD_NAME = (VALUE); \ + (SCOPE).addFinalizer(std::move(finalizer)); \ + } while(0) + +} // end namespace swift #endif // LLVM_SWIFT_AST_PRINTOPTIONS_H diff --git a/include/swift/AST/ProtocolAssociations.h b/include/swift/AST/ProtocolAssociations.h index 968dcb1f8bff5..c9589a367ef20 100644 --- a/include/swift/AST/ProtocolAssociations.h +++ b/include/swift/AST/ProtocolAssociations.h @@ -23,51 +23,6 @@ namespace swift { -/// A type associated with a protocol. -/// -/// This struct exists largely so that we can maybe eventually -/// generalize it to an arbitrary path. -class AssociatedType { - AssociatedTypeDecl *Association; - using AssociationInfo = llvm::DenseMapInfo; - - struct SpecialValue {}; - explicit AssociatedType(SpecialValue _, AssociatedTypeDecl *specialValue) - : Association(specialValue) {} - -public: - explicit AssociatedType(AssociatedTypeDecl *association) - : Association(association) { - assert(association); - } - - ProtocolDecl *getSourceProtocol() const { - return Association->getProtocol(); - } - - AssociatedTypeDecl *getAssociation() const { - return Association; - } - - friend bool operator==(AssociatedType lhs, AssociatedType rhs) { - return lhs.Association == rhs.Association; - } - friend bool operator!=(AssociatedType lhs, AssociatedType rhs) { - return !(lhs == rhs); - } - - unsigned getHashValue() const { - return llvm::hash_value(Association); - } - - static AssociatedType getEmptyKey() { - return AssociatedType(SpecialValue(), AssociationInfo::getEmptyKey()); - } - static AssociatedType getTombstoneKey() { - return AssociatedType(SpecialValue(), AssociationInfo::getTombstoneKey()); - } -}; - /// A base conformance of a protocol. class BaseConformance { ProtocolDecl *Source; @@ -147,24 +102,6 @@ class AssociatedConformance { } // end namespace swift namespace llvm { - template <> struct DenseMapInfo { - static inline swift::AssociatedType getEmptyKey() { - return swift::AssociatedType::getEmptyKey(); - } - - static inline swift::AssociatedType getTombstoneKey() { - return swift::AssociatedType::getTombstoneKey(); - } - - static unsigned getHashValue(swift::AssociatedType val) { - return val.getHashValue(); - } - - static bool isEqual(swift::AssociatedType lhs, swift::AssociatedType rhs) { - return lhs == rhs; - } - }; - template <> struct DenseMapInfo { static inline swift::AssociatedConformance getEmptyKey() { return swift::AssociatedConformance::getEmptyKey(); diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index a3981486bc1a9..70d6a71bbe91f 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -131,6 +131,9 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance /// conformance definition. Type ConformingType; + friend class ConformanceIsolationRequest; + friend class RawConformanceIsolationRequest; + protected: // clang-format off // @@ -139,9 +142,16 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(ProtocolConformance, + 1+1+ bitmax(NumProtocolConformanceKindBits, 8), /// The kind of protocol conformance. - Kind : bitmax(NumProtocolConformanceKindBits, 8) + Kind : bitmax(NumProtocolConformanceKindBits, 8), + + /// Whether the "raw" conformance isolation is "inferred", which applies to most conformances. + IsRawIsolationInferred : 1, + + /// Whether the computed actor isolation is nonisolated. + IsComputedNonisolated : 1 ); SWIFT_INLINE_BITFIELD_EMPTY(RootProtocolConformance, ProtocolConformance); @@ -161,9 +171,6 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance /// this conformance. IsPreconcurrencyEffectful : 1, - /// Whether the computed actor isolation is nonisolated. - IsComputedNonisolated : 1, - /// Whether there is an explicit global actor specified for this /// conformance. HasExplicitGlobalActor : 1, @@ -198,6 +205,24 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance ProtocolConformance(ProtocolConformanceKind kind, Type conformingType) : ConformingType(conformingType) { Bits.ProtocolConformance.Kind = unsigned(kind); + Bits.ProtocolConformance.IsRawIsolationInferred = false; + Bits.ProtocolConformance.IsComputedNonisolated = false; + } + + bool isRawIsolationInferred() const { + return Bits.ProtocolConformance.IsRawIsolationInferred; + } + + void setRawConformanceInferred(bool value = true) { + Bits.ProtocolConformance.IsRawIsolationInferred = value; + } + + bool isComputedNonisolated() const { + return Bits.ProtocolConformance.IsComputedNonisolated; + } + + void setComputedNonnisolated(bool value = true) { + Bits.ProtocolConformance.IsComputedNonisolated = value; } public: @@ -246,6 +271,14 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance /// Otherwise a new conformance will be created. ProtocolConformance *getCanonicalConformance(); + /// Determine the "raw" actor isolation of this conformance, before applying any inference rules. + /// + /// Most clients should use `getIsolation()`, unless they are part of isolation inference + /// themselves (e.g., conformance checking). + /// + /// - Returns std::nullopt if the isolation will be inferred. + std::optional getRawIsolation() const; + /// Determine the actor isolation of this conformance. ActorIsolation getIsolation() const; @@ -363,10 +396,6 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance /// Retrieve the protocol conformance for the inherited protocol. ProtocolConformance *getInheritedConformance(ProtocolDecl *protocol) const; - /// Given a dependent type expressed in terms of the self parameter, - /// map it into the context of this conformance. - Type getAssociatedType(Type assocType) const; - /// 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. @@ -544,6 +573,7 @@ class NormalProtocolConformance : public RootProtocolConformance, friend class ValueWitnessRequest; friend class TypeWitnessRequest; friend class ConformanceIsolationRequest; + friend class RawConformanceIsolationRequest; /// The protocol being conformed to. ProtocolDecl *Protocol; @@ -552,10 +582,17 @@ class NormalProtocolConformance : public RootProtocolConformance, SourceLoc Loc; /// The location of the protocol name within the conformance. + /// + /// - Important: This is not a valid insertion location for an attribute. + /// Use `applyConformanceAttribute` instead. SourceLoc ProtocolNameLoc; - /// The location of the `@preconcurrency` attribute, if any. - SourceLoc PreconcurrencyLoc; + /// The `TypeRepr` of the inheritance clause entry that declares this + /// conformance, if any. For example, if this is a conformance to `Y` + /// declared as `struct S: X, Y & Z {}`, this is the `TypeRepr` for `Y & Z`. + /// + /// - Important: The value can be valid only for an explicit conformance. + TypeRepr *inheritedTypeRepr; /// The declaration context containing the ExtensionDecl or /// NominalTypeDecl that declared the conformance. @@ -591,30 +628,26 @@ class NormalProtocolConformance : public RootProtocolConformance, // Record the explicitly-specified global actor isolation. void setExplicitGlobalActorIsolation(TypeExpr *typeExpr); - bool isComputedNonisolated() const { - return Bits.NormalProtocolConformance.IsComputedNonisolated; - } - - void setComputedNonnisolated(bool value = true) { - Bits.NormalProtocolConformance.IsComputedNonisolated = value; - } + /// Return the `TypeRepr` of the inheritance clause entry that declares this + /// conformance, if any. For example, if this is a conformance to `Y` + /// declared as `struct S: X, Y & Z {}`, this is the `TypeRepr` for `Y & Z`. + /// + /// - Important: The value can be valid only for an explicit conformance. + TypeRepr *getInheritedTypeRepr() const { return inheritedTypeRepr; } public: NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol, - SourceLoc loc, DeclContext *dc, - ProtocolConformanceState state, - ProtocolConformanceOptions options, - SourceLoc preconcurrencyLoc) + SourceLoc loc, TypeRepr *inheritedTypeRepr, + DeclContext *dc, ProtocolConformanceState state, + ProtocolConformanceOptions options) : RootProtocolConformance(ProtocolConformanceKind::Normal, conformingType), Protocol(protocol), Loc(extractNearestSourceLoc(dc)), - ProtocolNameLoc(loc), PreconcurrencyLoc(preconcurrencyLoc), + ProtocolNameLoc(loc), inheritedTypeRepr(inheritedTypeRepr), Context(dc) { assert(!conformingType->hasArchetype() && "ProtocolConformances should store interface types"); - assert((preconcurrencyLoc.isInvalid() || - options.contains(ProtocolConformanceFlags::Preconcurrency)) && - "Cannot have a @preconcurrency location without isPreconcurrency"); + setState(state); Bits.NormalProtocolConformance.IsInvalid = false; Bits.NormalProtocolConformance.IsPreconcurrencyEffectful = false; @@ -622,9 +655,11 @@ class NormalProtocolConformance : public RootProtocolConformance, Bits.NormalProtocolConformance.HasComputedAssociatedConformances = false; Bits.NormalProtocolConformance.SourceKind = unsigned(ConformanceEntryKind::Explicit); - Bits.NormalProtocolConformance.IsComputedNonisolated = false; Bits.NormalProtocolConformance.HasExplicitGlobalActor = false; setExplicitGlobalActorIsolation(options.getGlobalActorIsolationType()); + + assert((!getPreconcurrencyLoc() || isPreconcurrency()) && + "Cannot have a @preconcurrency location without isPreconcurrency"); } /// Get the protocol being conformed to. @@ -634,6 +669,9 @@ class NormalProtocolConformance : public RootProtocolConformance, SourceLoc getLoc() const { return Loc; } /// Retrieve the name of the protocol location. + /// + /// - Important: This is not a valid insertion location for an attribute. + /// Use `applyConformanceAttribute` instead. SourceLoc getProtocolNameLoc() const { return ProtocolNameLoc; } /// Get the declaration context that contains the conforming extension or @@ -704,7 +742,9 @@ class NormalProtocolConformance : public RootProtocolConformance, /// Retrieve the location of `@preconcurrency`, if there is one and it is /// known. - SourceLoc getPreconcurrencyLoc() const { return PreconcurrencyLoc; } + /// + /// - Important: The value can be valid only for an explicit conformance. + SourceLoc getPreconcurrencyLoc() const; /// Query whether this conformance was explicitly declared to be safe or /// unsafe. @@ -714,6 +754,9 @@ class NormalProtocolConformance : public RootProtocolConformance, return ExplicitSafety::Unspecified; } + /// Whether this conformance has explicitly-specified global actor isolation. + bool hasExplicitGlobalActorIsolation() const; + /// Determine whether we've lazily computed the associated conformance array /// already. bool hasComputedAssociatedConformances() const { @@ -825,6 +868,15 @@ class NormalProtocolConformance : public RootProtocolConformance, /// Triggers a request that resolves all of the conformance's value witnesses. void resolveValueWitnesses() const; + /// If the necessary source location information is found, attaches a fix-it + /// to the given diagnostic for applying the given attribute to the + /// conformance. + /// + /// \param attrStr A conformance attribute as a string, e.g. "@unsafe" or + /// "nonisolated". + void applyConformanceAttribute(InFlightDiagnostic &diag, + std::string attrStr) const; + /// Determine whether the witness for the given type requirement /// is the default definition. bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const { @@ -916,9 +968,7 @@ class SelfProtocolConformance : public RootProtocolConformance { } ProtocolConformanceRef getAssociatedConformance(Type assocType, - ProtocolDecl *protocol) const{ - llvm_unreachable("self-conformances never have associated types"); - } + ProtocolDecl *protocol) const; bool hasWitness(ValueDecl *requirement) const { return true; diff --git a/include/swift/AST/ProtocolConformanceRef.h b/include/swift/AST/ProtocolConformanceRef.h index 5267e03c9d8d0..f7fde1e87b8a1 100644 --- a/include/swift/AST/ProtocolConformanceRef.h +++ b/include/swift/AST/ProtocolConformanceRef.h @@ -120,27 +120,26 @@ class ProtocolConformanceRef { ProtocolDecl *protocol); bool isConcrete() const { - return !isInvalid() && Union.is(); + return !isInvalid() && isa(Union); } ProtocolConformance *getConcrete() const { ASSERT(isConcrete()); - return Union.get(); + return cast(Union); } - bool isPack() const { - return !isInvalid() && Union.is(); - } + bool isPack() const { return !isInvalid() && isa(Union); } PackConformance *getPack() const { ASSERT(isPack()); - return Union.get(); + return cast(Union); } bool isAbstract() const { - return !isInvalid() && Union.is(); + return !isInvalid() && isa(Union); } AbstractConformance *getAbstract() const { - return Union.get(); + ASSERT(isAbstract()); + return cast(Union); } /// Determine whether this conformance (or a conformance it depends on) @@ -185,11 +184,11 @@ class ProtocolConformanceRef { ProtocolDecl *getProtocol() const; /// Apply a substitution to the conforming type. - ProtocolConformanceRef subst(Type origType, SubstitutionMap subMap, + ProtocolConformanceRef subst(SubstitutionMap subMap, SubstOptions options = std::nullopt) const; /// Apply a substitution to the conforming type. - ProtocolConformanceRef subst(Type origType, TypeSubstitutionFn subs, + ProtocolConformanceRef subst(TypeSubstitutionFn subs, LookupConformanceFn conformances, SubstOptions options = std::nullopt) const; @@ -197,26 +196,24 @@ class ProtocolConformanceRef { /// /// This function should generally not be used outside of the substitution /// subsystem. - ProtocolConformanceRef subst(Type origType, - InFlightSubstitution &IFS) const; + ProtocolConformanceRef subst(InFlightSubstitution &IFS) const; /// Map contextual types to interface types in the conformance. ProtocolConformanceRef mapConformanceOutOfContext() const; /// Look up the type witness for an associated type declaration in this /// conformance. - Type getTypeWitness(Type origType, AssociatedTypeDecl *assocType, + Type getTypeWitness(AssociatedTypeDecl *assocType, SubstOptions options = std::nullopt) const; /// Given a dependent type (expressed in terms of this conformance's /// protocol), follow it from the conforming type. - Type getAssociatedType(Type origType, Type dependentType) const; + Type getAssociatedType(Type dependentType) const; /// Given a dependent type (expressed in terms of this conformance's /// protocol) and conformance, follow it from the conforming type. ProtocolConformanceRef - getAssociatedConformance(Type origType, Type dependentType, - ProtocolDecl *requirement) const; + getAssociatedConformance(Type dependentType, ProtocolDecl *requirement) const; SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &out, unsigned indent = 0, @@ -235,7 +232,7 @@ class ProtocolConformanceRef { return llvm::hash_value(conformance.Union.getOpaqueValue()); } - Type getTypeWitnessByName(Type type, Identifier name) const; + Type getTypeWitnessByName(Identifier name) const; /// Find a particular named function witness for a type that conforms to /// the given protocol. @@ -243,7 +240,7 @@ class ProtocolConformanceRef { /// \param type The conforming type. /// /// \param name The name of the requirement. - ConcreteDeclRef getWitnessByName(Type type, DeclName name) const; + ConcreteDeclRef getWitnessByName(DeclName name) const; /// Determine whether this conformance is canonical. bool isCanonical() const; diff --git a/include/swift/AST/RequirementEnvironment.h b/include/swift/AST/RequirementEnvironment.h index a0ce72a59ca94..282e7e4da26b8 100644 --- a/include/swift/AST/RequirementEnvironment.h +++ b/include/swift/AST/RequirementEnvironment.h @@ -114,7 +114,8 @@ class RequirementEnvironment { } /// Retrieve the substitution map that maps the interface types of the - /// requirement to the interface types of the witness thunk signature. + /// requirement to the archetypes of the witness thunk signature's + /// generic environment. SubstitutionMap getRequirementToWitnessThunkSubs() const { return reqToWitnessThunkSigMap; } diff --git a/include/swift/AST/RequirementMatch.h b/include/swift/AST/RequirementMatch.h index 1130be03d9502..31326d6a72e2a 100644 --- a/include/swift/AST/RequirementMatch.h +++ b/include/swift/AST/RequirementMatch.h @@ -12,6 +12,7 @@ #ifndef SWIFT_AST_REQUIREMENTMATCH_H #define SWIFT_AST_REQUIREMENTMATCH_H +#include "swift/AST/AvailabilityConstraint.h" #include "swift/AST/RequirementEnvironment.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" @@ -217,10 +218,6 @@ enum class CheckKind : unsigned { /// The witness is less accessible than the requirement. Access, - /// The witness is storage whose setter is less accessible than the - /// requirement. - AccessOfSetter, - /// The witness needs to be @usableFromInline. UsableFromInline, @@ -228,7 +225,7 @@ enum class CheckKind : unsigned { Availability, /// The requirement was marked explicitly unavailable. - Unavailable, + RequirementUnavailable, /// The witness requires optional adjustments. OptionalityConflict, @@ -237,39 +234,81 @@ enum class CheckKind : unsigned { /// requirement. ConstructorFailability, - /// The witness itself is inaccessible. - WitnessUnavailable, - /// The witness is a deprecated default implementation provided by the /// protocol. DefaultWitnessDeprecated, }; + /// Describes the suitability of the chosen witness for /// the requirement. -struct RequirementCheck { +class RequirementCheck { CheckKind Kind; - /// The required access scope, if the check failed due to the - /// witness being less accessible than the requirement. - AccessScope RequiredAccessScope; + union { + /// Storage for `CheckKind::Access`. + struct { + AccessScope requiredScope; + bool forSetter; + } Access; + + /// Storage for `CheckKind::Availability`. + struct { + AvailabilityConstraint constraint; + AvailabilityContext requiredContext; + } Availability; + }; - /// The required availability, if the check failed due to the - /// witness being less available than the requirement. - AvailabilityRange RequiredAvailability; +public: + RequirementCheck(CheckKind kind) : Kind(kind) { + // These kinds have their own constructors. + ASSERT(kind != CheckKind::Access); + ASSERT(kind != CheckKind::Availability); + } - RequirementCheck(CheckKind kind) - : Kind(kind), RequiredAccessScope(AccessScope::getPublic()), - RequiredAvailability(AvailabilityRange::alwaysAvailable()) {} + RequirementCheck(AccessScope requiredAccessScope, bool forSetter) + : Kind(CheckKind::Access), Access{requiredAccessScope, forSetter} {} - RequirementCheck(CheckKind kind, AccessScope requiredAccessScope) - : Kind(kind), RequiredAccessScope(requiredAccessScope), - RequiredAvailability(AvailabilityRange::alwaysAvailable()) {} + RequirementCheck(AvailabilityConstraint constraint, + AvailabilityContext requiredContext) + : Kind(CheckKind::Availability), + Availability{constraint, requiredContext} {} - RequirementCheck(CheckKind kind, AvailabilityRange requiredAvailability) - : Kind(kind), RequiredAccessScope(AccessScope::getPublic()), - RequiredAvailability(requiredAvailability) {} -}; + CheckKind getKind() const { return Kind; } + /// True if the witness is storage whose setter is less accessible than the + /// requirement. + bool isForSetterAccess() const { + return (Kind == CheckKind::Access) ? Access.forSetter : false; + } + + /// True if the witness is less available than the requirement. + bool isLessAvailable() const { + return (Kind == CheckKind::Availability) + ? !Availability.constraint.isUnavailable() + : false; + } + + /// The required access scope for checks that failed due to the witness being + /// less accessible than the requirement. + AccessScope getRequiredAccessScope() const { + ASSERT(Kind == CheckKind::Access); + return Access.requiredScope; + } + + /// The availability constraint that would fail if the witness were accessed + /// from contexts in which the requirement is available. + AvailabilityConstraint getAvailabilityConstraint() const { + ASSERT(Kind == CheckKind::Availability); + return Availability.constraint; + } + + /// The required availability range for checks that failed due to the witness + /// being less available than the requirement. + AvailabilityContext getRequiredAvailabilityContext() const { + ASSERT(Kind == CheckKind::Availability); + return Availability.requiredContext; + } +}; /// Describes a match between a requirement and a witness. struct RequirementMatch { diff --git a/include/swift/AST/RuntimeVersions.def b/include/swift/AST/RuntimeVersions.def index 6a5c8918f8160..39d0a279c6f28 100644 --- a/include/swift/AST/RuntimeVersions.def +++ b/include/swift/AST/RuntimeVersions.def @@ -152,11 +152,22 @@ RUNTIME_VERSION( RUNTIME_VERSION( (6, 1), - FUTURE + PLATFORM(macOS, (15, 4, 0)) + PLATFORM(iOS, (18, 4, 0)) + PLATFORM(watchOS, (11, 4, 0)) + PLATFORM(visionOS,(2, 4, 0)) ) RUNTIME_VERSION( (6, 2), + PLATFORM(macOS, (26, 0, 0)) + PLATFORM(iOS, (26, 0, 0)) + PLATFORM(watchOS, (26, 0, 0)) + PLATFORM(visionOS,(26, 0, 0)) +) + +RUNTIME_VERSION( + (6, 3), FUTURE ) diff --git a/include/swift/AST/SILLayout.h b/include/swift/AST/SILLayout.h index 1b5267bd7b33d..63c486e579263 100644 --- a/include/swift/AST/SILLayout.h +++ b/include/swift/AST/SILLayout.h @@ -141,10 +141,8 @@ class SILLayout final : public llvm::FoldingSetNode, } /// Get the fields inside the layout. - ArrayRef getFields() const { - return llvm::ArrayRef(getTrailingObjects(), NumFields); - } - + ArrayRef getFields() const { return getTrailingObjects(NumFields); } + /// Produce a profile of this layout, for use in a folding set. static void Profile(llvm::FoldingSetNodeID &id, CanGenericSignature Generics, diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index 2c18a09d21823..da8e88772a59d 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -45,14 +45,17 @@ enum class CopyPropagationOption : uint8_t { // Do not add any copy propagation passes. Off = 0, - // Only add the copy propagation passes requested by other flags, currently - // just -enable-ossa-modules. + // Only add the copy propagation passes requested by other flags. RequestedPassesOnly, - // Add all relevant copy propagation passes. If a setting, e.g. - // -enable-ossa-modules, requests to add copy propagation to the pipeline, do - // so. - On + // Run copy propagation during optimized builds only. + // + // If a setting, requests to add copy propagation + // to the performance pipeline, do so. + Optimizing, + + // Run copy propagation during all builds and whenever requested. + Always }; enum class DestroyHoistingOption : uint8_t { @@ -90,11 +93,8 @@ class SILOptions { /// observable end of lexical scope. LexicalLifetimesOption LexicalLifetimes = LexicalLifetimesOption::On; - /// Whether to run SIL copy propagation to shorten object lifetime in whatever - /// optimization pipeline is currently used. - /// - /// When this is 'On' the pipeline has default behavior. - CopyPropagationOption CopyPropagation = CopyPropagationOption::On; + /// Controls when to run SIL copy propagation, which shortens object lifetimes + CopyPropagationOption CopyPropagation = CopyPropagationOption::Optimizing; /// Whether to run the DestroyAddrHoisting pass. /// @@ -188,11 +188,6 @@ class SILOptions { /// and go from OSSA to non-ownership SIL. bool StopOptimizationBeforeLoweringOwnership = false; - /// Do we always serialize SIL in OSSA form? - /// - /// If this is disabled we do not serialize in OSSA form when optimizing. - bool EnableOSSAModules = true; - /// Allow recompilation of a non-OSSA module to an OSSA module when imported /// from another OSSA module. bool EnableRecompilationToOSSAModule = false; @@ -308,14 +303,14 @@ class SILOptions { /// Are we building in embedded Swift + -no-allocations? bool NoAllocations = false; - /// Should we use the experimental Swift based closure-specialization - /// optimization pass instead of the existing C++ one. - bool EnableExperimentalSwiftBasedClosureSpecialization = false; - /// The name of the file to which the backend should save optimization /// records. std::string OptRecordFile; + /// The names of the auxiliar files to which the backend should save optimization + /// records for the remaining (other than the main one) LLVMModules. + std::vector AuxOptRecordFiles; + /// The regex that filters the passes that should be saved to the optimization /// records. std::string OptRecordPasses; @@ -323,6 +318,9 @@ class SILOptions { /// The format used for serializing remarks (default: YAML) llvm::remarks::Format OptRecordFormat = llvm::remarks::Format::YAML; + /// Whether to apply _assemblyVision to all functions. + bool EnableGlobalAssemblyVision = false; + /// Are there any options that indicate that functions should not be preserved /// for the debugger? bool ShouldFunctionsBePreservedToDebugger = true; @@ -338,6 +336,10 @@ class SILOptions { // Whether to allow merging traps and cond_fails. bool MergeableTraps = false; + /// Whether the @yield_once_2 convention is used by accessors added with the + /// CoroutineAccessors feature (i.e. read2/modify2). + bool CoroutineAccessorsUseYieldOnce2 = false; + SILOptions() {} /// Return a hash code of any components from these options that should diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index ff697f5ef49b4..f8101fe93db68 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -35,7 +35,7 @@ namespace swift { enum class ModuleSearchPathKind { Import, Framework, - DarwinImplicitFramework, + ImplicitFramework, RuntimeLibrary, }; @@ -323,6 +323,10 @@ class SearchPathOptions { friend bool operator!=(const SearchPath &LHS, const SearchPath &RHS) { return !(LHS == RHS); } + friend llvm::hash_code + hash_value(const SearchPath &searchPath) { + return llvm::hash_combine(searchPath.Path, searchPath.IsSystem); + } }; private: @@ -352,12 +356,8 @@ class SearchPathOptions { /// When on Darwin the framework paths that are implicitly imported. /// $SDKROOT/System/Library/Frameworks/ and $SDKROOT/Library/Frameworks/. /// - /// On non-Darwin platforms these are populated, but ignored. - /// - /// Computed when the SDK path is set and cached so we can reference the - /// Darwin implicit framework search paths as \c StringRef from - /// \c ModuleSearchPath. - std::vector DarwinImplicitFrameworkSearchPaths; + /// Must be modified through setter to keep \c Lookup in sync. + std::vector ImplicitFrameworkSearchPaths; /// Compiler plugin library search paths. std::vector CompilerPluginLibraryPaths; @@ -397,21 +397,6 @@ class SearchPathOptions { void setSDKPath(std::string NewSDKPath) { SDKPath = NewSDKPath; - - // Compute Darwin implicit framework search paths. - SmallString<128> systemFrameworksScratch(NewSDKPath); - llvm::sys::path::append(systemFrameworksScratch, "System", "Library", - "Frameworks"); - SmallString<128> systemSubFrameworksScratch(NewSDKPath); - llvm::sys::path::append(systemSubFrameworksScratch, "System", "Library", - "SubFrameworks"); - SmallString<128> frameworksScratch(NewSDKPath); - llvm::sys::path::append(frameworksScratch, "Library", "Frameworks"); - DarwinImplicitFrameworkSearchPaths = {systemFrameworksScratch.str().str(), - systemSubFrameworksScratch.str().str(), - frameworksScratch.str().str()}; - - Lookup.searchPathsDidChange(); } /// Retrieves the corresponding parent platform path for the SDK, or @@ -466,8 +451,14 @@ class SearchPathOptions { /// The extra implicit framework search paths on Apple platforms: /// $SDKROOT/System/Library/Frameworks/ and $SDKROOT/Library/Frameworks/. - ArrayRef getDarwinImplicitFrameworkSearchPaths() const { - return DarwinImplicitFrameworkSearchPaths; + ArrayRef getImplicitFrameworkSearchPaths() const { + return ImplicitFrameworkSearchPaths; + } + + void setImplicitFrameworkSearchPaths( + std::vector NewImplicitFrameworkSearchPaths) { + ImplicitFrameworkSearchPaths = NewImplicitFrameworkSearchPaths; + Lookup.searchPathsDidChange(); } ArrayRef getRuntimeLibraryImportPaths() const { @@ -501,14 +492,17 @@ class SearchPathOptions { /// Path to in-process plugin server shared library. std::string InProcessPluginServerPath; - /// Don't look in for compiler-provided modules. - bool SkipRuntimeLibraryImportPaths = false; + /// Don't automatically add any import paths. + bool SkipAllImplicitImportPaths = false; - /// Don't include SDK paths in the RuntimeLibraryImportPaths - bool ExcludeSDKPathsFromRuntimeLibraryImportPaths = false; + /// Don't automatically add any import paths from the SDK. + bool SkipSDKImportPaths = false; /// Scanner Prefix Mapper. - std::vector ScannerPrefixMapper; + std::vector> ScannerPrefixMapper; + + /// Verify resolved plugin is not changed. + bool ResolvedPluginVerification = false; /// When set, don't validate module system dependencies. /// @@ -524,11 +518,8 @@ class SearchPathOptions { std::string ExplicitSwiftModuleMapPath; /// Module inputs specified with -swift-module-input, - /// - std::vector> ExplicitSwiftModuleInputs; - - /// A map of placeholder Swift module dependency information. - std::string PlaceholderDependencyModuleMap; + /// ModuleName: Path to .swiftmodule file + llvm::StringMap ExplicitSwiftModuleInputs; /// A file containing a list of protocols whose conformances require const value extraction. std::string ConstGatherProtocolListFilePath; @@ -596,35 +587,24 @@ class SearchPathOptions { makeOverlayFileSystem( llvm::IntrusiveRefCntPtr BaseFS) const; -private: - static StringRef pathStringFromSearchPath(const SearchPath &next) { - return next.Path; - }; - public: /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { using llvm::hash_combine; using llvm::hash_combine_range; - - using SearchPathView = - ArrayRefView; - SearchPathView importPathsOnly{ImportSearchPaths}; - SearchPathView frameworkPathsOnly{FrameworkSearchPaths}; - return hash_combine(SDKPath, - // FIXME: Should we include the system-ness of - // search paths too? - hash_combine_range(importPathsOnly.begin(), importPathsOnly.end()), + hash_combine_range(ImportSearchPaths.begin(), ImportSearchPaths.end()), hash_combine_range(VFSOverlayFiles.begin(), VFSOverlayFiles.end()), - hash_combine_range(frameworkPathsOnly.begin(), - frameworkPathsOnly.end()), + hash_combine_range(FrameworkSearchPaths.begin(), + FrameworkSearchPaths.end()), hash_combine_range(LibrarySearchPaths.begin(), LibrarySearchPaths.end()), RuntimeResourcePath, hash_combine_range(RuntimeLibraryImportPaths.begin(), RuntimeLibraryImportPaths.end()), + hash_combine_range(ImplicitFrameworkSearchPaths.begin(), + ImplicitFrameworkSearchPaths.end()), DisableModulesValidateSystemDependencies, ScannerModuleValidation, ModuleLoadMode); diff --git a/include/swift/AST/SemanticAttrs.def b/include/swift/AST/SemanticAttrs.def index f2b93fb575ffe..98ada5ecb7a2e 100644 --- a/include/swift/AST/SemanticAttrs.def +++ b/include/swift/AST/SemanticAttrs.def @@ -73,6 +73,8 @@ SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_PARTIAL_NEVER, "optimize.sil.specialize.generic.partial.never") SEMANTICS_ATTR(OPTIMIZE_SIL_INLINE_CONSTANT_ARGUMENTS, "optimize.sil.inline.constant.arguments") +SEMANTICS_ATTR(DERIVED_ENUM_EQUALS, + "derived_enum_equals") SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_SIZE_NEVER, "optimize.sil.specialize.generic.size.never") SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_OWNED2GUARANTEE_NEVER, @@ -165,5 +167,7 @@ SEMANTICS_ATTR(USE_FRAME_POINTER, "use_frame_pointer") SEMANTICS_ATTR(FIXED_STORAGE_CHECK_INDEX, "fixed_storage.check_index") SEMANTICS_ATTR(FIXED_STORAGE_GET_COUNT, "fixed_storage.get_count") +SEMANTICS_ATTR(NO_SIL_VERIFICATION, "sil.verify_none") + #undef SEMANTICS_ATTR diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index b154007d7e864..a4f663c2eea99 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -28,9 +28,11 @@ namespace swift { class ASTScope; class AvailabilityScope; +class GeneratedSourceInfo; class PersistentParserState; struct SourceFileExtras; class Token; +enum class DefaultIsolation : uint8_t; /// Kind of import affecting how a decl can be reexported. /// @@ -53,15 +55,6 @@ enum class RestrictedImportKind { /// Import that limits the access level of imported entities. using ImportAccessLevel = std::optional>; -/// Language options only for use with a specific SourceFile. -/// -/// Vended by SourceFile::getLanguageOptions(). -struct SourceFileLangOptions { - /// If unset, no value was provided. If a Type, that type is the type of the - /// isolation. If set to an empty type, nil was specified explicitly. - std::optional defaultIsolation; -}; - /// A file containing Swift source code. /// /// This is a .swift or .sil file (or a virtual file, such as the contents of @@ -144,8 +137,8 @@ class SourceFile final : public FileUnit { /// Associates a list of source locations to the member declarations that must /// be diagnosed as being out of scope due to a missing import. - using DelayedMissingImportForMemberDiags = - llvm::SmallDenseMap>; + using DelayedMissingImportForMemberDiags = llvm::SmallDenseMap< + const ValueDecl *, std::vector>>; DelayedMissingImportForMemberDiags MissingImportForMemberDiagnostics; /// A unique identifier representing this file; used to mark private decls @@ -478,6 +471,11 @@ class SourceFile final : public FileUnit { const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const override; + /// Returns true if any import of \p importedModule has the `@preconcurrency` + /// attribute. + virtual bool isModuleImportedPreconcurrency( + const ModuleDecl *importedModule) const override; + // Is \p targetDecl accessible as an explicitly imported SPI from this file? bool isImportedAsSPI(const ValueDecl *targetDecl) const; @@ -525,8 +523,9 @@ class SourceFile final : public FileUnit { /// Add a source location for which a delayed missing import for member /// diagnostic should be emited. void addDelayedMissingImportForMemberDiagnostic(const ValueDecl *decl, - SourceLoc loc) { - MissingImportForMemberDiagnostics[decl].push_back(loc); + SourceLoc loc, + DiagnosticBehavior limit) { + MissingImportForMemberDiagnostics[decl].push_back({loc, limit}); } DelayedMissingImportForMemberDiags @@ -571,9 +570,6 @@ class SourceFile final : public FileUnit { ObjCSelector selector, SmallVectorImpl &results) const override; - /// File level language options. - SourceFileLangOptions getLanguageOptions() const; - protected: virtual void lookupOperatorDirect(Identifier name, OperatorFixity fixity, @@ -701,6 +697,11 @@ class SourceFile final : public FileUnit { DelayedParserState = std::move(state); } + /// Retrieve default action isolation to be used for this source file. + /// It's determine based on on top-level `using <>` declaration + /// found in the file. + std::optional getDefaultIsolation() const; + SWIFT_DEBUG_DUMP; void dump(raw_ostream &os, diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 0682f73e5bcb1..50238fef5fc39 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -19,6 +19,7 @@ #include "swift/AST/ASTAllocated.h" #include "swift/AST/ASTNode.h" +#include "swift/AST/AvailabilityQuery.h" #include "swift/AST/AvailabilityRange.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/IfConfigClause.h" @@ -83,8 +84,9 @@ class alignas(8) Stmt : public ASTAllocated { NumElements : 32 ); - SWIFT_INLINE_BITFIELD_FULL(CaseStmt, Stmt, 32, + SWIFT_INLINE_BITFIELD_FULL(CaseStmt, Stmt, 16+32, : NumPadBits, + NumCaseBodyVars : 16, NumPatterns : 32 ); @@ -213,16 +215,20 @@ class BraceStmt final : public Stmt, /// The elements contained within the BraceStmt. MutableArrayRef getElements() { - return {getTrailingObjects(), static_cast(Bits.BraceStmt.NumElements)}; + return getTrailingObjects(static_cast(Bits.BraceStmt.NumElements)); } /// The elements contained within the BraceStmt (const version). ArrayRef getElements() const { - return {getTrailingObjects(), static_cast(Bits.BraceStmt.NumElements)}; + return getTrailingObjects(static_cast(Bits.BraceStmt.NumElements)); } ASTNode findAsyncNode(); + /// Whether the body contains an explicit `return` statement. This computation + /// is cached. + bool hasExplicitReturnStmt(ASTContext &ctx) const; + /// If this brace contains a single ASTNode, or a \c #if that has a single active /// element, returns it. This will always be the last element of the brace. /// Otherwise returns \c nullptr. @@ -331,10 +337,10 @@ class YieldStmt final SourceLoc getEndLoc() const; ArrayRef getYields() const { - return {getTrailingObjects(), static_cast(Bits.YieldStmt.NumYields)}; + return getTrailingObjects(static_cast(Bits.YieldStmt.NumYields)); } MutableArrayRef getMutableYields() { - return {getTrailingObjects(), static_cast(Bits.YieldStmt.NumYields)}; + return getTrailingObjects(static_cast(Bits.YieldStmt.NumYields)); } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Yield; } @@ -477,20 +483,11 @@ class alignas(8) PoundAvailableInfo final : SourceLoc LParenLoc; SourceLoc RParenLoc; - // The number of queries tail allocated after this object. + /// The number of queries tail allocated after this object. unsigned NumQueries; - - /// The version range when this query will return true. This value is - /// filled in by Sema. - std::optional AvailableRange; - - /// For zippered builds, this is the version range for the target variant - /// that must hold for the query to return true. For example, when - /// compiling with target x86_64-macosx10.15 and target-variant - /// x86_64-ios13.0 a query of #available(macOS 10.22, iOS 20.0, *) will - /// have a variant range of [20.0, +inf). - /// This is filled in by Sema. - std::optional VariantAvailableRange; + + /// The type-checked availability query information. + std::optional Query; struct { unsigned isInvalid : 1; @@ -504,12 +501,11 @@ class alignas(8) PoundAvailableInfo final : ArrayRef queries, SourceLoc RParenLoc, bool isUnavailability) : PoundLoc(PoundLoc), LParenLoc(LParenLoc), RParenLoc(RParenLoc), - NumQueries(queries.size()), AvailableRange(VersionRange::empty()), - VariantAvailableRange(VersionRange::empty()), Flags() { + NumQueries(queries.size()), Flags() { Flags.isInvalid = false; Flags.isUnavailability = isUnavailability; std::uninitialized_copy(queries.begin(), queries.end(), - getTrailingObjects()); + getTrailingObjects()); } public: @@ -523,7 +519,7 @@ class alignas(8) PoundAvailableInfo final : void setInvalid() { Flags.isInvalid = true; } ArrayRef getQueries() const { - return llvm::ArrayRef(getTrailingObjects(), NumQueries); + return getTrailingObjects(NumQueries); } /// Returns an iterator for the statement's type-checked availability specs. @@ -539,16 +535,11 @@ class alignas(8) PoundAvailableInfo final : SourceRange getSourceRange() const { return SourceRange(getStartLoc(), getEndLoc()); } - std::optional getAvailableRange() const { - return AvailableRange; + std::optional getAvailabilityQuery() const { + return Query; } - void setAvailableRange(const VersionRange &Range) { AvailableRange = Range; } - - std::optional getVariantAvailableRange() const { - return VariantAvailableRange; - } - void setVariantAvailableRange(const VersionRange &Range) { - VariantAvailableRange = Range; + void setAvailabilityQuery(const AvailabilityQuery &query) { + Query.emplace(query); } bool isUnavailability() const { return Flags.isUnavailability; } @@ -640,13 +631,13 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement { }; ConditionKind getKind() const { - if (Condition.is()) + if (isa(Condition)) return CK_Boolean; - if (Condition.is()) + if (isa(Condition)) return CK_PatternBinding; - if (Condition.is()) + if (isa(Condition)) return CK_Availability; - if (Condition.is()) + if (isa(Condition)) return CK_HasSymbol; return CK_Boolean; } @@ -656,7 +647,7 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement { Expr *getBoolean() const { assert(getKind() == CK_Boolean && "Not a condition"); - return Condition.get(); + return cast(Condition); } void setBoolean(Expr *E) { assert(getKind() == CK_Boolean && "Not a condition"); @@ -670,7 +661,7 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement { ConditionalPatternBindingInfo *getPatternBinding() const { assert(getKind() == CK_PatternBinding && "Not a pattern binding condition"); - return Condition.get(); + return cast(Condition); } SourceLoc getIntroducerLoc() const { @@ -704,7 +695,7 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement { // Availability Accessors PoundAvailableInfo *getAvailability() const { assert(getKind() == CK_Availability && "Not an #available condition"); - return Condition.get(); + return cast(Condition); } void setAvailability(PoundAvailableInfo *Info) { @@ -715,7 +706,7 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement { // #_hasSymbol Accessors PoundHasSymbolInfo *getHasSymbolInfo() const { assert(getKind() == CK_HasSymbol && "Not a #_hasSymbol condition"); - return Condition.get(); + return cast(Condition); } void setHasSymbolInfo(PoundHasSymbolInfo *Info) { @@ -729,6 +720,8 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement { /// RHS of the self condition references a var defined in a capture list. /// - If `requireLoadExpr` is `true`, additionally requires that the RHS of /// the self condition is a `LoadExpr`. + /// TODO: Remove `requireLoadExpr` after full-on of the ImmutableWeakCaptures + /// feature bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false, bool requireLoadExpr = false) const; @@ -839,8 +832,6 @@ class LabeledConditionalStmt : public LabeledStmt { /// or `let self = self` condition. /// - If `requiresCaptureListRef` is `true`, additionally requires that the /// RHS of the self condition references a var defined in a capture list. - /// - If `requireLoadExpr` is `true`, additionally requires that the RHS of - /// the self condition is a `LoadExpr`. bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false, bool requireLoadExpr = false) const; @@ -1224,8 +1215,8 @@ enum CaseParentKind { Switch, DoCatch }; /// class CaseStmt final : public Stmt, - private llvm::TrailingObjects { + private llvm::TrailingObjects { friend TrailingObjects; Stmt *ParentStmt = nullptr; @@ -1236,15 +1227,17 @@ class CaseStmt final llvm::PointerIntPair BodyAndHasFallthrough; - std::optional> CaseBodyVariables; - CaseStmt(CaseParentKind ParentKind, SourceLoc ItemIntroducerLoc, ArrayRef CaseLabelItems, SourceLoc UnknownAttrLoc, SourceLoc ItemTerminatorLoc, BraceStmt *Body, - std::optional> CaseBodyVariables, - std::optional Implicit, + ArrayRef CaseBodyVariables, std::optional Implicit, NullablePtr fallthroughStmt); + MutableArrayRef getCaseBodyVariablesBuffer() { + return {getTrailingObjects(), + static_cast(Bits.CaseStmt.NumCaseBodyVars)}; + } + public: /// Create a parsed 'case'/'default' for 'switch' statement. static CaseStmt * @@ -1258,13 +1251,17 @@ class CaseStmt final ArrayRef CaseLabelItems, BraceStmt *Body); + static CaseStmt * + createImplicit(ASTContext &ctx, CaseParentKind parentKind, + ArrayRef caseLabelItems, BraceStmt *body, + NullablePtr fallthroughStmt = nullptr); + static CaseStmt * create(ASTContext &C, CaseParentKind ParentKind, SourceLoc ItemIntroducerLoc, ArrayRef CaseLabelItems, SourceLoc UnknownAttrLoc, SourceLoc ItemTerminatorLoc, BraceStmt *Body, - std::optional> CaseBodyVariables, - std::optional Implicit = std::nullopt, - NullablePtr fallthroughStmt = nullptr); + ArrayRef CaseBodyVariables, std::optional Implicit, + NullablePtr fallthroughStmt); CaseParentKind getParentKind() const { return ParentKind; } @@ -1307,7 +1304,7 @@ class CaseStmt final void setBody(BraceStmt *body) { BodyAndHasFallthrough.setPointer(body); } /// True if the case block declares any patterns with local variable bindings. - bool hasBoundDecls() const { return CaseBodyVariables.has_value(); } + bool hasCaseBodyVariables() const { return !getCaseBodyVariables().empty(); } /// Get the source location of the 'case', 'default', or 'catch' of the first /// label. @@ -1359,38 +1356,8 @@ class CaseStmt final } /// Return an ArrayRef containing the case body variables of this CaseStmt. - /// - /// Asserts if case body variables was not explicitly initialized. In contexts - /// where one wants a non-asserting version, \see - /// getCaseBodyVariablesOrEmptyArray. ArrayRef getCaseBodyVariables() const { - ArrayRef a = *CaseBodyVariables; - return a; - } - - bool hasCaseBodyVariables() const { return CaseBodyVariables.has_value(); } - - /// Return an MutableArrayRef containing the case body variables of this - /// CaseStmt. - /// - /// Asserts if case body variables was not explicitly initialized. In contexts - /// where one wants a non-asserting version, \see - /// getCaseBodyVariablesOrEmptyArray. - MutableArrayRef getCaseBodyVariables() { - return *CaseBodyVariables; - } - - ArrayRef getCaseBodyVariablesOrEmptyArray() const { - if (!CaseBodyVariables) - return ArrayRef(); - ArrayRef a = *CaseBodyVariables; - return a; - } - - MutableArrayRef getCaseBodyVariablesOrEmptyArray() { - if (!CaseBodyVariables) - return MutableArrayRef(); - return *CaseBodyVariables; + return const_cast(this)->getCaseBodyVariablesBuffer(); } /// Find the next case statement within the same 'switch' or 'do-catch', @@ -1464,8 +1431,7 @@ class SwitchStmt final : public LabeledStmt, /// Get the list of case clauses. ArrayRef getCases() const { - return {getTrailingObjects(), - static_cast(Bits.SwitchStmt.CaseCount)}; + return getTrailingObjects(static_cast(Bits.SwitchStmt.CaseCount)); } /// Retrieve the complete set of branches for this switch statement. @@ -1505,7 +1471,7 @@ class DoCatchStmt final Body(body) { Bits.DoCatchStmt.NumCatches = catches.size(); std::uninitialized_copy(catches.begin(), catches.end(), - getTrailingObjects()); + getTrailingObjects()); for (auto *catchStmt : getCatches()) catchStmt->setParentStmt(this); } @@ -1539,10 +1505,10 @@ class DoCatchStmt final void setBody(Stmt *s) { Body = s; } ArrayRef getCatches() const { - return {getTrailingObjects(), static_cast(Bits.DoCatchStmt.NumCatches)}; + return getTrailingObjects(static_cast(Bits.DoCatchStmt.NumCatches)); } MutableArrayRef getMutableCatches() { - return {getTrailingObjects(), static_cast(Bits.DoCatchStmt.NumCatches)}; + return getTrailingObjects(static_cast(Bits.DoCatchStmt.NumCatches)); } /// Retrieve the complete set of branches for this do-catch statement. diff --git a/include/swift/AST/StorageImpl.h b/include/swift/AST/StorageImpl.h index 6f94f504207eb..64cf4f5b17a01 100644 --- a/include/swift/AST/StorageImpl.h +++ b/include/swift/AST/StorageImpl.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 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -18,6 +18,7 @@ #ifndef SWIFT_STORAGEIMPL_H #define SWIFT_STORAGEIMPL_H +#include "swift/AST/AccessorKind.h" #include "swift/Basic/Range.h" #include "llvm/ADT/StringRef.h" @@ -47,16 +48,6 @@ enum class OpaqueReadOwnership : uint8_t { OwnedOrBorrowed }; -// Note that the values of these enums line up with %select values in -// diagnostics. -enum class AccessorKind { -#define ACCESSOR(ID) ID, -#define LAST_ACCESSOR(ID) Last = ID -#include "swift/AST/AccessorKinds.def" -#undef ACCESSOR -#undef LAST_ACCESSOR -}; - inline bool requiresFeatureCoroutineAccessors(AccessorKind kind) { switch (kind) { case AccessorKind::Read2: @@ -71,6 +62,29 @@ inline bool requiresFeatureCoroutineAccessors(AccessorKind kind) { case AccessorKind::DidSet: case AccessorKind::Address: case AccessorKind::MutableAddress: + case AccessorKind::Init: + case AccessorKind::Borrow: + case AccessorKind::Mutate: + return false; + } +} + +inline bool requiresFeatureBorrowAndMutateAccessors(AccessorKind kind) { + switch (kind) { + case AccessorKind::Borrow: + case AccessorKind::Mutate: + return true; + case AccessorKind::Get: + case AccessorKind::DistributedGet: + case AccessorKind::Set: + case AccessorKind::Read: + case AccessorKind::Read2: + case AccessorKind::Modify: + case AccessorKind::Modify2: + case AccessorKind::WillSet: + case AccessorKind::DidSet: + case AccessorKind::Address: + case AccessorKind::MutableAddress: case AccessorKind::Init: return false; } @@ -91,6 +105,8 @@ inline bool isYieldingAccessor(AccessorKind kind) { case AccessorKind::Address: case AccessorKind::MutableAddress: case AccessorKind::Init: + case AccessorKind::Borrow: + case AccessorKind::Mutate: return false; } } @@ -110,6 +126,8 @@ inline bool isYieldingImmutableAccessor(AccessorKind kind) { case AccessorKind::Address: case AccessorKind::MutableAddress: case AccessorKind::Init: + case AccessorKind::Borrow: + case AccessorKind::Mutate: return false; } } @@ -129,6 +147,8 @@ inline bool isYieldingMutableAccessor(AccessorKind kind) { case AccessorKind::Address: case AccessorKind::MutableAddress: case AccessorKind::Init: + case AccessorKind::Borrow: + case AccessorKind::Mutate: return false; } } @@ -144,7 +164,7 @@ static inline IntRange allAccessorKinds() { static inline llvm::StringRef accessorKindName(AccessorKind ak) { switch(ak) { -#define ACCESSOR(ID) ID +#define ACCESSOR(ID, KEYWORD) ID #define SINGLETON_ACCESSOR(ID, KEYWORD) \ case AccessorKind::ID: \ return #KEYWORD; @@ -287,6 +307,9 @@ enum class ReadImplKind { /// There's a read coroutine. Read2, + + /// There's a borrow accessor. + Borrow, }; enum { NumReadImplKindBits = 4 }; @@ -316,6 +339,9 @@ enum class WriteImplKind { /// There's a modify coroutine. Modify2, + + /// There's a mutate accessor. + Mutate, }; enum { NumWriteImplKindBits = 4 }; @@ -344,6 +370,9 @@ enum class ReadWriteImplKind { // access pattern. StoredWithDidSet, InheritedWithDidSet, + + /// There's a mutate accessor. + Mutate, }; enum { NumReadWriteImplKindBits = 4 }; @@ -397,34 +426,45 @@ class StorageImplInfo { return; case WriteImplKind::Set: - assert(readImpl == ReadImplKind::Get || - readImpl == ReadImplKind::Address || - readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Read2); + assert( + readImpl == ReadImplKind::Get || readImpl == ReadImplKind::Address || + readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Read2 || + readImpl == ReadImplKind::Borrow); assert(readWriteImpl == ReadWriteImplKind::MaterializeToTemporary || readWriteImpl == ReadWriteImplKind::Modify || readWriteImpl == ReadWriteImplKind::Modify2); return; case WriteImplKind::Modify: - assert(readImpl == ReadImplKind::Get || - readImpl == ReadImplKind::Address || - readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Read2); + assert( + readImpl == ReadImplKind::Get || readImpl == ReadImplKind::Address || + readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Read2 || + readImpl == ReadImplKind::Borrow); assert(readWriteImpl == ReadWriteImplKind::Modify); return; case WriteImplKind::Modify2: - assert(readImpl == ReadImplKind::Get || - readImpl == ReadImplKind::Address || - readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Read2); + assert( + readImpl == ReadImplKind::Get || readImpl == ReadImplKind::Address || + readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Read2 || + readImpl == ReadImplKind::Borrow); assert(readWriteImpl == ReadWriteImplKind::Modify2); return; case WriteImplKind::MutableAddress: - assert(readImpl == ReadImplKind::Get || - readImpl == ReadImplKind::Address || - readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Read2); + assert( + readImpl == ReadImplKind::Get || readImpl == ReadImplKind::Address || + readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Read2 || + readImpl == ReadImplKind::Borrow); assert(readWriteImpl == ReadWriteImplKind::MutableAddress); return; + case WriteImplKind::Mutate: + assert( + readImpl == ReadImplKind::Get || readImpl == ReadImplKind::Address || + readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Read2 || + readImpl == ReadImplKind::Borrow); + assert(readWriteImpl == ReadWriteImplKind::Mutate); + return; } llvm_unreachable("bad write impl kind"); #endif diff --git a/include/swift/AST/SubstitutionMap.h b/include/swift/AST/SubstitutionMap.h index 961d87d87bcd7..868d59ef265b7 100644 --- a/include/swift/AST/SubstitutionMap.h +++ b/include/swift/AST/SubstitutionMap.h @@ -181,11 +181,9 @@ class SubstitutionMap { /// subsystem. SubstitutionMap subst(InFlightSubstitution &subs) const; - /// Apply type expansion lowering to all types in the substitution map. Opaque - /// archetypes will be lowered to their underlying types if the type expansion - /// context allows. - SubstitutionMap mapIntoTypeExpansionContext( - TypeExpansionContext context) const; + /// Create a substitution map for a protocol conformance. + static SubstitutionMap + getProtocolSubstitutions(ProtocolConformanceRef conformance); /// Create a substitution map for a protocol conformance. static SubstitutionMap @@ -219,7 +217,7 @@ class SubstitutionMap { /// Whether to dump the full substitution map, or just a minimal useful subset /// (on a single line). - enum class DumpStyle { Minimal, Full }; + enum class DumpStyle { Minimal, NoConformances, Full }; /// Dump the contents of this substitution map for debugging purposes. void dump(llvm::raw_ostream &out, DumpStyle style = DumpStyle::Full, unsigned indent = 0) const; @@ -296,13 +294,12 @@ class LookUpConformanceInSubstitutionMap { explicit LookUpConformanceInSubstitutionMap(SubstitutionMap Subs) : Subs(Subs) {} - ProtocolConformanceRef operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const; + ProtocolConformanceRef operator()(InFlightSubstitution &IFS, + Type dependentType, + ProtocolDecl *proto) const; }; struct OverrideSubsInfo { - ASTContext &Ctx; unsigned BaseDepth; unsigned OrigDepth; SubstitutionMap BaseSubMap; @@ -329,8 +326,8 @@ struct LookUpConformanceInOverrideSubs { explicit LookUpConformanceInOverrideSubs(const OverrideSubsInfo &info) : info(info) {} - ProtocolConformanceRef operator()(CanType type, - Type substType, + ProtocolConformanceRef operator()(InFlightSubstitution &IFS, + Type dependentType, ProtocolDecl *proto) const; }; @@ -340,11 +337,10 @@ struct OuterSubstitutions { SubstitutionMap subs; unsigned depth; - bool isUnsubstitutedTypeParameter(Type type) const; Type operator()(SubstitutableType *type) const; - ProtocolConformanceRef operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const; + ProtocolConformanceRef operator()(InFlightSubstitution &IFS, + Type dependentType, + ProtocolDecl *proto) const; }; } // end namespace swift diff --git a/include/swift/AST/SwiftNameTranslation.h b/include/swift/AST/SwiftNameTranslation.h index 3e3c880643197..1ae3f691fccd9 100644 --- a/include/swift/AST/SwiftNameTranslation.h +++ b/include/swift/AST/SwiftNameTranslation.h @@ -13,6 +13,7 @@ #ifndef SWIFT_NAME_TRANSLATION_H #define SWIFT_NAME_TRANSLATION_H +#include "swift/AST/ASTContext.h" #include "swift/AST/AttrKind.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" @@ -112,6 +113,9 @@ inline bool isExposableToCxx( return !getDeclRepresentation(VD, isZeroSized).isUnsupported(); } +bool isObjCxxOnly(const ValueDecl *VD); +bool isObjCxxOnly(const clang::Decl *D, const ASTContext &ctx); + /// Returns true if the given value decl D is visible to C++ of its /// own accord (i.e. without considering its context) bool isVisibleToCxx(const ValueDecl *VD, AccessLevel minRequiredAccess, diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 7591f14e8520e..8d6a849fb3010 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.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 - 2025 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 @@ -26,6 +26,7 @@ #include "swift/Basic/ArrayRefView.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/Debug.h" +#include "swift/Basic/InlineBitfield.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" #include "llvm/ADT/DenseMap.h" @@ -88,9 +89,9 @@ struct QueryTypeSubstitutionMap { }; /// Function used to resolve conformances. -using GenericFunction = auto(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) +using GenericFunction = auto(InFlightSubstitution &IFS, + Type dependentType, + ProtocolDecl *proto) -> ProtocolConformanceRef; using LookupConformanceFn = llvm::function_ref; @@ -100,19 +101,9 @@ class LookUpConformanceInModule { public: explicit LookUpConformanceInModule() {} - ProtocolConformanceRef operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const; -}; - -/// Functor class suitable for use as a \c LookupConformanceFn that provides -/// only abstract conformances for generic types. Asserts that the replacement -/// type is an opaque generic type. -class MakeAbstractConformanceForGenericType { -public: - ProtocolConformanceRef operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const; + ProtocolConformanceRef operator()(InFlightSubstitution &IFS, + Type dependentType, + ProtocolDecl *proto) const; }; /// Flags that can be passed when substituting into a type. @@ -161,7 +152,7 @@ inline SubstOptions operator|(SubstFlags lhs, SubstFlags rhs) { /// Enumeration describing foreign languages to which Swift may be /// bridged. -enum class ForeignLanguage { +enum class ForeignLanguage : uint8_t { C, ObjectiveC, }; @@ -347,11 +338,14 @@ class Type { SWIFT_DEBUG_DUMP; void dump(raw_ostream &os, unsigned indent = 0) const; - void print(raw_ostream &OS, const PrintOptions &PO = PrintOptions()) const; - void print(ASTPrinter &Printer, const PrintOptions &PO) const; + void print(raw_ostream &OS, const PrintOptions &PO = PrintOptions(), + NonRecursivePrintOptions OPO = std::nullopt) const; + void print(ASTPrinter &Printer, const PrintOptions &PO, + NonRecursivePrintOptions OPO = std::nullopt) const; /// Return the name of the type as a string, for use in diagnostics only. - std::string getString(const PrintOptions &PO = PrintOptions()) const; + std::string getString(const PrintOptions &PO = PrintOptions(), + NonRecursivePrintOptions OPO = std::nullopt) const; /// Return the name of the type, adding parens in cases where /// appending or prepending text to the result would cause that text @@ -360,7 +354,8 @@ class Type { /// the type would make it appear that it's appended to "Float" as /// opposed to the entire type. std::string - getStringAsComponent(const PrintOptions &PO = PrintOptions()) const; + getStringAsComponent(const PrintOptions &PO = PrintOptions(), + NonRecursivePrintOptions OPO = std::nullopt) const; /// Computes the join between two types. /// diff --git a/include/swift/AST/TypeAlignments.h b/include/swift/AST/TypeAlignments.h index 827e9ba1313d6..c9b5d49b8f78a 100644 --- a/include/swift/AST/TypeAlignments.h +++ b/include/swift/AST/TypeAlignments.h @@ -26,6 +26,7 @@ namespace swift { class AbstractClosureExpr; + class AbstractSpecializeAttr; class AbstractStorageDecl; class ArchetypeType; class AssociatedTypeDecl; @@ -33,6 +34,7 @@ namespace swift { class AttributeBase; class BraceStmt; class CaptureListExpr; + class CustomAvailabilityDomain; class Decl; class DeclContext; class DifferentiableAttr; @@ -54,6 +56,7 @@ namespace swift { class SILFunction; class SILFunctionType; class SpecializeAttr; + class SpecializedAttr; class Stmt; class TrailingWhereClause; class TypeVariableType; @@ -78,6 +81,7 @@ namespace swift { constexpr size_t ASTContextAlignInBits = 2; constexpr size_t TypeVariableAlignInBits = 4; constexpr size_t StoredDefaultArgumentAlignInBits = 3; + constexpr size_t CustomAvailabilityDomainAlignInBits = 3; // Well, this is the *minimum* pointer alignment; it's going to be 3 on // 64-bit targets, but that doesn't matter. @@ -149,6 +153,8 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::RequirementRepr, LLVM_DECLARE_TYPE_ALIGNMENT(swift::SILFunction, swift::SILFunctionAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::SpecializeAttr, swift::PointerAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::SpecializedAttr, swift::PointerAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::AbstractSpecializeAttr, swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::TrailingWhereClause, swift::PointerAlignInBits) @@ -161,6 +167,9 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::CaseLabelItem, swift::PatternAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::StmtConditionElement, swift::PatternAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::CustomAvailabilityDomain, + swift::CustomAvailabilityDomainAlignInBits) + static_assert(alignof(void*) >= 2, "pointer alignment is too small"); #endif diff --git a/include/swift/AST/TypeAttr.def b/include/swift/AST/TypeAttr.def index 811e85a08d865..d03ad6ce013cb 100644 --- a/include/swift/AST/TypeAttr.def +++ b/include/swift/AST/TypeAttr.def @@ -67,7 +67,7 @@ TYPE_ATTR(_opaqueReturnTypeOf, OpaqueReturnTypeOf) TYPE_ATTR(isolated, Isolated) SIMPLE_TYPE_ATTR(nonisolated, Nonisolated) SIMPLE_TYPE_ATTR(_addressable, Addressable) -TYPE_ATTR(execution, Execution) +SIMPLE_TYPE_ATTR(concurrent, Concurrent) // SIL-specific attributes SIMPLE_SIL_TYPE_ATTR(async, Async) @@ -95,6 +95,7 @@ SIMPLE_SIL_TYPE_ATTR(pack_out, PackOut) SIMPLE_SIL_TYPE_ATTR(owned, Owned) SIMPLE_SIL_TYPE_ATTR(unowned_inner_pointer, UnownedInnerPointer) SIMPLE_SIL_TYPE_ATTR(guaranteed, Guaranteed) +SIMPLE_SIL_TYPE_ATTR(guaranteed_addr, GuaranteedAddress) SIMPLE_SIL_TYPE_ATTR(autoreleased, Autoreleased) SIMPLE_SIL_TYPE_ATTR(callee_owned, CalleeOwned) SIMPLE_SIL_TYPE_ATTR(callee_guaranteed, CalleeGuaranteed) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 112790754f961..1d56c937eb950 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -64,7 +64,7 @@ struct PropertyWrapperLValueness; struct PropertyWrapperMutability; class RequirementRepr; class ReturnStmt; -class SpecializeAttr; +class AbstractSpecializeAttr; class TrailingWhereClause; class TypeAliasDecl; class TypeLoc; @@ -475,9 +475,31 @@ class ConformanceHasEffectRequest : bool isCached() const { return true; } }; +class RawConformanceIsolationRequest : + public SimpleRequest(NormalProtocolConformance *), + RequestFlags::SeparatelyCached | + RequestFlags::SplitCached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + std::optional + evaluate(Evaluator &evaluator, NormalProtocolConformance *conformance) const; + +public: + // Separate caching. + bool isCached() const { return true; } + std::optional> getCachedResult() const; + void cacheResult(std::optional result) const; +}; + class ConformanceIsolationRequest : public SimpleRequest { public: @@ -488,7 +510,7 @@ class ConformanceIsolationRequest : // Evaluation. ActorIsolation - evaluate(Evaluator &evaluator, ProtocolConformance *conformance) const; + evaluate(Evaluator &evaluator, NormalProtocolConformance *conformance) const; public: // Separate caching. @@ -648,7 +670,7 @@ struct WhereClauseOwner { /// The source of the where clause, which can be a generic parameter list /// or a declaration that can have a where clause. llvm::PointerUnion + AbstractSpecializeAttr *, DifferentiableAttr *> source; WhereClauseOwner() : dc(nullptr) {} @@ -662,7 +684,7 @@ struct WhereClauseOwner { WhereClauseOwner(DeclContext *dc, TrailingWhereClause *where) : dc(dc), source(where) {} - WhereClauseOwner(DeclContext *dc, SpecializeAttr *attr) + WhereClauseOwner(DeclContext *dc, AbstractSpecializeAttr *attr) : dc(dc), source(attr) {} WhereClauseOwner(DeclContext *dc, DifferentiableAttr *attr) @@ -735,6 +757,48 @@ class RequirementRequest : bool isCached() const; }; +/// Generate the Clang USR for the given declaration. +class ClangUSRGenerationRequest + : public SimpleRequest(const ValueDecl *), + RequestFlags::SeparatelyCached | + RequestFlags::SplitCached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + std::optional evaluate(Evaluator &eval, + const ValueDecl *d) const; + +public: + // Split caching. + bool isCached() const { return true; } + std::optional> getCachedResult() const; + void cacheResult(std::optional result) const; +}; + +/// Generate the Swift USR for the given declaration. +class SwiftUSRGenerationRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + std::string evaluate(Evaluator &eval, const ValueDecl *d) const; + +public: + // Caching + bool isCached() const { return true; } +}; + struct USRGenerationOptions { /// @brief Whether to emit USRs using the Swift declaration when it is /// synthesized from a Clang based declaration. Useful in cases where Swift @@ -742,13 +806,21 @@ struct USRGenerationOptions { /// wants the USR of the Swift declaration. bool distinguishSynthesizedDecls; + /// @brief Whether to emit USRs using the Swift declaration for all + /// declarations specifically, emits a Swift USR for all Clang-based + /// declarations. + bool useSwiftUSR; + friend llvm::hash_code hash_value(const USRGenerationOptions &options) { - return llvm::hash_value(options.distinguishSynthesizedDecls); + return llvm::hash_combine( + llvm::hash_value(options.distinguishSynthesizedDecls), + llvm::hash_value(options.useSwiftUSR)); } friend bool operator==(const USRGenerationOptions &lhs, const USRGenerationOptions &rhs) { - return lhs.distinguishSynthesizedDecls == rhs.distinguishSynthesizedDecls; + return lhs.distinguishSynthesizedDecls == rhs.distinguishSynthesizedDecls && + lhs.useSwiftUSR == rhs.useSwiftUSR; } friend bool operator!=(const USRGenerationOptions &lhs, @@ -761,10 +833,12 @@ void simple_display(llvm::raw_ostream &out, const USRGenerationOptions &options); /// Generate the USR for the given declaration. +/// This is an umbrella request that forwards to ClangUSRGenerationRequest or +/// SwiftUSRGenerationRequest. class USRGenerationRequest : public SimpleRequest { + RequestFlags::Uncached> { public: using SimpleRequest::SimpleRequest; @@ -774,10 +848,6 @@ class USRGenerationRequest // Evaluation. std::string evaluate(Evaluator &eval, const ValueDecl *d, USRGenerationOptions options) const; - -public: - // Caching - bool isCached() const { return true; } }; /// Generate the mangling for the given local type declaration. @@ -2861,7 +2931,7 @@ class SynthesizeDefaultInitRequest class CompareDeclSpecializationRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -2871,8 +2941,8 @@ class CompareDeclSpecializationRequest // Evaluation. bool evaluate(Evaluator &evaluator, DeclContext *DC, ValueDecl *VD1, - ValueDecl *VD2, bool dynamic, - bool allowMissingConformances) const; + ValueDecl *VD2, bool dynamic, bool allowMissingConformances, + bool debugMode) const; public: // Caching. @@ -3311,7 +3381,7 @@ class DynamicallyReplacedDeclRequest class SpecializeAttrTargetDeclRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -3321,7 +3391,7 @@ class SpecializeAttrTargetDeclRequest // Evaluation. ValueDecl *evaluate(Evaluator &evaluator, const ValueDecl *vd, - SpecializeAttr *attr) const; + AbstractSpecializeAttr *attr) const; public: // Caching. @@ -4019,6 +4089,31 @@ class PrimarySourceFilesRequest bool isCached() const { return true; } }; +/// Load the access notes to apply for the main module. +/// +/// Note this is keyed on the ASTContext instead of the ModuleDecl to avoid +/// needing to re-load the access notes in cases where we have multiple main +/// modules, e.g when doing cached top-level code completion. +/// +/// FIXME: This isn't really a type-checking request, if we ever split off a +/// zone for more general requests, this should be moved there. +class LoadAccessNotesRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + const AccessNotesFile *evaluate(Evaluator &evaluator, ASTContext *ctx) const; + +public: + // Cached. + bool isCached() const { return true; } +}; + /// Kinds of types for CustomAttr. enum class CustomAttrTypeKind { /// The type is required to not be expressed in terms of @@ -4853,6 +4948,62 @@ class TypeCheckObjCImplementationRequest bool isCached() const { return true; } }; +/// Check @c functions for compatibility with the foreign language. +class TypeCheckCDeclFunctionRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + evaluator::SideEffect + evaluate(Evaluator &evaluator, FuncDecl *FD, CDeclAttr *attr) const; + +public: + bool isCached() const { return true; } +}; + +/// A request to emit performance hints +class EmitPerformanceHints +: public SimpleRequest { +public: +using SimpleRequest::SimpleRequest; + +private: +friend SimpleRequest; + +evaluator::SideEffect +evaluate(Evaluator &evaluator, SourceFile *SF) const; + +public: +bool isCached() const { return true; } +}; + +/// Check @c enums for compatibility with C. +class TypeCheckCDeclEnumRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + evaluator::SideEffect + evaluate(Evaluator &evaluator, EnumDecl *ED, CDeclAttr *attr) const; + +public: + bool isCached() const { return true; } +}; + void simple_display(llvm::raw_ostream &out, ASTNode node); void simple_display(llvm::raw_ostream &out, Type value); void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR); @@ -4937,7 +5088,7 @@ class ExpandChildAvailabilityScopesRequest class SerializeAttrGenericSignatureRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -4947,7 +5098,7 @@ class SerializeAttrGenericSignatureRequest GenericSignature evaluate(Evaluator &evaluator, const AbstractFunctionDecl *decl, - SpecializeAttr *attr) const; + AbstractSpecializeAttr *attr) const; public: // Separate caching. @@ -5073,8 +5224,7 @@ class ImportDeclRequest class LifetimeDependenceInfoRequest : public SimpleRequest< LifetimeDependenceInfoRequest, - std::optional>( - AbstractFunctionDecl *), + std::optional>(ValueDecl *), RequestFlags::SeparatelyCached | RequestFlags::SplitCached> { public: using SimpleRequest::SimpleRequest; @@ -5083,7 +5233,7 @@ class LifetimeDependenceInfoRequest friend SimpleRequest; std::optional> - evaluate(Evaluator &evaluator, AbstractFunctionDecl *AFD) const; + evaluate(Evaluator &evaluator, ValueDecl *AFD) const; public: // Separate caching. @@ -5134,6 +5284,26 @@ class ParamCaptureInfoRequest : void cacheResult(CaptureInfo value) const; }; +class PatternBindingCaptureInfoRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + CaptureInfo evaluate(Evaluator &evaluator, PatternBindingDecl *PBD, + unsigned idx) const; + +public: + // Separate caching. + bool isCached() const { return true; } + std::optional getCachedResult() const; + void cacheResult(CaptureInfo info) const; +}; + class SuppressesConformanceRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -5228,7 +5398,7 @@ class GenericTypeParamDeclGetValueTypeRequest private: friend SimpleRequest; - Type evaluate(Evaluator &evaluator, GenericTypeParamDecl *decl) const; + Type evaluate(Evaluator &evaluator, const GenericTypeParamDecl *decl) const; public: bool isCached() const { return true; } @@ -5293,24 +5463,97 @@ class SemanticAvailabilitySpecRequest void cacheResult(std::optional value) const; }; -class SourceFileLangOptionsRequest - : public SimpleRequest(const SourceFile *), + RequestFlags::Cached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + std::optional evaluate(Evaluator &evaluator, + const SourceFile *file) const; + +public: + bool isCached() const { return true; } +}; + +/// Performs extension binding for all of the extensions in a module. +class BindExtensionsRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + evaluator::SideEffect evaluate(Evaluator &evaluator, ModuleDecl *M) const; + +public: + bool isCached() const { return true; } +}; + +class ModuleHasTypeCheckerPerformanceHacksEnabledRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + bool evaluate(Evaluator &evaluator, const ModuleDecl *module) const; + +public: + bool isCached() const { return true; } +}; +class AvailabilityDomainForDeclRequest + : public SimpleRequest(ValueDecl *), + RequestFlags::Cached | RequestFlags::SplitCached> { public: using SimpleRequest::SimpleRequest; private: friend SimpleRequest; - SourceFileLangOptions evaluate(Evaluator &evaluator, - SourceFile *sourceFile) const; + std::optional evaluate(Evaluator &evaluator, + ValueDecl *decl) const; public: bool isCached() const { return true; } - std::optional getCachedResult() const; - void cacheResult(SourceFileLangOptions value) const; + std::optional> getCachedResult() const; + void cacheResult(std::optional domain) const; +}; + +class IsCustomAvailabilityDomainPermanentlyEnabled + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + bool evaluate(Evaluator &evaluator, + const CustomAvailabilityDomain *customDomain) const; + +public: + bool isCached() const { return true; } + std::optional getCachedResult() const; + void cacheResult(bool isPermanentlyEnabled) const; + + SourceLoc getNearestLoc() const { + auto *domain = std::get<0>(getStorage()); + return extractNearestSourceLoc(domain->getDecl()); + } }; #define SWIFT_TYPEID_ZONE TypeChecker diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 5173cc5d3a196..e4f9df0fa6840 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -52,7 +52,7 @@ SWIFT_REQUEST(TypeChecker, CheckRedeclarationRequest, SWIFT_REQUEST(TypeChecker, ClassAncestryFlagsRequest, AncestryFlags(ClassDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, CompareDeclSpecializationRequest, - bool (DeclContext *, ValueDecl *, ValueDecl *, bool, bool), + bool (DeclContext *, ValueDecl *, ValueDecl *, bool, bool, bool), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ConditionalRequirementsRequest, ArrayRef (NormalProtocolConformance *), @@ -96,7 +96,7 @@ SWIFT_REQUEST(TypeChecker, ABIMembersRequest, SWIFT_REQUEST(TypeChecker, AllMembersRequest, ArrayRef(IterableDeclContext *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SpecializeAttrTargetDeclRequest, - ValueDecl *(const ValueDecl *, SpecializeAttr *), + ValueDecl *(const ValueDecl *, AbstractSpecializeAttr *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, EnumRawValuesRequest, evaluator::SideEffect (EnumDecl *, TypeResolutionStage), @@ -275,6 +275,8 @@ SWIFT_REQUEST(TypeChecker, PatternBindingCheckedAndContextualizedInitRequest, SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, PrimarySourceFilesRequest, ArrayRef(ModuleDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, LoadAccessNotesRequest, + const AccessNotesFile *(ASTContext *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, PropertyWrapperAuxiliaryVariablesRequest, PropertyWrapperAuxiliaryVariables(VarDecl *), SeparatelyCached | SplitCached, @@ -364,9 +366,15 @@ SWIFT_REQUEST(TypeChecker, TypeCheckASTNodeAtLocRequest, Uncached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, UnderlyingTypeRequest, Type(TypeAliasDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, ClangUSRGenerationRequest, + std::optional(const ValueDecl *), + SeparatelyCached | SplitCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, SwiftUSRGenerationRequest, + std::string(const ValueDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, USRGenerationRequest, std::string(const ValueDecl *, USRGenerationOptions), - Cached, NoLocationInfo) + Uncached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsABICompatibleOverrideRequest, bool(ValueDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsStaticRequest, @@ -406,8 +414,12 @@ SWIFT_REQUEST(TypeChecker, PolymorphicEffectRequirementsRequest, SWIFT_REQUEST(TypeChecker, ConformanceHasEffectRequest, bool(EffectKind, ProtocolConformanceRef), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, RawConformanceIsolationRequest, + std::optional(NormalProtocolConformance *), + SeparatelyCached | SplitCached, + NoLocationInfo) SWIFT_REQUEST(TypeChecker, ConformanceIsolationRequest, - ActorIsolation(ProtocolConformance *), + ActorIsolation(NormalProtocolConformance *), SeparatelyCached | SplitCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResolveTypeRequest, @@ -550,6 +562,12 @@ SWIFT_REQUEST(TypeChecker, IsNonUserModuleRequest, SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest, unsigned(ExtensionDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, TypeCheckCDeclFunctionRequest, + evaluator::SideEffect(FunctionDecl *, CDeclAttr *), + Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, TypeCheckCDeclEnumRequest, + evaluator::SideEffect(EnumDecl *, CDeclAttr *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, HasInitAccessorRequest, bool(AbstractStorageDecl *), Cached, NoLocationInfo) @@ -561,7 +579,7 @@ SWIFT_REQUEST(TypeChecker, ExpandChildAvailabilityScopesRequest, std::vector(AvailabilityScope *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SerializeAttrGenericSignatureRequest, - GenericSignature(Decl *, SpecializeAttr *), + GenericSignature(Decl *, AbstractSpecializeAttr *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsFunctionBodySkippedRequest, bool(const AbstractFunctionDecl *), @@ -608,12 +626,15 @@ SWIFT_REQUEST(TypeChecker, CaptureInfoRequest, SWIFT_REQUEST(TypeChecker, ParamCaptureInfoRequest, CaptureInfo(ParamDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, PatternBindingCaptureInfoRequest, + CaptureInfo(PatternBindingDecl *, unsigned), + SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, CustomDerivativesRequest, CustomDerivativesResult(SourceFile *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, GenericTypeParamDeclGetValueTypeRequest, - Type(GenericTypeParamDecl *), Cached, NoLocationInfo) + Type(const GenericTypeParamDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SemanticAvailableAttrRequest, std::optional @@ -624,5 +645,26 @@ SWIFT_REQUEST(TypeChecker, SemanticAvailabilitySpecRequest, (const AvailabilitySpec *, const DeclContext *), SeparatelyCached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, SourceFileLangOptionsRequest, - SourceFileLangOptions (SourceFile *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DefaultIsolationInSourceFileRequest, + std::optional(const SourceFile *), + Cached, NoLocationInfo) + +SWIFT_REQUEST(TypeChecker, BindExtensionsRequest, + evaluator::SideEffect(ModuleDecl *), + Cached, NoLocationInfo) + +SWIFT_REQUEST(TypeChecker, ModuleHasTypeCheckerPerformanceHacksEnabledRequest, + bool(const ModuleDecl *), + Cached, NoLocationInfo) + +SWIFT_REQUEST(TypeChecker, AvailabilityDomainForDeclRequest, + std::optional(ValueDecl *), + Cached | SplitCached, NoLocationInfo) + +SWIFT_REQUEST(TypeChecker, IsCustomAvailabilityDomainPermanentlyEnabled, + bool(const CustomAvailabilityDomain *), + SeparatelyCached, NoLocationInfo) + +SWIFT_REQUEST(TypeChecker, EmitPerformanceHints, + evaluator::SideEffect(SourceFile *), + Cached, NoLocationInfo) diff --git a/include/swift/AST/TypeMatcher.h b/include/swift/AST/TypeMatcher.h index ab6d90eb2190d..31ac8e6ab299d 100644 --- a/include/swift/AST/TypeMatcher.h +++ b/include/swift/AST/TypeMatcher.h @@ -117,12 +117,6 @@ class TypeMatcher { #define SINGLETON_TYPE(SHORT_ID, ID) TRIVIAL_CASE(ID##Type) #include "swift/AST/TypeNodes.def" - bool visitUnresolvedType(CanUnresolvedType firstType, Type secondType, - Type sugaredFirstType) { - // Unresolved types never match. - return mismatch(firstType.getPointer(), secondType, sugaredFirstType); - } - bool visitTupleType(CanTupleType firstTuple, Type secondType, Type sugaredFirstType) { if (auto secondTuple = secondType->getAs()) { diff --git a/include/swift/AST/TypeMemberVisitor.h b/include/swift/AST/TypeMemberVisitor.h index 7ad863f980968..479a84510d49d 100644 --- a/include/swift/AST/TypeMemberVisitor.h +++ b/include/swift/AST/TypeMemberVisitor.h @@ -41,6 +41,7 @@ class TypeMemberVisitor : public DeclVisitor { BAD_MEMBER(Operator) BAD_MEMBER(PrecedenceGroup) BAD_MEMBER(Macro) + BAD_MEMBER(Using) RetTy visitMacroExpansionDecl(MacroExpansionDecl *D) { // Expansion already visited as auxiliary decls. diff --git a/include/swift/AST/TypeNodes.def b/include/swift/AST/TypeNodes.def index 5ae2e5c658401..869a7cc7e99d5 100644 --- a/include/swift/AST/TypeNodes.def +++ b/include/swift/AST/TypeNodes.def @@ -123,7 +123,6 @@ #if !defined(SINGLETON_TYPE) TYPE(Error, Type) -UNCHECKED_TYPE(Unresolved, Type) UNCHECKED_TYPE(Placeholder, Type) ABSTRACT_TYPE(Builtin, Type) ABSTRACT_TYPE(AnyBuiltinInteger, BuiltinType) @@ -177,7 +176,7 @@ TYPE(DynamicSelf, Type) ABSTRACT_TYPE(Substitutable, Type) ABSTRACT_TYPE(Archetype, SubstitutableType) ALWAYS_CANONICAL_TYPE(PrimaryArchetype, ArchetypeType) - ALWAYS_CANONICAL_TYPE(OpaqueTypeArchetype, ArchetypeType) + TYPE(OpaqueTypeArchetype, ArchetypeType) ABSTRACT_TYPE(LocalArchetype, ArchetypeType) ALWAYS_CANONICAL_TYPE(ExistentialArchetype, LocalArchetypeType) ALWAYS_CANONICAL_TYPE(ElementArchetype, LocalArchetypeType) @@ -207,7 +206,7 @@ TYPE(PackExpansion, Type) TYPE(PackElement, Type) UNCHECKED_TYPE(TypeVariable, Type) UNCHECKED_TYPE(ErrorUnion, Type) -ALWAYS_CANONICAL_TYPE(Integer, Type) +TYPE(Integer, Type) ABSTRACT_SUGARED_TYPE(Sugar, Type) SUGARED_TYPE(TypeAlias, SugarType) SUGARED_TYPE(Locatable, SugarType) diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index 749cf72190223..13c37817aec13 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -205,8 +205,10 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr //*** Allocation Routines ************************************************/ - void print(raw_ostream &OS, const PrintOptions &Opts = PrintOptions()) const; - void print(ASTPrinter &Printer, const PrintOptions &Opts) const; + void print(raw_ostream &OS, const PrintOptions &opts = PrintOptions(), + NonRecursivePrintOptions nrOpts = std::nullopt) const; + void print(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts = std::nullopt) const; SWIFT_DEBUG_DUMP; void dump(raw_ostream &OS, unsigned indent = 0) const; }; @@ -214,35 +216,29 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr /// A TypeRepr for a type with a syntax error. Can be used both as a /// top-level TypeRepr and as a part of other TypeRepr. /// -/// The client can either emit a detailed diagnostic at the construction time -/// (in the parser) or store a zero-arg diagnostic in this TypeRepr to be -/// emitted after parsing, during type resolution. -/// /// All uses of this type should be ignored and not re-diagnosed. class ErrorTypeRepr : public TypeRepr { SourceRange Range; - std::optional DelayedDiag; - ErrorTypeRepr(SourceRange Range, std::optional Diag) - : TypeRepr(TypeReprKind::Error), Range(Range), DelayedDiag(Diag) {} + /// The original expression that failed to be resolved as a TypeRepr. Like + /// ErrorExpr's original expr, this exists to ensure that semantic + /// functionality can still work correctly, and is used to ensure we don't + /// drop nodes from the AST. + Expr *OriginalExpr; -public: - static ErrorTypeRepr * - create(ASTContext &Context, SourceRange Range, - std::optional DelayedDiag = std::nullopt) { - assert((!DelayedDiag || Range) && "diagnostic needs a location"); - return new (Context) ErrorTypeRepr(Range, DelayedDiag); - } + ErrorTypeRepr(SourceRange Range, Expr *OriginalExpr) + : TypeRepr(TypeReprKind::Error), Range(Range), + OriginalExpr(OriginalExpr) {} - static ErrorTypeRepr * - create(ASTContext &Context, SourceLoc Loc = SourceLoc(), - std::optional DelayedDiag = std::nullopt) { - return create(Context, SourceRange(Loc), DelayedDiag); +public: + static ErrorTypeRepr *create(ASTContext &Context, SourceRange Range, + Expr *OriginalExpr = nullptr) { + return new (Context) ErrorTypeRepr(Range, OriginalExpr); } - /// If there is a delayed diagnostic stored in this TypeRepr, consumes and - /// emits that diagnostic. - void dischargeDiagnostic(ASTContext &Context); + /// Retrieve the original expression that failed to be resolved as a TypeRepr. + Expr *getOriginalExpr() const { return OriginalExpr; } + void setOriginalExpr(Expr *newExpr) { OriginalExpr = newExpr; } static bool classof(const TypeRepr *T) { return T->getKind() == TypeReprKind::Error; @@ -252,7 +248,8 @@ class ErrorTypeRepr : public TypeRepr { private: SourceLoc getStartLocImpl() const { return Range.Start; } SourceLoc getEndLocImpl() const { return Range.End; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -269,9 +266,7 @@ class AttributedTypeRepr final : TypeRepr(TypeReprKind::Attributed), Ty(Ty) { assert(!attrs.empty()); Bits.AttributedTypeRepr.NumAttributes = attrs.size(); - std::uninitialized_copy(attrs.begin(), attrs.end(), - getTrailingObjects()); - + std::uninitialized_copy(attrs.begin(), attrs.end(), getTrailingObjects()); } friend TrailingObjects; @@ -282,8 +277,7 @@ class AttributedTypeRepr final TypeRepr *ty); ArrayRef getAttrs() const { - return llvm::ArrayRef(getTrailingObjects(), - Bits.AttributedTypeRepr.NumAttributes); + return getTrailingObjects(Bits.AttributedTypeRepr.NumAttributes); } TypeAttribute *get(TypeAttrKind kind) const; @@ -303,7 +297,7 @@ class AttributedTypeRepr final ReferenceOwnership getSILOwnership() const; void printAttrs(llvm::raw_ostream &OS) const; - void printAttrs(ASTPrinter &Printer, const PrintOptions &Options) const; + void printAttrs(ASTPrinter &Printer, const PrintOptions &options) const; static bool classof(const TypeRepr *T) { return T->getKind() == TypeReprKind::Attributed; @@ -316,12 +310,13 @@ class AttributedTypeRepr final if (auto customAttr = attr.dyn_cast()) { return customAttr->getStartLoc(); } else { - return attr.get()->getStartLoc(); + return cast(attr)->getStartLoc(); } } SourceLoc getEndLocImpl() const { return Ty->getEndLoc(); } SourceLoc getLocImpl() const { return Ty->getLoc(); } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -417,7 +412,8 @@ class DeclRefTypeRepr : public TypeRepr { SourceLoc getLocImpl() const; SourceLoc getEndLocImpl() const; - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &Opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -610,7 +606,8 @@ class FunctionTypeRepr : public TypeRepr { SourceLoc getEndLocImpl() const { return RetTy->getEndLoc(); } SourceLoc getLocImpl() const { return ArrowLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -637,11 +634,12 @@ class ArrayTypeRepr : public TypeRepr { private: SourceLoc getStartLocImpl() const { return Brackets.Start; } SourceLoc getEndLocImpl() const { return Brackets.End; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; -/// An InlineArray type e.g `[2 x Foo]`, sugar for `InlineArray<2, Foo>`. +/// An InlineArray type e.g `[2 of Foo]`, sugar for `InlineArray<2, Foo>`. class InlineArrayTypeRepr : public TypeRepr { TypeRepr *Count; TypeRepr *Element; @@ -666,7 +664,8 @@ class InlineArrayTypeRepr : public TypeRepr { private: SourceLoc getStartLocImpl() const { return Brackets.Start; } SourceLoc getEndLocImpl() const { return Brackets.End; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -699,7 +698,8 @@ class DictionaryTypeRepr : public TypeRepr { private: SourceLoc getStartLocImpl() const { return Brackets.Start; } SourceLoc getEndLocImpl() const { return Brackets.End; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -731,7 +731,8 @@ class OptionalTypeRepr : public TypeRepr { SourceLoc getLocImpl() const { return QuestionLoc.isValid() ? QuestionLoc : Base->getLoc(); } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -760,7 +761,8 @@ class ImplicitlyUnwrappedOptionalTypeRepr : public TypeRepr { SourceLoc getStartLocImpl() const { return Base->getStartLoc(); } SourceLoc getEndLocImpl() const { return ExclamationLoc; } SourceLoc getLocImpl() const { return ExclamationLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -801,7 +803,8 @@ class VarargTypeRepr final : public TypeRepr { SourceLoc getStartLocImpl() const { return Element->getEndLoc(); } SourceLoc getEndLocImpl() const { return EllipsisLoc; } SourceLoc getLocImpl() const { return EllipsisLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -832,7 +835,8 @@ class PackExpansionTypeRepr final : public TypeRepr { SourceLoc getStartLocImpl() const { return RepeatLoc; } SourceLoc getEndLocImpl() const { return Pattern->getEndLoc(); } SourceLoc getLocImpl() const { return RepeatLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -863,12 +867,10 @@ class PackTypeRepr final SourceRange getBracesRange() const { return BraceLocs; } MutableArrayRef getMutableElements() { - return llvm::MutableArrayRef(getTrailingObjects(), - Bits.PackTypeRepr.NumElements); + return getTrailingObjects(Bits.PackTypeRepr.NumElements); } ArrayRef getElements() const { - return llvm::ArrayRef(getTrailingObjects(), - Bits.PackTypeRepr.NumElements); + return getTrailingObjects(Bits.PackTypeRepr.NumElements); } static bool classof(const TypeRepr *T) { @@ -880,7 +882,8 @@ class PackTypeRepr final SourceLoc getStartLocImpl() const { return KeywordLoc; } SourceLoc getEndLocImpl() const { return BraceLocs.End; } SourceLoc getLocImpl() const { return KeywordLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -915,7 +918,8 @@ class PackElementTypeRepr: public TypeRepr { SourceLoc getStartLocImpl() const { return EachLoc; } SourceLoc getEndLocImpl() const { return PackType->getEndLoc(); } SourceLoc getLocImpl() const { return EachLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -949,8 +953,8 @@ class TupleTypeRepr final : public TypeRepr, } ArrayRef getElements() const { - return { getTrailingObjects(), - static_cast(Bits.TupleTypeRepr.NumElements) }; + return getTrailingObjects( + static_cast(Bits.TupleTypeRepr.NumElements)); } void getElementTypes(SmallVectorImpl &Types) const { @@ -1010,7 +1014,8 @@ class TupleTypeRepr final : public TypeRepr, private: SourceLoc getStartLocImpl() const { return Parens.Start; } SourceLoc getEndLocImpl() const { return Parens.End; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1030,13 +1035,13 @@ class CompositionTypeRepr final : public TypeRepr, : TypeRepr(TypeReprKind::Composition), FirstTypeLoc(FirstTypeLoc), CompositionRange(CompositionRange) { Bits.CompositionTypeRepr.NumTypes = Types.size(); - std::uninitialized_copy(Types.begin(), Types.end(), - getTrailingObjects()); + std::uninitialized_copy(Types.begin(), Types.end(), getTrailingObjects()); } public: ArrayRef getTypes() const { - return {getTrailingObjects(), static_cast(Bits.CompositionTypeRepr.NumTypes)}; + return getTrailingObjects( + static_cast(Bits.CompositionTypeRepr.NumTypes)); } SourceLoc getSourceLoc() const { return FirstTypeLoc; } SourceRange getCompositionRange() const { return CompositionRange; } @@ -1066,7 +1071,8 @@ class CompositionTypeRepr final : public TypeRepr, SourceLoc getStartLocImpl() const { return FirstTypeLoc; } SourceLoc getLocImpl() const { return CompositionRange.Start; } SourceLoc getEndLocImpl() const { return CompositionRange.End; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1095,7 +1101,8 @@ class MetatypeTypeRepr : public TypeRepr { SourceLoc getStartLocImpl() const { return Base->getStartLoc(); } SourceLoc getEndLocImpl() const { return MetaLoc; } SourceLoc getLocImpl() const { return MetaLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1124,7 +1131,8 @@ class ProtocolTypeRepr : public TypeRepr { SourceLoc getStartLocImpl() const { return Base->getStartLoc(); } SourceLoc getEndLocImpl() const { return ProtocolLoc; } SourceLoc getLocImpl() const { return ProtocolLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1155,7 +1163,8 @@ class SpecifierTypeRepr : public TypeRepr { private: SourceLoc getStartLocImpl() const { return SpecifierLoc; } SourceLoc getEndLocImpl() const { return Base->getEndLoc(); } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1249,6 +1258,36 @@ class SendingTypeRepr : public SpecifierTypeRepr { static bool classof(const SendingTypeRepr *T) { return true; } }; +/// A 'nonisolated(nonsending)' function type. +/// \code +/// x : nonisolated(nonsending) () async -> Int +/// \endcode +class CallerIsolatedTypeRepr : public TypeRepr { + TypeRepr *Base; + SourceLoc Loc; + +public: + CallerIsolatedTypeRepr(TypeRepr *Base, SourceLoc Loc) + : TypeRepr(TypeReprKind::CallerIsolated), Base(Base), Loc(Loc) { + assert(Base); + } + + TypeRepr *getBase() const { return Base; } + + static bool classof(const TypeRepr *T) { + return T->getKind() == TypeReprKind::CallerIsolated; + } + static bool classof(const CallerIsolatedTypeRepr *T) { return true; } + +private: + SourceLoc getStartLocImpl() const { return Loc; } + SourceLoc getEndLocImpl() const { return Base->getEndLoc(); } + SourceLoc getLocImpl() const { return Base->getLoc(); } + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; + friend class TypeRepr; +}; + /// A TypeRepr for a known, fixed type. /// /// Fixed type representations should be used sparingly, in places @@ -1282,7 +1321,8 @@ class FixedTypeRepr : public TypeRepr { private: SourceLoc getStartLocImpl() const { return Loc; } SourceLoc getEndLocImpl() const { return Loc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1309,7 +1349,8 @@ class SelfTypeRepr : public TypeRepr { private: SourceLoc getStartLocImpl() const { return Loc; } SourceLoc getEndLocImpl() const { return Loc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1362,7 +1403,8 @@ class OpaqueReturnTypeRepr : public TypeRepr { SourceLoc getStartLocImpl() const { return OpaqueLoc; } SourceLoc getEndLocImpl() const { return Constraint->getEndLoc(); } SourceLoc getLocImpl() const { return OpaqueLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1397,7 +1439,8 @@ class NamedOpaqueReturnTypeRepr : public TypeRepr { SourceLoc getStartLocImpl() const; SourceLoc getEndLocImpl() const; SourceLoc getLocImpl() const; - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1426,7 +1469,8 @@ class ExistentialTypeRepr: public TypeRepr { SourceLoc getStartLocImpl() const { return AnyLoc; } SourceLoc getEndLocImpl() const { return Constraint->getEndLoc(); } SourceLoc getLocImpl() const { return AnyLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1454,7 +1498,8 @@ class InverseTypeRepr : public TypeRepr { SourceLoc getStartLocImpl() const { return TildeLoc; } SourceLoc getEndLocImpl() const { return Constraint->getEndLoc(); } SourceLoc getLocImpl() const { return TildeLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1482,7 +1527,8 @@ class PlaceholderTypeRepr: public TypeRepr { SourceLoc getStartLocImpl() const { return UnderscoreLoc; } SourceLoc getEndLocImpl() const { return UnderscoreLoc; } SourceLoc getLocImpl() const { return UnderscoreLoc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1571,7 +1617,8 @@ class SILBoxTypeRepr final : public TypeRepr, SourceLoc getStartLocImpl() const; SourceLoc getEndLocImpl() const; SourceLoc getLocImpl() const; - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend TypeRepr; }; @@ -1598,7 +1645,8 @@ class LifetimeDependentTypeRepr final : public SpecifierTypeRepr { SourceLoc getStartLocImpl() const; SourceLoc getEndLocImpl() const; SourceLoc getLocImpl() const; - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1639,7 +1687,8 @@ class IntegerTypeRepr final : public TypeRepr { SourceLoc getEndLocImpl() const { return Loc; } SourceLoc getLocImpl() const { return Loc; } - void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; + void printImpl(ASTPrinter &Printer, const PrintOptions &opts, + NonRecursivePrintOptions nrOpts) const; friend class TypeRepr; }; @@ -1680,6 +1729,7 @@ inline bool TypeRepr::isSimple() const { case TypeReprKind::ConstValue: case TypeReprKind::LifetimeDependent: case TypeReprKind::Integer: + case TypeReprKind::CallerIsolated: return true; } llvm_unreachable("bad TypeRepr kind"); diff --git a/include/swift/AST/TypeReprNodes.def b/include/swift/AST/TypeReprNodes.def index a1f7e51ded313..6a98b8f4dd3e6 100644 --- a/include/swift/AST/TypeReprNodes.def +++ b/include/swift/AST/TypeReprNodes.def @@ -77,6 +77,7 @@ TYPEREPR(Fixed, TypeRepr) TYPEREPR(SILBox, TypeRepr) TYPEREPR(Self, TypeRepr) TYPEREPR(LifetimeDependent, TypeRepr) +TYPEREPR(CallerIsolated, TypeRepr) TYPEREPR(Integer, TypeRepr) LAST_TYPEREPR(Integer) diff --git a/include/swift/AST/TypeTransform.h b/include/swift/AST/TypeTransform.h index 8348506c836a8..1305c2695901e 100644 --- a/include/swift/AST/TypeTransform.h +++ b/include/swift/AST/TypeTransform.h @@ -109,7 +109,6 @@ case TypeKind::Id: #define TYPE(Id, Parent) #include "swift/AST/TypeNodes.def" case TypeKind::Error: - case TypeKind::Unresolved: case TypeKind::TypeVariable: case TypeKind::Placeholder: case TypeKind::SILToken: @@ -358,7 +357,8 @@ case TypeKind::Id: } }); if (didRemoveLifetimeDependencies) { - extInfo = extInfo.withLifetimeDependencies(substDependenceInfos); + extInfo = extInfo.withLifetimeDependencies( + ctx.AllocateCopy(substDependenceInfos)); } } @@ -939,7 +939,8 @@ case TypeKind::Id: }); if (didRemoveLifetimeDependencies) { - extInfo = extInfo->withLifetimeDependencies(substDependenceInfos); + extInfo = extInfo->withLifetimeDependencies( + ctx.AllocateCopy(substDependenceInfos)); } } @@ -1176,6 +1177,12 @@ case TypeKind::Id: if (transformedPack.getPointer() == element->getPackType().getPointer()) return element; + if (!transformedPack->isParameterPack() && + !transformedPack->is() && + !transformedPack->isTypeVariableOrMember()) { + return transformedPack; + } + return PackElementType::get(transformedPack, element->getLevel()); } diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index bc53a44510809..bf44de8fd514b 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -141,24 +141,24 @@ class RecursiveTypeProperties { /// This type expression contains a GenericTypeParamType. HasTypeParameter = 0x04, - /// This type expression contains an UnresolvedType. - HasUnresolvedType = 0x08, - /// Whether this type expression contains an unbound generic type. - HasUnboundGeneric = 0x10, + HasUnboundGeneric = 0x08, /// This type expression contains an LValueType other than as a /// function input, and can be loaded to convert to an rvalue. - IsLValue = 0x20, + IsLValue = 0x10, /// This type expression contains an opened existential ArchetypeType. - HasOpenedExistential = 0x40, + HasOpenedExistential = 0x20, /// This type expression contains a DynamicSelf type. - HasDynamicSelf = 0x80, + HasDynamicSelf = 0x40, /// This type contains an Error type. - HasError = 0x100, + HasError = 0x80, + + /// This type contains an Error type without an underlying original type. + HasBareError = 0x100, /// This type contains a DependentMemberType. HasDependentMember = 0x200, @@ -225,15 +225,15 @@ class RecursiveTypeProperties { /// Does a type with these properties have a type parameter somewhere in it? bool hasTypeParameter() const { return Bits & HasTypeParameter; } - /// Does a type with these properties have an unresolved type somewhere in it? - bool hasUnresolvedType() const { return Bits & HasUnresolvedType; } - /// Is a type with these properties an lvalue? bool isLValue() const { return Bits & IsLValue; } /// Does this type contain an error? bool hasError() const { return Bits & HasError; } + /// Does this type contain an error without an original type? + bool hasBareError() const { return Bits & HasBareError; } + /// Does this type contain a dependent member type, possibly with a /// non-type parameter base, such as a type variable or concrete type? bool hasDependentMember() const { return Bits & HasDependentMember; } @@ -318,6 +318,11 @@ class RecursiveTypeProperties { Bits &= ~HasDependentMember; } + /// Remove the IsUnsafe property from this set. + void removeIsUnsafe() { + Bits &= ~IsUnsafe; + } + /// Test for a particular property in this set. bool operator&(Property prop) const { return Bits & prop; @@ -402,21 +407,25 @@ class alignas(1 << TypeAlignInBits) TypeBase } protected: - enum { NumAFTExtInfoBits = 15 }; + enum { NumAFTExtInfoBits = 16 }; enum { NumSILExtInfoBits = 14 }; // clang-format off union { uint64_t OpaqueBits; - SWIFT_INLINE_BITFIELD_BASE(TypeBase, bitmax(NumTypeKindBits,8) + - RecursiveTypeProperties::BitWidth + 1, + SWIFT_INLINE_BITFIELD_BASE(TypeBase, NumTypeKindBits + + RecursiveTypeProperties::BitWidth + 1 + 3, /// Kind - The discriminator that indicates what subclass of type this is. - Kind : bitmax(NumTypeKindBits,8), + Kind : NumTypeKindBits, Properties : RecursiveTypeProperties::BitWidth, /// Whether this type is canonical or not. - IsCanonical : 1 + IsCanonical : 1, + + ComputedInvertibleConformances : 1, + IsCopyable : 1, + IsEscapable : 1 ); SWIFT_INLINE_BITFIELD(ErrorType, TypeBase, 1, @@ -435,9 +444,7 @@ class alignas(1 << TypeAlignInBits) TypeBase HasExtInfo : 1, HasClangTypeInfo : 1, HasThrownError : 1, - HasLifetimeDependencies : 1, - : NumPadBits, - NumParams : 16 + HasLifetimeDependencies : 1 ); SWIFT_INLINE_BITFIELD_FULL(ArchetypeType, TypeBase, 1+1+16, @@ -450,9 +457,8 @@ class alignas(1 << TypeAlignInBits) TypeBase SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 7+28, /// Type variable options. Options : 7, - : NumPadBits, /// The unique number assigned to this type variable. - ID : 28 + ID : 27 ); SWIFT_INLINE_BITFIELD_FULL(ErrorUnionType, TypeBase, 32, @@ -555,6 +561,11 @@ class alignas(1 << TypeAlignInBits) TypeBase Bits.TypeBase.IsCanonical = true; Context = CanTypeCtx; } + + Bits.TypeBase.ComputedInvertibleConformances = false; + Bits.TypeBase.IsCopyable = false; + Bits.TypeBase.IsEscapable = false; + setRecursiveProperties(properties); } @@ -586,6 +597,8 @@ class alignas(1 << TypeAlignInBits) TypeBase private: CanType computeCanonicalType(); + void computeInvertibleConformances(); + public: /// getCanonicalType - Return the canonical version of this type, which has /// sugar from all levels stripped off. @@ -685,6 +698,9 @@ class alignas(1 << TypeAlignInBits) TypeBase /// Copyable. bool isNoncopyable(); + /// Returns true if this contextual type satisfies a conformance to Copyable. + bool isCopyable(); + /// Returns true if this contextual type satisfies a conformance to Escapable. bool isEscapable(); @@ -695,6 +711,14 @@ class alignas(1 << TypeAlignInBits) TypeBase /// Returns true if this contextual type is (Escapable && !isNoEscape). bool mayEscape() { return !isNoEscape() && isEscapable(); } + /// Returns true if this contextual type satisfies a conformance to + /// BitwiseCopyable. + bool isBitwiseCopyable(); + + /// Returns true if this type satisfies a conformance to BitwiseCopyable in + /// the given generic signature. + bool isBitwiseCopyable(GenericSignature sig); + /// Are values of this type essentially just class references, /// possibly with some sort of additional information? /// @@ -715,15 +739,16 @@ class alignas(1 << TypeAlignInBits) TypeBase return getRecursiveProperties().hasTypeVariable(); } + // Convenience for checking whether the given type either has a type + // variable or placeholder. + bool hasTypeVariableOrPlaceholder() const { + return hasTypeVariable() || hasPlaceholder(); + } + /// Determine where this type is a type variable or a dependent /// member root in a type variable. bool isTypeVariableOrMember(); - /// Determine whether this type involves a UnresolvedType. - bool hasUnresolvedType() const { - return getRecursiveProperties().hasUnresolvedType(); - } - /// Determine whether this type involves a \c PlaceholderType. bool hasPlaceholder() const { return getRecursiveProperties().hasPlaceholder(); @@ -824,12 +849,11 @@ class alignas(1 << TypeAlignInBits) TypeBase /// type variables referenced by this type. void getTypeVariables(SmallPtrSetImpl &typeVariables); -private: +public: /// If the receiver is a `DependentMemberType`, returns its root. Otherwise, /// returns the receiver. Type getDependentMemberRoot(); -public: /// Determine whether this type is a type parameter, which is either a /// GenericTypeParamType or a DependentMemberType. /// @@ -918,6 +942,16 @@ class alignas(1 << TypeAlignInBits) TypeBase return getRecursiveProperties().hasError(); } + /// Determine whether this type contains an error type without an + /// underlying original type, i.e prints as `_`. + bool hasBareError() const { + return getRecursiveProperties().hasBareError(); + } + + /// Whether this is a top-level ErrorType without an underlying original + /// type, i.e prints as `_`. + bool isBareErrorType() const; + /// Does this type contain a dependent member type, possibly with a /// non-type parameter base, such as a type variable or concrete type? bool hasDependentMember() const { @@ -982,9 +1016,13 @@ class alignas(1 << TypeAlignInBits) TypeBase /// type) from `DistributedActor`. bool isDistributedActor(); - /// Determine if the type in question is an Array and, if so, provide the + /// Determine if this type is an Array and, if so, provide the element type + /// of the array. + Type getArrayElementType(); + + /// Determine if this type is an InlineArray and, if so, provide the /// element type of the array. - Type isArrayType(); + Type getInlineArrayElementType(); /// Determines the element type of a known /// [Autoreleasing]Unsafe[Mutable][Raw]Pointer variant, or returns null if the @@ -1049,6 +1087,9 @@ class alignas(1 << TypeAlignInBits) TypeBase bool is##NAME(); #include "swift/AST/KnownStdlibTypes.def" + /// Check if this type is from the Builtin module. + bool isBuiltinType(); + /// Check if this type is equal to Builtin.IntN. bool isBuiltinIntegerType(unsigned bitWidth); @@ -1059,6 +1100,21 @@ class alignas(1 << TypeAlignInBits) TypeBase /// on macOS or Foundation on Linux. bool isCGFloat(); + /// Check if this is a ObjCBool type from the Objective-C module. + bool isObjCBool(); + + /// Check if this is a std.string type from C++. + bool isCxxString(); + + /// Check if this is the type Unicode.Scalar from the Swift standard library. + bool isUnicodeScalar(); + + /// Check if this type is known to represent key paths. + bool isKnownKeyPathType(); + + /// Check if this type is known to represent immutable key paths. + bool isKnownImmutableKeyPathType(); + /// Check if this is either an Array, Set or Dictionary collection type defined /// at the top level of the Swift module bool isKnownStdlibCollectionType(); @@ -1229,6 +1285,9 @@ class alignas(1 << TypeAlignInBits) TypeBase /// representation, i.e. whether it is representable as a single, /// possibly nil pointer that can be unknown-retained and /// unknown-released. + /// This does not include C++ imported `SWIFT_SHARED_REFERENCE` classes. + /// They act as Swift classes but are not compatible with Swift's + /// retain/release runtime functions. bool hasRetainablePointerRepresentation(); /// Given that this type is a reference type, which kind of reference @@ -1296,18 +1355,16 @@ class alignas(1 << TypeAlignInBits) TypeBase /// argument labels removed. Type removeArgumentLabels(unsigned numArgumentLabels); - /// Replace the base type of the result type of the given function - /// type with a new result type, as per a DynamicSelf or other - /// covariant return transformation. The optionality of the - /// existing result will be preserved. - /// - /// \param newResultType The new result type. - /// - /// \param uncurryLevel The number of uncurry levels to apply before - /// replacing the type. With uncurry level == 0, this simply - /// replaces the current type with the new result type. - Type replaceCovariantResultType(Type newResultType, - unsigned uncurryLevel); + /// Replace DynamicSelfType anywhere it appears in covariant position with + /// its underlying Self type. + Type eraseDynamicSelfType(); + + /// Replace DynamicSelfType anywhere it appears in covariant position with + /// the given type. + Type replaceDynamicSelfType(Type newSelfType); + + /// Hack to deal with ConstructorDecl interface types. + Type withCovariantResultType(); /// Returns a new function type exactly like this one but with the self /// parameter replaced. Only makes sense for function members of types. @@ -1479,8 +1536,10 @@ class alignas(1 << TypeAlignInBits) TypeBase SWIFT_DEBUG_DUMPER(print()); void print(raw_ostream &OS, - const PrintOptions &PO = PrintOptions()) const; - void print(ASTPrinter &Printer, const PrintOptions &PO) const; + const PrintOptions &PO = PrintOptions(), + NonRecursivePrintOptions nrOptions = std::nullopt) const; + void print(ASTPrinter &Printer, const PrintOptions &PO, + NonRecursivePrintOptions nrOptions = std::nullopt) const; /// Can this type be written in source at all? /// @@ -1493,7 +1552,8 @@ class alignas(1 << TypeAlignInBits) TypeBase bool hasSimpleTypeRepr() const; /// Return the name of the type as a string, for use in diagnostics only. - std::string getString(const PrintOptions &PO = PrintOptions()) const; + std::string getString(const PrintOptions &PO = PrintOptions(), + NonRecursivePrintOptions nrOptions = std::nullopt) const; /// Return the name of the type, adding parens in cases where /// appending or prepending text to the result would cause that text @@ -1502,7 +1562,8 @@ class alignas(1 << TypeAlignInBits) TypeBase /// the type would make it appear that it's appended to "Float" as /// opposed to the entire type. std::string - getStringAsComponent(const PrintOptions &PO = PrintOptions()) const; + getStringAsComponent(const PrintOptions &PO = PrintOptions(), + NonRecursivePrintOptions nrOptions = std::nullopt) const; /// Return whether this type is or can be substituted for a bridgeable /// object type. @@ -1599,11 +1660,19 @@ DEFINE_EMPTY_CAN_TYPE_WRAPPER(NominalOrBoundGenericNominalType, AnyGenericType) /// have to emit further diagnostics to abort compilation. class ErrorType final : public TypeBase { friend class ASTContext; - // The Error type is always canonical. - ErrorType(ASTContext &C, Type originalType, - RecursiveTypeProperties properties) - : TypeBase(TypeKind::Error, &C, properties) { - assert(properties.hasError()); + + static RecursiveTypeProperties getProperties(Type originalType) { + RecursiveTypeProperties props = RecursiveTypeProperties::HasError; + if (!originalType || originalType->hasBareError()) + props |= RecursiveTypeProperties::HasBareError; + + return props; + } + + ErrorType(ASTContext &C, Type originalType) + : TypeBase(TypeKind::Error, + (!originalType || originalType->isCanonical()) ? &C : nullptr, + getProperties(originalType)) { if (originalType) { Bits.ErrorType.HasOriginalType = true; *reinterpret_cast(this + 1) = originalType; @@ -1634,25 +1703,6 @@ class ErrorType final : public TypeBase { } }; DEFINE_EMPTY_CAN_TYPE_WRAPPER(ErrorType, Type) - -/// UnresolvedType - This represents a type variable that cannot be resolved to -/// a concrete type because the expression is ambiguous. This is produced when -/// parsing expressions and producing diagnostics. Any instance of this should -/// cause the entire expression to be ambiguously typed. -class UnresolvedType : public TypeBase { - friend class ASTContext; - // The Unresolved type is always canonical. - UnresolvedType(ASTContext &C) - : TypeBase(TypeKind::Unresolved, &C, - RecursiveTypeProperties(RecursiveTypeProperties::HasUnresolvedType)) { } -public: - // Implement isa/cast/dyncast/etc. - static bool classof(const TypeBase *T) { - return T->getKind() == TypeKind::Unresolved; - } -}; -DEFINE_EMPTY_CAN_TYPE_WRAPPER(UnresolvedType, Type) - /// BuiltinType - An abstract class for all the builtin types. class BuiltinType : public TypeBase { @@ -1734,18 +1784,10 @@ class BuiltinFixedArrayType : public BuiltinType, public llvm::FoldingSetNode { CanType Size; CanType ElementType; - static RecursiveTypeProperties - getRecursiveTypeProperties(CanType Size, CanType Element) { - RecursiveTypeProperties properties; - properties |= Size->getRecursiveProperties(); - properties |= Element->getRecursiveProperties(); - return properties; - } - - BuiltinFixedArrayType(CanType Size, - CanType ElementType) + BuiltinFixedArrayType(CanType Size, CanType ElementType, + RecursiveTypeProperties properties) : BuiltinType(TypeKind::BuiltinFixedArray, ElementType->getASTContext(), - getRecursiveTypeProperties(Size, ElementType)), + properties), Size(Size), ElementType(ElementType) {} @@ -2298,22 +2340,24 @@ class TypeAliasType final /// Retrieve the parent of this type as written, e.g., the part that was /// written before ".", if provided. Type getParent() const { - return Bits.TypeAliasType.HasParent ? *getTrailingObjects() - : Type(); + return Bits.TypeAliasType.HasParent ? *getTrailingObjects() : Type(); } /// Retrieve the substitution map applied to the declaration's underlying /// to produce the described type. - SubstitutionMap getSubstitutionMap() const; + /// + /// \param wantContextualType If \c true, the substitution map will bind + /// outer local generic parameters to archetypes. Otherwise they will be left + /// unchanged. + SubstitutionMap getSubstitutionMap(bool wantContextualType = false) const; /// Get the direct generic arguments, which correspond to the generic /// arguments that are directly applied to the typealias declaration /// this type references. ArrayRef getDirectGenericArgs() const { - return ArrayRef( - getTrailingObjects() + - (Bits.TypeAliasType.HasParent ? 1 : 0), - Bits.TypeAliasType.GenericArgCount); + return ArrayRef(getTrailingObjects() + + (Bits.TypeAliasType.HasParent ? 1 : 0), + Bits.TypeAliasType.GenericArgCount); } SmallVector getExpandedGenericArgs(); @@ -2704,16 +2748,16 @@ class TupleType final : public TypeBase, public llvm::FoldingSetNode, /// getElements - Return the elements of this tuple. ArrayRef getElements() const { - return {getTrailingObjects(), getNumElements()}; + return getTrailingObjects(getNumElements()); } const TupleTypeElt &getElement(unsigned i) const { - return getTrailingObjects()[i]; + return getTrailingObjects()[i]; } /// getElementType - Return the type of the specified element. Type getElementType(unsigned ElementNo) const { - return getTrailingObjects()[ElementNo].getType(); + return getTrailingObjects()[ElementNo].getType(); } TupleEltTypeArrayRef getElementTypes() const { @@ -2743,7 +2787,7 @@ class TupleType final : public TypeBase, public llvm::FoldingSetNode, : TypeBase(TypeKind::Tuple, CanCtx, properties) { Bits.TupleType.Count = elements.size(); std::uninitialized_copy(elements.begin(), elements.end(), - getTrailingObjects()); + getTrailingObjects()); } }; BEGIN_CAN_TYPE_WRAPPER(TupleType, Type) @@ -3307,7 +3351,8 @@ END_CAN_TYPE_WRAPPER(DynamicSelfType, Type) /// represented at the binary level as a single function pointer. class AnyFunctionType : public TypeBase { const Type Output; - + uint16_t NumParams; + public: using Representation = FunctionTypeRepresentation; @@ -3568,16 +3613,16 @@ class AnyFunctionType : public TypeBase { Bits.AnyFunctionType.HasThrownError = false; Bits.AnyFunctionType.HasLifetimeDependencies = false; } - Bits.AnyFunctionType.NumParams = NumParams; - assert(Bits.AnyFunctionType.NumParams == NumParams && "Params dropped!"); + this->NumParams = NumParams; + assert(this->NumParams == NumParams && "Params dropped!"); - if (Info) { + if (Info && CONDITIONAL_ASSERT_enabled()) { unsigned maxLifetimeTarget = NumParams + 1; if (auto outputFn = Output->getAs()) { maxLifetimeTarget += outputFn->getNumParams(); } for (auto &dep : Info->getLifetimeDependencies()) { - assert(dep.getTargetIndex() < maxLifetimeTarget); + ASSERT(dep.getTargetIndex() < maxLifetimeTarget); } } } @@ -3607,7 +3652,7 @@ class AnyFunctionType : public TypeBase { Type getResult() const { return Output; } ArrayRef getParams() const; - unsigned getNumParams() const { return Bits.AnyFunctionType.NumParams; } + unsigned getNumParams() const { return NumParams; } GenericSignature getOptGenericSignature() const; @@ -3984,7 +4029,7 @@ class FunctionType final ArrayRef getLifetimeDependencies() const { if (!hasLifetimeDependencies()) - return std::nullopt; + return {}; return {getTrailingObjects(), getNumLifetimeDependencies()}; } @@ -4037,6 +4082,7 @@ struct ParameterListInfo { SmallBitVector propertyWrappers; SmallBitVector implicitSelfCapture; SmallBitVector inheritActorContext; + SmallBitVector alwaysInheritActorContext; SmallBitVector variadicGenerics; SmallBitVector sendingParameters; @@ -4063,7 +4109,8 @@ struct ParameterListInfo { /// Whether the given parameter is a closure that should inherit the /// actor context from the context in which it was created. - bool inheritsActorContext(unsigned paramIdx) const; + std::pair + inheritsActorContext(unsigned paramIdx) const; bool isVariadicGenericParameter(unsigned paramIdx) const; @@ -4152,7 +4199,7 @@ class GenericFunctionType final ArrayRef getLifetimeDependencies() const { if (!hasLifetimeDependencies()) - return std::nullopt; + return {}; return {getTrailingObjects(), getNumLifetimeDependencies()}; } @@ -4733,7 +4780,7 @@ enum class ResultConvention : uint8_t { /// The validity of the return value is dependent on the 'self' parameter, /// so it may be invalidated if that parameter is released. UnownedInnerPointer, - + /// This value has been (or may have been) returned autoreleased. /// The caller should make an effort to reclaim the autorelease. /// The type must be a class or class existential type, and this @@ -4745,6 +4792,14 @@ enum class ResultConvention : uint8_t { /// depending on the pact type). The callee is responsible for /// leaving an initialized object in each element of the pack. Pack, + + /// The caller is responsible for using the returned address within a valid + /// scope. This is valid only for borrow and mutate accessors. + GuaranteedAddress, + + /// The caller is responsible for using the returned value within a valid + /// scope. This is valid only for borrow accessors. + Guaranteed, }; // Does this result require indirect storage for the purpose of reabstraction? @@ -4901,6 +4956,14 @@ class SILResultInfo { return getConvention() == ResultConvention::Pack; } + bool isGuaranteedAddressResult() const { + return getConvention() == ResultConvention::GuaranteedAddress; + } + + bool isGuaranteedResult() const { + return getConvention() == ResultConvention::Guaranteed; + } + /// Transform this SILResultInfo by applying the user-provided /// function to its type. /// @@ -5022,12 +5085,20 @@ enum class SILCoroutineKind : uint8_t { class SILFunctionConventions; +Type substOpaqueTypesWithUnderlyingTypes(Type type, + TypeExpansionContext context); CanType substOpaqueTypesWithUnderlyingTypes(CanType type, TypeExpansionContext context); + ProtocolConformanceRef -substOpaqueTypesWithUnderlyingTypes(ProtocolConformanceRef ref, Type origType, +substOpaqueTypesWithUnderlyingTypes(ProtocolConformanceRef ref, + TypeExpansionContext context); + +SubstitutionMap +substOpaqueTypesWithUnderlyingTypes(SubstitutionMap subs, TypeExpansionContext context); + namespace Lowering { class TypeConverter; } @@ -5188,12 +5259,12 @@ class SILFunctionType final /// Given an existing ExtInfo, and a set of interface parameters and results /// destined for a new SILFunctionType, return a new ExtInfo with only the /// lifetime dependencies relevant after substitution. - static ExtInfo - getSubstLifetimeDependencies(GenericSignature genericSig, - ExtInfo origExtInfo, - ArrayRef params, - ArrayRef yields, - ArrayRef results); + static ExtInfo getSubstLifetimeDependencies(GenericSignature genericSig, + ExtInfo origExtInfo, + ASTContext &context, + ArrayRef params, + ArrayRef yields, + ArrayRef results); /// Return a structurally-identical function type with a slightly tweaked /// ExtInfo. @@ -5333,6 +5404,20 @@ class SILFunctionType final return hasErrorResult() && getErrorResult().isFormalIndirect(); } + bool hasGuaranteedResult() const { + if (getNumResults() != 1) { + return false; + } + return getResults()[0].isGuaranteedResult(); + } + + bool hasGuaranteedAddressResult() const { + if (getNumResults() != 1) { + return false; + } + return getResults()[0].isGuaranteedAddressResult(); + } + struct IndirectFormalResultFilter { bool operator()(SILResultInfo result) const { return result.isFormalIndirect(); @@ -5450,6 +5535,10 @@ class SILFunctionType final return getParameters().back(); } + unsigned getSelfParameterIndex() const { + return NumParameters - 1; + } + /// Return SILParameterInfo for the isolated parameter in this SILFunctionType /// if one exists. Returns None otherwise. std::optional maybeGetIsolatedParameter() const { @@ -5632,7 +5721,7 @@ class SILFunctionType final // relative to the original FunctionType. ArrayRef getLifetimeDependencies() const { if (!hasLifetimeDependencies()) - return std::nullopt; + return {}; return {getTrailingObjects(), NumLifetimeDependencies}; } @@ -5647,6 +5736,24 @@ class SILFunctionType final return getLifetimeDependenceFor(getNumParameters()); } + /// Return true of the specified parameter is addressable based on its type + /// lowering in 'caller's context. This includes @_addressableForDependencies + /// parameter types. + /// + /// Defined in SILType.cpp. + bool isAddressable(unsigned paramIdx, SILFunction *caller); + + /// Return true of the specified parameter is addressable based on its type + /// lowering. This includes @_addressableForDependencies parameter types. + /// + /// 'genericEnv' may be null. + /// + /// Defined in SILType.cpp. + bool isAddressable(unsigned paramIdx, SILModule &module, + GenericEnvironment *genericEnv, + Lowering::TypeConverter &typeConverter, + TypeExpansionContext expansion); + /// 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). @@ -6035,7 +6142,7 @@ class SILMoveOnlyWrappedType final : public TypeBase, innerType(innerType) { // If it has a type parameter, we can't check whether it's copyable. assert(innerType->hasTypeParameter() || - !innerType->isNoncopyable() && "Inner type must be copyable"); + innerType->isCopyable() && "Inner type must be copyable"); } public: @@ -6125,7 +6232,7 @@ class SILPackType final : public TypeBase, public llvm::FoldingSetNode, : TypeBase(TypeKind::SILPack, &ctx, properties) { Bits.SILPackType.Count = elements.size(); Bits.SILPackType.ElementIsAddress = info.ElementIsAddress; - memcpy(getTrailingObjects(), elements.data(), + memcpy(getTrailingObjects(), elements.data(), elements.size() * sizeof(CanType)); } @@ -6147,13 +6254,13 @@ class SILPackType final : public TypeBase, public llvm::FoldingSetNode, /// Retrieves the type of the elements in the pack. ArrayRef getElementTypes() const { - return {getTrailingObjects(), getNumElements()}; + return getTrailingObjects(getNumElements()); } /// Returns the type of the element at the given \p index. /// This is a lowered SIL type. CanType getElementType(unsigned index) const { - return getTrailingObjects()[index]; + return getTrailingObjects()[index]; } SILType getSILElementType(unsigned index) const; // in SILType.h @@ -6255,7 +6362,7 @@ class ArraySliceType : public UnarySyntaxSugarType { } }; -/// An InlineArray type e.g `[2 x Foo]`, sugar for `InlineArray<2, Foo>`. +/// An InlineArray type e.g `[2 of Foo]`, sugar for `InlineArray<2, Foo>`. class InlineArrayType : public SyntaxSugarType { Type Count; Type Elt; @@ -6448,7 +6555,8 @@ class ProtocolCompositionType final : public TypeBase, /// a protocol composition type; you also have to look at /// hasExplicitAnyObject(). ArrayRef getMembers() const { - return {getTrailingObjects(), static_cast(Bits.ProtocolCompositionType.Count)}; + return getTrailingObjects( + static_cast(Bits.ProtocolCompositionType.Count)); } InvertibleProtocolSet getInverses() const { return Inverses; } @@ -6496,7 +6604,7 @@ class ProtocolCompositionType final : public TypeBase, Bits.ProtocolCompositionType.HasExplicitAnyObject = hasExplicitAnyObject; Bits.ProtocolCompositionType.Count = members.size(); std::uninitialized_copy(members.begin(), members.end(), - getTrailingObjects()); + getTrailingObjects()); } }; BEGIN_CAN_TYPE_WRAPPER(ProtocolCompositionType, Type) @@ -6545,8 +6653,8 @@ class ParameterizedProtocolType final : public TypeBase, } ArrayRef getArgs() const { - return {getTrailingObjects(), - static_cast(Bits.ParameterizedProtocolType.ArgCount)}; + return getTrailingObjects( + static_cast(Bits.ParameterizedProtocolType.ArgCount)); } bool requiresClass() const { @@ -6900,7 +7008,7 @@ class ArchetypeType : public SubstitutableType, } protected: ArchetypeType(TypeKind Kind, - const ASTContext &C, + const ASTContext *C, RecursiveTypeProperties properties, Type InterfaceType, ArrayRef ConformsTo, @@ -6979,7 +7087,8 @@ class OpaqueTypeArchetypeType final : public ArchetypeType, } private: - OpaqueTypeArchetypeType(GenericEnvironment *environment, + OpaqueTypeArchetypeType(const ASTContext *ctx, + GenericEnvironment *environment, RecursiveTypeProperties properties, Type interfaceType, ArrayRef conformsTo, @@ -7011,31 +7120,28 @@ enum class OpaqueSubstitutionKind { /// archetypes with underlying types visible at a given resilience expansion /// to their underlying types. class ReplaceOpaqueTypesWithUnderlyingTypes { -public: - using SeenDecl = std::pair; private: - ResilienceExpansion contextExpansion; - llvm::PointerIntPair inContextAndIsWholeModule; - llvm::DenseSet *seenDecls; + const DeclContext *inContext; + unsigned contextExpansion : 1; + bool isWholeModule : 1; + bool typeCheckFunctionBodies : 1; public: ReplaceOpaqueTypesWithUnderlyingTypes(const DeclContext *inContext, ResilienceExpansion contextExpansion, - bool isWholeModuleContext) - : contextExpansion(contextExpansion), - inContextAndIsWholeModule(inContext, isWholeModuleContext), - seenDecls(nullptr) {} - - ReplaceOpaqueTypesWithUnderlyingTypes( - const DeclContext *inContext, ResilienceExpansion contextExpansion, - bool isWholeModuleContext, llvm::DenseSet &seen); + bool isWholeModule, + bool typeCheckFunctionBodies=true) + : inContext(inContext), + contextExpansion(unsigned(contextExpansion)), + isWholeModule(isWholeModule), + typeCheckFunctionBodies(typeCheckFunctionBodies) {} /// TypeSubstitutionFn Type operator()(SubstitutableType *maybeOpaqueType) const; /// LookupConformanceFn - ProtocolConformanceRef operator()(CanType maybeOpaqueType, - Type replacementType, + ProtocolConformanceRef operator()(InFlightSubstitution &IFS, + Type maybeOpaqueType, ProtocolDecl *protocol) const; OpaqueSubstitutionKind @@ -7044,13 +7150,32 @@ class ReplaceOpaqueTypesWithUnderlyingTypes { static OpaqueSubstitutionKind shouldPerformSubstitution(OpaqueTypeDecl *opaque, ModuleDecl *contextModule, ResilienceExpansion contextExpansion); +}; +/// A function object that can be used as a \c TypeSubstitutionFn and +/// \c LookupConformanceFn for \c Type::subst style APIs to map existential +/// archetypes in the given generic environment to known concrete types from +/// the given substitution map. +class ReplaceExistentialArchetypesWithConcreteTypes { private: - const DeclContext *getContext() const { - return inContextAndIsWholeModule.getPointer(); - } + GenericEnvironment *env; + SubstitutionMap subs; + + Type getInterfaceType(ExistentialArchetypeType *type) const; + +public: + ReplaceExistentialArchetypesWithConcreteTypes(GenericEnvironment *env, + SubstitutionMap subs) + : env(env), subs(subs) {} + + /// TypeSubstitutionFn + Type operator()(SubstitutableType *type) const; + + /// LookupConformanceFn + ProtocolConformanceRef operator()(InFlightSubstitution &IFS, + Type origType, + ProtocolDecl *protocol) const; - bool isWholeModule() const { return inContextAndIsWholeModule.getInt(); } }; /// An archetype that's only valid in a portion of a local context. @@ -7107,11 +7232,13 @@ class ExistentialArchetypeType final : public LocalArchetypeType, } private: - ExistentialArchetypeType(GenericEnvironment *environment, Type interfaceType, - ArrayRef conformsTo, - Type superclass, - LayoutConstraint layout, - RecursiveTypeProperties properties); + ExistentialArchetypeType(const ASTContext *ctx, + GenericEnvironment *environment, + Type interfaceType, + ArrayRef conformsTo, + Type superclass, + LayoutConstraint layout, + RecursiveTypeProperties properties); }; BEGIN_CAN_TYPE_WRAPPER(ExistentialArchetypeType, LocalArchetypeType) END_CAN_TYPE_WRAPPER(ExistentialArchetypeType, LocalArchetypeType) @@ -7184,7 +7311,7 @@ class ElementArchetypeType final : public LocalArchetypeType, } private: - ElementArchetypeType(const ASTContext &ctx, + ElementArchetypeType(const ASTContext *ctx, GenericEnvironment *environment, Type interfaceType, ArrayRef conformsTo, Type superclass, LayoutConstraint layout); @@ -7225,9 +7352,10 @@ class GenericTypeParamType : public SubstitutableType, Identifier Name; }; - unsigned Depth : 15; unsigned IsDecl : 1; - unsigned Index : 16; + unsigned Depth : 15; + unsigned Weight : 1; + unsigned Index : 15; /// The kind of generic type parameter this is. GenericTypeParamKind ParamKind; @@ -7252,15 +7380,21 @@ class GenericTypeParamType : public SubstitutableType, Type valueType, const ASTContext &ctx); /// Retrieve a canonical generic type parameter with the given kind, depth, - /// index, and optional value type. + /// index, weight, and optional value type. static GenericTypeParamType *get(GenericTypeParamKind paramKind, - unsigned depth, unsigned index, + unsigned depth, unsigned index, unsigned weight, Type valueType, const ASTContext &ctx); - /// Retrieve a canonical generic type parameter at the given depth and index. + /// Retrieve a canonical generic type parameter at the given depth and index, + /// with weight 0. static GenericTypeParamType *getType(unsigned depth, unsigned index, const ASTContext &ctx); + /// Retrieve a canonical generic type parameter at the given depth and index + /// for an opaque result type, so with weight 1. + static GenericTypeParamType *getOpaqueResultType(unsigned depth, unsigned index, + const ASTContext &ctx); + /// Retrieve a canonical generic parameter pack at the given depth and index. static GenericTypeParamType *getPack(unsigned depth, unsigned index, const ASTContext &ctx); @@ -7316,6 +7450,14 @@ class GenericTypeParamType : public SubstitutableType, return Index; } + /// The weight of this generic parameter in the type parameter order. + /// + /// Opaque result types have weight 1, while all other generic parameters + /// have weight 0. + unsigned getWeight() const { + return Weight; + } + /// Returns \c true if this type parameter is declared as a pack. /// /// \code @@ -7337,20 +7479,24 @@ class GenericTypeParamType : public SubstitutableType, Type getValueType() const; + GenericTypeParamType *withDepth(unsigned depth) const; + void Profile(llvm::FoldingSetNodeID &ID) { // Note: We explicitly don't use 'getName()' because for canonical forms // which don't store an identifier we'll go create a tau based form. We // really want to just plumb down the null Identifier because that's what's // inside the cache. - Profile(ID, getParamKind(), getDepth(), getIndex(), getValueType(), - Name); + Profile(ID, getParamKind(), getDepth(), getIndex(), getWeight(), + getValueType(), Name); } static void Profile(llvm::FoldingSetNodeID &ID, GenericTypeParamKind paramKind, unsigned depth, - unsigned index, Type valueType, Identifier name) { + unsigned index, unsigned weight, Type valueType, + Identifier name) { ID.AddInteger((uint8_t)paramKind); ID.AddInteger(depth); ID.AddInteger(index); + ID.AddInteger(weight); ID.AddPointer(valueType.getPointer()); ID.AddPointer(name.get()); } @@ -7373,7 +7519,7 @@ class GenericTypeParamType : public SubstitutableType, const ASTContext &ctx); explicit GenericTypeParamType(GenericTypeParamKind paramKind, unsigned depth, - unsigned index, Type valueType, + unsigned index, unsigned weight, Type valueType, RecursiveTypeProperties props, const ASTContext &ctx); }; @@ -7383,6 +7529,11 @@ static CanGenericTypeParamType getType(unsigned depth, unsigned index, return CanGenericTypeParamType( GenericTypeParamType::getType(depth, index, C)); } +static CanGenericTypeParamType getOpaqueResultType(unsigned depth, unsigned index, + const ASTContext &C) { + return CanGenericTypeParamType( + GenericTypeParamType::getOpaqueResultType(depth, index, C)); +} END_CAN_TYPE_WRAPPER(GenericTypeParamType, SubstitutableType) /// A type that refers to a member type of some type that is dependent on a @@ -7587,8 +7738,7 @@ class ErrorUnionType final RecursiveTypeProperties properties) : TypeBase(TypeKind::ErrorUnion, /*Context=*/ctx, properties) { Bits.ErrorUnionType.NumTerms = terms.size(); - std::uninitialized_copy(terms.begin(), terms.end(), - getTrailingObjects()); + std::uninitialized_copy(terms.begin(), terms.end(), getTrailingObjects()); } public: @@ -7596,7 +7746,8 @@ class ErrorUnionType final static Type get(const ASTContext &ctx, ArrayRef terms); ArrayRef getTerms() const { - return { getTrailingObjects(), static_cast(Bits.ErrorUnionType.NumTerms) }; + return getTrailingObjects( + static_cast(Bits.ErrorUnionType.NumTerms)); }; // Support for FoldingSet. @@ -7613,16 +7764,16 @@ class ErrorUnionType final }; DEFINE_EMPTY_CAN_TYPE_WRAPPER(ErrorUnionType, Type) -/// PlaceholderType - This represents a placeholder type for a type variable -/// or dependent member type that cannot be resolved to a concrete type -/// because the expression is ambiguous. This type is only used by the -/// constraint solver and transformed into UnresolvedType to be used in AST. +/// PlaceholderType - In the AST, this represents the type of a placeholder `_`. +/// In the constraint system, this is opened into a type variable, and uses of +/// PlaceholderType are instead used to represent holes where types cannot be +/// inferred. class PlaceholderType : public TypeBase { // NOTE: If you add a new Type-based originator, you'll need to update the // recursive property logic in PlaceholderType::get. using Originator = - llvm::PointerUnion; + llvm::PointerUnion; Originator O; @@ -7688,12 +7839,12 @@ class PackType final : public TypeBase, public llvm::FoldingSetNode, /// Retrieves the type of the elements in the pack. ArrayRef getElementTypes() const { - return {getTrailingObjects(), getNumElements()}; + return getTrailingObjects(getNumElements()); } /// Returns the type of the element at the given \p index. Type getElementType(unsigned index) const { - return getTrailingObjects()[index]; + return getTrailingObjects()[index]; } bool containsPackExpansionType() const; @@ -7719,7 +7870,7 @@ class PackType final : public TypeBase, public llvm::FoldingSetNode, : TypeBase(TypeKind::Pack, CanCtx, properties) { Bits.PackType.Count = elements.size(); std::uninitialized_copy(elements.begin(), elements.end(), - getTrailingObjects()); + getTrailingObjects()); } }; BEGIN_CAN_TYPE_WRAPPER(PackType, Type) @@ -7908,10 +8059,27 @@ class IntegerType final : public TypeBase, public llvm::FoldingSetNode { friend class ASTContext; StringRef Value; + // Integers may not be canonical, but don't have any structural type + // components from which to get the ASTContext, so we need to store a + // reference to it ourselves. + const ASTContext &Context; + + static const ASTContext * + getCanonicalIntegerLiteralContext(StringRef value, const ASTContext &ctx) { + for (char c : value) { + // A canonical integer literal consists only of ASCII decimal digits. + if (c < '0' || c > '9') { + return nullptr; + } + } + return &ctx; + } IntegerType(StringRef value, bool isNegative, const ASTContext &ctx) : - TypeBase(TypeKind::Integer, &ctx, RecursiveTypeProperties()), - Value(value) { + TypeBase(TypeKind::Integer, getCanonicalIntegerLiteralContext(value, ctx), + RecursiveTypeProperties()), + Value(value), + Context(ctx) { Bits.IntegerType.IsNegative = isNegative; } @@ -7941,6 +8109,8 @@ class IntegerType final : public TypeBase, public llvm::FoldingSetNode { static bool classof(const TypeBase *T) { return T->getKind() == TypeKind::Integer; } + + const ASTContext &getASTContext() { return Context; } }; DEFINE_EMPTY_CAN_TYPE_WRAPPER(IntegerType, Type) @@ -7960,6 +8130,17 @@ inline ASTContext &TypeBase::getASTContext() const { return *const_cast(getCanonicalType()->Context); } +inline bool TypeBase::isBareErrorType() const { + auto *errTy = dyn_cast(this); + if (!errTy) + return false; + + // FIXME: We shouldn't need to check for a recursive bare error type, we can + // remove this once we flatten them. + auto originalTy = errTy->getOriginalType(); + return !originalTy || originalTy->isBareErrorType(); +} + // TODO: This will become redundant once InOutType is removed. inline bool TypeBase::isMaterializable() { return !(hasLValueType() || is()); @@ -8095,6 +8276,10 @@ inline GenericTypeDecl *TypeBase::getAnyGeneric() { return getCanonicalType().getAnyGeneric(); } +inline bool TypeBase::isBuiltinType() { + return isa(getCanonicalType()); +} + inline bool TypeBase::isBuiltinIntegerType(unsigned n) { if (auto intTy = dyn_cast(getCanonicalType())) return intTy->getWidth().isFixedWidth() @@ -8184,11 +8369,11 @@ inline ParameterTypeFlags ParameterTypeFlags::fromParameterType( inline const Type *BoundGenericType::getTrailingObjectsPointer() const { if (auto ty = dyn_cast(this)) - return ty->getTrailingObjects(); + return ty->getTrailingObjects(); if (auto ty = dyn_cast(this)) - return ty->getTrailingObjects(); + return ty->getTrailingObjects(); if (auto ty = dyn_cast(this)) - return ty->getTrailingObjects(); + return ty->getTrailingObjects(); llvm_unreachable("Unhandled BoundGenericType!"); } diff --git a/include/swift/AST/USRGeneration.h b/include/swift/AST/USRGeneration.h index 07e069b70aa38..61095c43e2a10 100644 --- a/include/swift/AST/USRGeneration.h +++ b/include/swift/AST/USRGeneration.h @@ -45,9 +45,20 @@ bool printDeclTypeUSR(const ValueDecl *D, raw_ostream &OS); /// Prints out the USR for the given ValueDecl. /// @param distinguishSynthesizedDecls Whether to use the USR of the /// synthesized declaration instead of the USR of the underlying Clang USR. +/// @param useSwiftUSR Whether to generate a Swift USR for all Clang +/// declarations as well. /// \returns true if it failed, false on success. bool printValueDeclUSR(const ValueDecl *D, raw_ostream &OS, - bool distinguishSynthesizedDecls = false); + bool distinguishSynthesizedDecls = false, + bool useSwiftUSR = false); + +/// Prints out the Swift USR for the given ValueDecl regardless of its source +/// (Swift or Clang). Equivalent to `printValueDeclUSR(D, OS, false, +/// /*useSwiftUSR=*/true)` +inline bool printValueDeclSwiftUSR(const ValueDecl *D, raw_ostream &OS) { + return printValueDeclUSR(D, OS, /*distinguishSynthesizedDecls=*/false, + /*useSwiftUSR=*/true); +} /// Prints out the USR for the given ModuleEntity. /// In case module aliasing is used, it prints the real module name. For example, diff --git a/include/swift/AST/UnsafeUse.h b/include/swift/AST/UnsafeUse.h index 70f8f2ecc2aec..bed9a9c0d328d 100644 --- a/include/swift/AST/UnsafeUse.h +++ b/include/swift/AST/UnsafeUse.h @@ -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 - 2025 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 @@ -53,6 +53,8 @@ class UnsafeUse { ReferenceToUnsafeThroughTypealias, /// A call to an unsafe declaration. CallToUnsafe, + /// An unsafe argument in a call. + CallArgument, /// A @preconcurrency import. PreconcurrencyImport, /// A use of withoutActuallyEscaping that lacks enforcement that the @@ -91,6 +93,15 @@ class UnsafeUse { MakeTemporarilyEscapableExpr *temporarilyEscaping; + struct { + Expr *call; + const Decl *calleeDecl; + TypeBase *paramType; + const void *argumentName; + unsigned argumentIndex; + Expr *argument; + } callArgument; + const ImportDecl *importDecl; } storage; @@ -201,6 +212,19 @@ class UnsafeUse { decl, type, location); } + static UnsafeUse forCallArgument( + Expr *call, const Decl *calleeDecl, Type paramType, + Identifier argumentName, unsigned argumentIndex, Expr *argument) { + UnsafeUse result(CallArgument); + result.storage.callArgument.call = call; + result.storage.callArgument.calleeDecl = calleeDecl; + result.storage.callArgument.paramType = paramType.getPointer(); + result.storage.callArgument.argumentName = argumentName.getAsOpaquePointer(); + result.storage.callArgument.argumentIndex = argumentIndex; + result.storage.callArgument.argument = argument; + return result; + } + static UnsafeUse forTemporarilyEscaping(MakeTemporarilyEscapableExpr *expr) { UnsafeUse result(TemporarilyEscaping); result.storage.temporarilyEscaping = expr; @@ -223,14 +247,12 @@ class UnsafeUse { return getDecl()->getLoc(); case UnsafeConformance: - return SourceLoc( - llvm::SMLoc::getFromPointer( - (const char *)storage.conformance.location)); + return SourceLoc::getFromPointer( + (const char *)storage.conformance.location); case TypeWitness: - return SourceLoc( - llvm::SMLoc::getFromPointer( - (const char *)storage.typeWitness.location)); + return SourceLoc::getFromPointer( + (const char *)storage.typeWitness.location); case UnownedUnsafe: case ExclusivityUnchecked: @@ -239,8 +261,10 @@ class UnsafeUse { case ReferenceToUnsafeStorage: case ReferenceToUnsafeThroughTypealias: case CallToUnsafe: - return SourceLoc( - llvm::SMLoc::getFromPointer((const char *)storage.entity.location)); + return SourceLoc::getFromPointer((const char *)storage.entity.location); + + case CallArgument: + return storage.callArgument.call->getLoc(); case TemporarilyEscaping: return storage.temporarilyEscaping->getLoc(); @@ -257,6 +281,7 @@ class UnsafeUse { case Witness: case TemporarilyEscaping: case PreconcurrencyImport: + case CallArgument: // Cannot replace location. return; @@ -298,6 +323,9 @@ class UnsafeUse { case CallToUnsafe: return storage.entity.decl; + case CallArgument: + return storage.callArgument.calleeDecl; + case UnsafeConformance: case TemporarilyEscaping: return nullptr; @@ -330,6 +358,7 @@ class UnsafeUse { case ReferenceToUnsafeThroughTypealias: case ReferenceToUnsafeStorage: case CallToUnsafe: + case CallArgument: case UnsafeConformance: case PreconcurrencyImport: case TemporarilyEscaping: @@ -360,6 +389,9 @@ class UnsafeUse { case CallToUnsafe: return storage.entity.type; + case CallArgument: + return storage.callArgument.paramType; + case TemporarilyEscaping: return storage.temporarilyEscaping->getOpaqueValue()->getType(); } @@ -386,11 +418,24 @@ class UnsafeUse { case ReferenceToUnsafeStorage: case ReferenceToUnsafeThroughTypealias: case CallToUnsafe: + case CallArgument: case TemporarilyEscaping: case PreconcurrencyImport: return ProtocolConformanceRef::forInvalid(); } } + + /// Get information about the call argument. + /// + /// Produces the argument name, argument index, and argument expression for + /// a unsafe use describing a call argument. + std::tuple getCallArgument() const { + assert(getKind() == CallArgument); + return std::make_tuple( + Identifier::getFromOpaquePointer(storage.callArgument.argumentName), + storage.callArgument.argumentIndex, + storage.callArgument.argument); + } }; } // end namespace swift diff --git a/include/swift/Basic/Assertions.h b/include/swift/Basic/Assertions.h index d8155add80baf..37b83d4fb8cd4 100644 --- a/include/swift/Basic/Assertions.h +++ b/include/swift/Basic/Assertions.h @@ -17,6 +17,8 @@ #ifndef SWIFT_BASIC_ASSERTIONS_H #define SWIFT_BASIC_ASSERTIONS_H +#include "swift/Basic/LLVM.h" + // Only for use in this header #if __has_builtin(__builtin_expect) #define ASSERT_UNLIKELY(expression) (__builtin_expect(!!(expression), 0)) @@ -24,6 +26,13 @@ #define ASSERT_UNLIKELY(expression) ((expression)) #endif +// Visual Studio doesn't have __FILE_NAME__ +#ifdef __FILE_NAME__ +#define _FILENAME_FOR_ASSERT __FILE_NAME__ +#else +#define _FILENAME_FOR_ASSERT __FILE__ +#endif + // ================================ Mandatory Asserts ================================ // `ASSERT(expr)`: @@ -41,27 +50,13 @@ // that are more expensive than you think. You can switch those to // `CONDITIONAL_ASSERT` or `DEBUG_ASSERT` as needed. -// Visual Studio doesn't have __FILE_NAME__ -#ifdef __FILE_NAME__ - -#define ASSERT(expr) \ - do { \ - if (ASSERT_UNLIKELY(!(expr))) { \ - ASSERT_failure(#expr, __FILE_NAME__, __LINE__, __func__); \ - } \ +#define ASSERT(expr) \ + do { \ + if (ASSERT_UNLIKELY(!(expr))) { \ + ASSERT_failure(#expr, _FILENAME_FOR_ASSERT, __LINE__, __func__); \ + } \ } while (0) -#else - -#define ASSERT(expr) \ - do { \ - if (ASSERT_UNLIKELY(!(expr))) { \ - ASSERT_failure(#expr, __FILE__, __LINE__, __func__); \ - } \ - } while (0) - -#endif - // Function that reports the actual failure when it occurs. void ASSERT_failure(const char *expr, const char *file, int line, const char *func); @@ -190,11 +185,33 @@ extern int CONDITIONAL_ASSERT_Global_enable_flag; #define SWIFT_ASSERT_ONLY_DECL DEBUG_ASSERT_DECL #define SWIFT_ASSERT_ONLY DEBUG_ASSERT_EXPR -// ================================ Utility and Helper Functions ================================ +// ================================ Abort ====================================== -// Utility function to print out help information for -// various command-line options that affect the assertion -// behavior. -void ASSERT_help(); +/// Implementation for \c ABORT, not to be used directly. +[[noreturn]] +void _ABORT(const char *file, int line, const char *func, + llvm::function_ref message); + +/// Implementation for \c ABORT, not to be used directly. +[[noreturn]] +void _ABORT(const char *file, int line, const char *func, + llvm::StringRef message); + +// Aborts the program, printing a given message to a PrettyStackTrace frame +// before exiting. This should be preferred over manually logging to stderr and +// `abort()`'ing since that won't be picked up by the crash reporter. +// +// There are two different forms of ABORT: +// +// ``` +// ABORT("abort with string"); +// +// ABORT([&](auto &out) { +// out << "abort with arbitrary stream"; +// node.dump(out); +// }); +// ``` +// +#define ABORT(arg) _ABORT(_FILENAME_FOR_ASSERT, __LINE__, __func__, (arg)) #endif // SWIFT_BASIC_ASSERTIONS_H diff --git a/include/swift/Basic/BasicBridging.h b/include/swift/Basic/BasicBridging.h index 36ea6df43908f..f0b1073e51b0b 100644 --- a/include/swift/Basic/BasicBridging.h +++ b/include/swift/Basic/BasicBridging.h @@ -13,61 +13,40 @@ #ifndef SWIFT_BASIC_BASICBRIDGING_H #define SWIFT_BASIC_BASICBRIDGING_H -#if !defined(COMPILED_WITH_SWIFT) || !defined(PURE_BRIDGING_MODE) -#define USED_IN_CPP_SOURCE -#endif - -// Do not add other C++/llvm/swift header files here! -// Function implementations should be placed into BasicBridging.cpp and required header files should be added there. -// -// Pure bridging mode does not permit including any C++/llvm/swift headers. -// See also the comments for `BRIDGING_MODE` in the top-level CMakeLists.txt file. -// +// Do not add other std/llvm header files here! +// Function implementations should be placed into BasicBridging.cpp and +// required header files should be added there. // -// Note: On Windows ARM64, how a C++ struct/class value type is -// returned is sensitive to conditions including whether a -// user-defined constructor exists, etc. See -// https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#return-values -// So, if a C++ struct/class type is returned as a value between Swift -// and C++, we need to be careful to match the return convention -// matches between the non-USED_IN_CPP_SOURCE (Swift) side and the -// USE_IN_CPP_SOURCE (C++) side. +// Pure bridging mode does not permit including any std/llvm headers. +// See also the comments for `BRIDGING_MODE` in the top-level CMakeLists.txt +// file. // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// !! Do not put any constructors inside an `#ifdef USED_IN_CPP_SOURCE` block !! +// !! Do not put any constructors inside an !! +// !! `#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE` block !! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -#include "swift/Basic/BridgedSwiftObject.h" -#include "swift/Basic/Compiler.h" +/// This header defines `NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE`, so it +/// needs to be imported before the first use of the macro. +#include "swift/Basic/SwiftBridging.h" +#include "swift/Basic/BridgedSwiftObject.h" +#include "swift/Basic/SourceLoc.h" #include #include -#ifdef USED_IN_CPP_SOURCE + +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE // Workaround to avoid a compiler error because `cas::ObjectRef` is not defined // when including VirtualFileSystem.h #include #include "llvm/CAS/CASReference.h" -#include "swift/Basic/SourceLoc.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/APInt.h" #include #include #endif -// FIXME: We ought to be importing '' instead. -#if __has_attribute(swift_name) -#define SWIFT_NAME(NAME) __attribute__((swift_name(NAME))) -#else -#define SWIFT_NAME(NAME) -#endif - -#if __has_attribute(availability) -#define SWIFT_UNAVAILABLE(msg) \ - __attribute__((availability(swift, unavailable, message=msg))) -#else -#define SWIFT_UNAVAILABLE(msg) -#endif - #ifdef PURE_BRIDGING_MODE // In PURE_BRIDGING_MODE, briding functions are not inlined #define BRIDGED_INLINE @@ -82,6 +61,7 @@ class VersionTuple; } // end namespace llvm namespace swift { +class LangOptions; class SourceLoc; class SourceRange; class CharSourceRange; @@ -110,13 +90,10 @@ typedef uintptr_t SwiftUInt; \ SWIFT_UNAVAILABLE("Use '.raw' instead") \ Qualifier Node *Nullability unbridged() const { return Ptr; } \ - }; \ \ - SWIFT_NAME("getter:Bridged" #Name ".raw(self:)") \ - inline Qualifier void *Nullability Bridged##Name##_getRaw( \ - Bridged##Name bridged) { \ - return bridged.unbridged(); \ - } \ + SWIFT_COMPUTED_PROPERTY \ + Qualifier void *Nullability getRaw() const { return unbridged(); }; \ + }; \ \ SWIFT_NAME("Bridged" #Name ".init(raw:)") \ inline Bridged##Name Bridged##Name##_fromRaw( \ @@ -140,6 +117,10 @@ typedef uintptr_t SwiftUInt; void assertFail(const char * _Nonnull msg, const char * _Nonnull file, SwiftUInt line, const char * _Nonnull function); +BRIDGED_INLINE +SWIFT_UNAVAILABLE("Unavailable in Swift") +void ASSERT_inBridgingHeader(bool condition); + //===----------------------------------------------------------------------===// // MARK: ArrayRef //===----------------------------------------------------------------------===// @@ -158,7 +139,7 @@ class BridgedArrayRef { BridgedArrayRef(const void *_Nullable data, size_t length) : Data(data), Length(length) {} -#ifdef USED_IN_CPP_SOURCE +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE template BridgedArrayRef(llvm::ArrayRef arr) : Data(arr.data()), Length(arr.size()) {} @@ -168,20 +149,19 @@ class BridgedArrayRef { return {static_cast(Data), Length}; } #endif -}; -SWIFT_NAME("getter:BridgedArrayRef.data(self:)") -BRIDGED_INLINE -const void *_Nullable BridgedArrayRef_data(BridgedArrayRef arr); + SWIFT_COMPUTED_PROPERTY + const void *_Nullable getData() const { return Data; } -SWIFT_NAME("getter:BridgedArrayRef.count(self:)") -BRIDGED_INLINE SwiftInt BridgedArrayRef_count(BridgedArrayRef arr); + SWIFT_COMPUTED_PROPERTY + SwiftInt getCount() const { return static_cast(Length); } -SWIFT_NAME("getter:BridgedArrayRef.isEmpty(self:)") -BRIDGED_INLINE bool BridgedArrayRef_isEmpty(BridgedArrayRef arr); + SWIFT_COMPUTED_PROPERTY + bool getIsEmpty() const { return Length == 0; } +}; //===----------------------------------------------------------------------===// -// MARK: Data +// MARK: BridgedData //===----------------------------------------------------------------------===// class BridgedData { @@ -197,17 +177,15 @@ class BridgedData { SWIFT_NAME("init(baseAddress:count:)") BridgedData(const char *_Nullable baseAddress, size_t length) : BaseAddress(baseAddress), Length(length) {} -}; -SWIFT_NAME("getter:BridgedData.baseAddress(self:)") -BRIDGED_INLINE -const char *_Nullable BridgedData_baseAddress(BridgedData data); + SWIFT_COMPUTED_PROPERTY + const char *_Nullable getBaseAddress() const { return BaseAddress; } -SWIFT_NAME("getter:BridgedData.count(self:)") -BRIDGED_INLINE SwiftInt BridgedData_count(BridgedData data); + SWIFT_COMPUTED_PROPERTY + SwiftInt getCount() const { return static_cast(Length); } -SWIFT_NAME("BridgedData.free(self:)") -void BridgedData_free(BridgedData data); + void free() const; +}; //===----------------------------------------------------------------------===// // MARK: Feature @@ -238,18 +216,17 @@ class BridgedStringRef { BridgedStringRef(const char *_Nullable data, size_t length) : Data(data), Length(length) {} - void write(BridgedOStream os) const; -}; + SWIFT_COMPUTED_PROPERTY + const uint8_t *_Nullable getData() const { return (const uint8_t *)Data; } -SWIFT_NAME("getter:BridgedStringRef.data(self:)") -BRIDGED_INLINE -const uint8_t *_Nullable BridgedStringRef_data(BridgedStringRef str); + SWIFT_COMPUTED_PROPERTY + SwiftInt getCount() const { return Length; } -SWIFT_NAME("getter:BridgedStringRef.count(self:)") -BRIDGED_INLINE SwiftInt BridgedStringRef_count(BridgedStringRef str); + SWIFT_COMPUTED_PROPERTY + bool getIsEmpty() const { return Length == 0; } -SWIFT_NAME("getter:BridgedStringRef.isEmpty(self:)") -BRIDGED_INLINE bool BridgedStringRef_empty(BridgedStringRef str); + void write(BridgedOStream os) const; +}; class BridgedOwnedString { char *_Nonnull Data; @@ -257,21 +234,59 @@ class BridgedOwnedString { public: BridgedOwnedString(llvm::StringRef stringToCopy); - BRIDGED_INLINE llvm::StringRef unbridgedRef() const; + SWIFT_COMPUTED_PROPERTY + const uint8_t *_Nonnull getData() const { + return (const uint8_t *)(Data ? Data : ""); + } + + SWIFT_COMPUTED_PROPERTY + SwiftInt getCount() const { return Length; } + + SWIFT_COMPUTED_PROPERTY + bool getIsEmpty() const { return Length == 0; } + void destroy() const; } SWIFT_SELF_CONTAINED; -SWIFT_NAME("getter:BridgedOwnedString.data(self:)") -BRIDGED_INLINE -const uint8_t *_Nullable BridgedOwnedString_data(BridgedOwnedString str); - -SWIFT_NAME("getter:BridgedOwnedString.count(self:)") -BRIDGED_INLINE SwiftInt BridgedOwnedString_count(BridgedOwnedString str); +//===----------------------------------------------------------------------===// +// MARK: BridgedOptional +//===----------------------------------------------------------------------===// -SWIFT_NAME("getter:BridgedOwnedString.isEmpty(self:)") -BRIDGED_INLINE bool BridgedOwnedString_empty(BridgedOwnedString str); +// FIXME: We should be able to make this a template once +// https://github.com/swiftlang/swift/issues/82258 is fixed. +#define BRIDGED_OPTIONAL(TYPE, SUFFIX) \ + class SWIFT_CONFORMS_TO_PROTOCOL(Swift.ExpressibleByNilLiteral) \ + BridgedOptional##SUFFIX { \ + TYPE _value; \ + bool _hasValue; \ + \ + public: \ + SWIFT_NAME("init(nilLiteral:)") \ + BridgedOptional##SUFFIX(void) : _hasValue(false) {} \ + BridgedOptional##SUFFIX(TYPE value) : _value(value), _hasValue(true) {} \ + \ + SWIFT_COMPUTED_PROPERTY \ + TYPE getValue() const { \ + ASSERT_inBridgingHeader(_hasValue); \ + return _value; \ + } \ + \ + SWIFT_COMPUTED_PROPERTY \ + bool getHasValue() const { return _hasValue; } \ + }; +BRIDGED_OPTIONAL(SwiftInt, Int) + +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE +inline BridgedOptionalInt getFromAPInt(llvm::APInt i) { + if (i.getSignificantBits() <= + std::min(std::numeric_limits::digits, 64)) { + return {(SwiftInt)i.getSExtValue()}; + } + return {}; +} +#endif //===----------------------------------------------------------------------===// // MARK: OStream @@ -284,9 +299,16 @@ class BridgedOStream { SWIFT_UNAVAILABLE("Use init(raw:) instead") BridgedOStream(llvm::raw_ostream * _Nonnull os) : os(os) {} + SWIFT_NAME("init(raw:)") + BridgedOStream(void *_Nonnull os) + : os(static_cast(os)) {} + SWIFT_UNAVAILABLE("Use '.raw' instead") llvm::raw_ostream * _Nonnull unbridged() const { return os; } + SWIFT_COMPUTED_PROPERTY + void *_Nonnull getRaw(BridgedOStream bridged) const { return unbridged(); } + void write(BridgedStringRef string) const; void newLine() const; @@ -294,102 +316,8 @@ class BridgedOStream { void flush() const; }; -SWIFT_NAME("getter:BridgedOStream.raw(self:)") -inline void * _Nonnull BridgedOStream_getRaw(BridgedOStream bridged) { - return bridged.unbridged(); -} - -SWIFT_NAME("BridgedOStream.init(raw:)") -inline BridgedOStream BridgedOStream_fromRaw(void * _Nonnull os) { - return static_cast(os); -} - BridgedOStream Bridged_dbgs(); -//===----------------------------------------------------------------------===// -// MARK: SourceLoc -//===----------------------------------------------------------------------===// - -class BridgedSourceLoc { - const void *_Nullable Raw; - -public: - BridgedSourceLoc() : Raw(nullptr) {} - - SWIFT_NAME("init(raw:)") - BridgedSourceLoc(const void *_Nullable raw) : Raw(raw) {} - - BRIDGED_INLINE BridgedSourceLoc(swift::SourceLoc loc); - - BRIDGED_INLINE swift::SourceLoc unbridged() const; - - SWIFT_IMPORT_UNSAFE - const void *_Nullable getOpaquePointerValue() const { return Raw; } - - SWIFT_NAME("advanced(by:)") - BRIDGED_INLINE - BridgedSourceLoc advancedBy(size_t n) const; -}; - -SWIFT_NAME("getter:BridgedSourceLoc.isValid(self:)") -BRIDGED_INLINE bool BridgedSourceLoc_isValid(BridgedSourceLoc loc); - -//===----------------------------------------------------------------------===// -// MARK: SourceRange -//===----------------------------------------------------------------------===// - -class BridgedSourceRange { -public: - SWIFT_NAME("start") - BridgedSourceLoc Start; - - SWIFT_NAME("end") - BridgedSourceLoc End; - - BridgedSourceRange() : Start(), End() {} - - SWIFT_NAME("init(start:end:)") - BridgedSourceRange(BridgedSourceLoc start, BridgedSourceLoc end) - : Start(start), End(end) {} - - BRIDGED_INLINE BridgedSourceRange(swift::SourceRange range); - - BRIDGED_INLINE swift::SourceRange unbridged() const; -}; - -//===----------------------------------------------------------------------===// -// MARK: BridgedCharSourceRange -//===----------------------------------------------------------------------===// - -class BridgedCharSourceRange { -public: - SWIFT_UNAVAILABLE("Use '.start' instead") - BridgedSourceLoc Start; - - SWIFT_UNAVAILABLE("Use '.byteLength' instead") - unsigned ByteLength; - - SWIFT_NAME("init(start:byteLength:)") - BridgedCharSourceRange(BridgedSourceLoc start, unsigned byteLength) - : Start(start), ByteLength(byteLength) {} - - BRIDGED_INLINE BridgedCharSourceRange(swift::CharSourceRange range); - - BRIDGED_INLINE swift::CharSourceRange unbridged() const; -}; - -SWIFT_NAME("getter:BridgedCharSourceRange.start(self:)") -inline BridgedSourceLoc -BridgedCharSourceRange_start(BridgedCharSourceRange range) { - return range.Start; -} - -SWIFT_NAME("getter:BridgedCharSourceRange.byteLength(self:)") -inline SwiftInt -BridgedCharSourceRange_byteLength(BridgedCharSourceRange range) { - return static_cast(range.ByteLength); -} - //===----------------------------------------------------------------------===// // MARK: std::vector //===----------------------------------------------------------------------===// @@ -406,9 +334,9 @@ class BridgedCharSourceRangeVector { BridgedCharSourceRangeVector(); SWIFT_NAME("append(_:)") - void push_back(BridgedCharSourceRange range); + void push_back(swift::CharSourceRange range); -#ifdef USED_IN_CPP_SOURCE +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE /// Returns the `std::vector` that this /// `BridgedCharSourceRangeVector` represents and frees the memory owned by /// this `BridgedCharSourceRangeVector`. @@ -524,6 +452,84 @@ struct BridgedSwiftClosure { BRIDGED_INLINE void operator()(const void *_Nullable); }; +//===----------------------------------------------------------------------===// +// MARK: LangOptions +//===----------------------------------------------------------------------===// + +enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedEndianness : size_t { + EndianLittle, + EndianBig, +}; + +class BridgedLangOptions { + const swift::LangOptions * _Nonnull LangOpts; + +public: + SWIFT_UNAVAILABLE("Use init(raw:) instead") + BRIDGED_INLINE BridgedLangOptions(const swift::LangOptions &langOpts); + + SWIFT_UNAVAILABLE("Use '.raw' instead") + BRIDGED_INLINE const swift::LangOptions &unbridged() const; + + SWIFT_COMPUTED_PROPERTY + const void *_Nonnull getRaw() const { return LangOpts; } + + SWIFT_COMPUTED_PROPERTY + unsigned getMajorLanguageVersion() const; + + SWIFT_COMPUTED_PROPERTY + unsigned getTargetPointerBitWidth() const; + + SWIFT_COMPUTED_PROPERTY + BridgedEndianness getTargetEndianness() const; + + SWIFT_COMPUTED_PROPERTY + bool getAttachCommentsToDecls() const; +}; + +/// Key used when enumerating build configuration entries to the +/// StaticBuildConfiguration initializer for an ASTContext. +enum ENUM_EXTENSIBILITY_ATTR(closed) BuildConfigurationKey : size_t { + BCKCustomCondition, + BCKFeature, + BCKAttribute, + BCKTargetOSName, + BCKTargetArchitecture, + BCKTargetEnvironment, + BCKTargetRuntime, + BCKTargetPointerAuthenticationScheme, + BCKTargetObjectFileFormat +}; + +SWIFT_NAME("BridgedLangOptions.hasFeature(self:_:)") +bool BridgedLangOptions_hasFeature(BridgedLangOptions cLangOpts, + BridgedFeature feature); + +SWIFT_NAME("BridgedLangOptions.getTargetAtomicBitWidths(self:_:)") +SwiftInt BridgedLangOptions_getTargetAtomicBitWidths(BridgedLangOptions cLangOpts, + SwiftInt* _Nullable * _Nonnull cComponents); + +SWIFT_NAME("BridgedLangOptions.getLanguageVersion(self:_:)") +SwiftInt BridgedLangOptions_getLanguageVersion(BridgedLangOptions cLangOpts, + SwiftInt* _Nullable * _Nonnull cComponents); + +SWIFT_NAME("BridgedLangOptions.getCompilerVersion(self:_:)") +SwiftInt BridgedLangOptions_getCompilerVersion(BridgedLangOptions cLangOpts, + SwiftInt* _Nullable * _Nonnull cComponents); + +/* Deallocate an array of Swift int values that was allocated in C++. */ +void deallocateIntBuffer(SwiftInt * _Nullable cComponents); + +/// Enumerate all of the key/value pairs for the build configuration by calling +/// the given callback for each one. +SWIFT_NAME("BridgedLangOptions.enumerateBuildConfigurationEntries(self:callbackContext:callback:)") +void BridgedLangOptions_enumerateBuildConfigurationEntries( + BridgedLangOptions cLangOpts, + void * _Nonnull callbackContext, + void (* _Nonnull callback)( + BridgedLangOptions cLangOpts, void * _Nonnull callbackContext, + BuildConfigurationKey key, BridgedStringRef value)); + SWIFT_END_NULLABILITY_ANNOTATIONS #ifndef PURE_BRIDGING_MODE diff --git a/include/swift/Basic/BasicBridgingImpl.h b/include/swift/Basic/BasicBridgingImpl.h index 35efe357fa253..ee0474caf8a56 100644 --- a/include/swift/Basic/BasicBridgingImpl.h +++ b/include/swift/Basic/BasicBridgingImpl.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2023 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -14,38 +14,11 @@ #define SWIFT_BASIC_BASICBRIDGINGIMPL_H #include "swift/Basic/Assertions.h" -#include "swift/Basic/SourceLoc.h" #include "llvm/ADT/StringRef.h" SWIFT_BEGIN_NULLABILITY_ANNOTATIONS -//===----------------------------------------------------------------------===// -// MARK: BridgedArrayRef -//===----------------------------------------------------------------------===// - -const void *_Nullable BridgedArrayRef_data(BridgedArrayRef arr) { - return arr.Data; -} - -SwiftInt BridgedArrayRef_count(BridgedArrayRef arr) { - return static_cast(arr.Length); -} - -bool BridgedArrayRef_isEmpty(BridgedArrayRef arr) { - return arr.Length == 0; -} - -//===----------------------------------------------------------------------===// -// MARK: BridgedData -//===----------------------------------------------------------------------===// - -const char *_Nullable BridgedData_baseAddress(BridgedData data) { - return data.BaseAddress; -} - -SwiftInt BridgedData_count(BridgedData data) { - return static_cast(data.Length); -} +void ASSERT_inBridgingHeader(bool condition) { ASSERT(condition); } //===----------------------------------------------------------------------===// // MARK: BridgedStringRef @@ -58,79 +31,12 @@ llvm::StringRef BridgedStringRef::unbridged() const { return llvm::StringRef(Data, Length); } -const uint8_t *_Nullable BridgedStringRef_data(BridgedStringRef str) { - return (const uint8_t *)str.unbridged().data(); -} - -SwiftInt BridgedStringRef_count(BridgedStringRef str) { - return (SwiftInt)str.unbridged().size(); -} - -bool BridgedStringRef_empty(BridgedStringRef str) { - return str.unbridged().empty(); -} - //===----------------------------------------------------------------------===// // MARK: BridgedOwnedString //===----------------------------------------------------------------------===// llvm::StringRef BridgedOwnedString::unbridgedRef() const { return llvm::StringRef(Data, Length); } -const uint8_t *_Nullable BridgedOwnedString_data(BridgedOwnedString str) { - auto *data = str.unbridgedRef().data(); - return (const uint8_t *)(data ? data : ""); -} - -SwiftInt BridgedOwnedString_count(BridgedOwnedString str) { - return (SwiftInt)str.unbridgedRef().size(); -} - -bool BridgedOwnedString_empty(BridgedOwnedString str) { - return str.unbridgedRef().empty(); -} - -//===----------------------------------------------------------------------===// -// MARK: BridgedSourceLoc -//===----------------------------------------------------------------------===// - -BridgedSourceLoc::BridgedSourceLoc(swift::SourceLoc loc) - : Raw(loc.getOpaquePointerValue()) {} - -swift::SourceLoc BridgedSourceLoc::unbridged() const { - return swift::SourceLoc( - llvm::SMLoc::getFromPointer(static_cast(Raw))); -} - -bool BridgedSourceLoc_isValid(BridgedSourceLoc loc) { - return loc.getOpaquePointerValue() != nullptr; -} - -BridgedSourceLoc BridgedSourceLoc::advancedBy(size_t n) const { - return BridgedSourceLoc(unbridged().getAdvancedLoc(n)); -} - -//===----------------------------------------------------------------------===// -// MARK: BridgedSourceRange -//===----------------------------------------------------------------------===// - -BridgedSourceRange::BridgedSourceRange(swift::SourceRange range) - : Start(range.Start), End(range.End) {} - -swift::SourceRange BridgedSourceRange::unbridged() const { - return swift::SourceRange(Start.unbridged(), End.unbridged()); -} - -//===----------------------------------------------------------------------===// -// MARK: BridgedCharSourceRange -//===----------------------------------------------------------------------===// - -BridgedCharSourceRange::BridgedCharSourceRange(swift::CharSourceRange range) - : Start(range.getStart()), ByteLength(range.getByteLength()) {} - -swift::CharSourceRange BridgedCharSourceRange::unbridged() const { - return swift::CharSourceRange(Start.unbridged(), ByteLength); -} - //===----------------------------------------------------------------------===// // MARK: BridgedSwiftVersion //===----------------------------------------------------------------------===// @@ -146,7 +52,11 @@ swift_ASTGen_bridgedSwiftClosureCall_1(const void *_Nonnull closure, const void *_Nullable arg1); void BridgedSwiftClosure::operator()(const void *_Nullable arg1) { +#if SWIFT_BUILD_SWIFT_SYNTAX swift_ASTGen_bridgedSwiftClosureCall_1(closure, arg1); +#else + llvm_unreachable("Must not be used in C++-only build"); +#endif } SWIFT_END_NULLABILITY_ANNOTATIONS diff --git a/include/swift/Basic/BlockListAction.def b/include/swift/Basic/BlockListAction.def index 8d9839f019ce3..ffa570e27272f 100644 --- a/include/swift/Basic/BlockListAction.def +++ b/include/swift/Basic/BlockListAction.def @@ -26,5 +26,6 @@ BLOCKLIST_ACTION(ShouldUseLayoutStringValueWitnesses) BLOCKLIST_ACTION(ShouldDisableOwnershipVerification) BLOCKLIST_ACTION(SkipEmittingFineModuleTrace) BLOCKLIST_ACTION(SkipIndexingModule) +BLOCKLIST_ACTION(ShouldUseTypeCheckerPerfHacks) #undef BLOCKLIST_ACTION diff --git a/include/swift/Basic/CASOptions.h b/include/swift/Basic/CASOptions.h index fb63938e1f3fb..567c34671fc49 100644 --- a/include/swift/Basic/CASOptions.h +++ b/include/swift/Basic/CASOptions.h @@ -34,17 +34,17 @@ class CASOptions final { /// Skip replaying outputs from cache. bool CacheSkipReplay = false; + /// Import modules from CAS. + bool ImportModuleFromCAS = false; + /// CASOptions clang::CASOptions CASOpts; - /// CASFS Root. - std::vector CASFSRootIDs; - /// Clang Include Trees. - std::vector ClangIncludeTrees; + std::string ClangIncludeTree; /// Clang Include Tree FileList. - std::vector ClangIncludeTreeFileList; + std::string ClangIncludeTreeFileList; /// CacheKey for input file. std::string InputFileKey; @@ -62,9 +62,8 @@ class CASOptions final { /// Check to see if a CASFileSystem is required. bool requireCASFS() const { return EnableCaching && - (!CASFSRootIDs.empty() || !ClangIncludeTrees.empty() || - !ClangIncludeTreeFileList.empty() || !InputFileKey.empty() || - !BridgingHeaderPCHCacheKey.empty()); + (!ClangIncludeTree.empty() || !ClangIncludeTreeFileList.empty() || + !InputFileKey.empty() || !BridgingHeaderPCHCacheKey.empty()); } /// Return a hash code of any components from these options that should diff --git a/include/swift/Basic/Casting.h b/include/swift/Basic/Casting.h new file mode 100644 index 0000000000000..85ffe8f0ae6ac --- /dev/null +++ b/include/swift/Basic/Casting.h @@ -0,0 +1,59 @@ +//===--- Casting.h - Helpers for casting ------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BASIC_CASTING_H +#define SWIFT_BASIC_CASTING_H + +#include + +namespace swift { + +/// Cast between two function types. Use in place of std::bit_cast, which +/// doesn't work on ARM64e with address-discriminated signed function types. +/// +/// Address-discriminated ptrauth attributes can only be applied to values with +/// a defined storage location, such as struct fields, since their value is +/// inherently tied to their address. They can't be applied to function +/// paremeters. When passing such a value to a templated function, the ptrauth +/// attribute disappears from the inferred type. +/// +/// bit_cast takes the source by reference, which means that the ptrauth +/// attribute remains on the inferred type, and the value is not trivially +/// copyable. +/// +/// function_cast instead takes the source by value, avoiding that issue and +/// ensuring that passed-in function pointers are always trivially copyable. +template +Destination function_cast(Source source) { + static_assert(sizeof(Destination) == sizeof(Source), + "Source and destination must be the same size"); + static_assert(std::is_trivially_copyable_v, + "The source type must be trivially constructible"); + static_assert(std::is_trivially_copyable_v, + "The destination type must be trivially constructible"); + +#if __has_feature(ptrauth_calls) + // Use reinterpret_cast here so we perform any necessary auth-and-sign. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-mismatch" + return reinterpret_cast(source); +#pragma clang diagnostic pop +#else + Destination destination; + memcpy(&destination, &source, sizeof(source)); + return destination; +#endif +} + +} + +#endif diff --git a/include/swift/Basic/Compiler.h b/include/swift/Basic/Compiler.h index 97562a24dcc38..5b057c3d62289 100644 --- a/include/swift/Basic/Compiler.h +++ b/include/swift/Basic/Compiler.h @@ -90,13 +90,6 @@ #define SWIFT_IMPORT_UNSAFE #endif -/// Same as `SWIFT_SELF_CONTAINED` in . -#if __has_attribute(swift_attr) -#define SWIFT_SELF_CONTAINED __attribute__((swift_attr("import_owned"))) -#else -#define SWIFT_SELF_CONTAINED -#endif - #ifdef __GNUC__ #define SWIFT_ATTRIBUTE_NORETURN __attribute__((noreturn)) #elif defined(_MSC_VER) @@ -197,4 +190,10 @@ inline const char *operator""_swift_u8(const char8_t *p, size_t) { #endif // defined(__cpp_char8_t) #endif // defined(__cplusplus) +#if __has_attribute(trivial_abi) +#define SWIFT_TRIVIAL_ABI __attribute__((trivial_abi)) +#else +#define SWIFT_TRIVIAL_ABI +#endif + #endif // SWIFT_BASIC_COMPILER_H diff --git a/include/swift/Basic/DiagnosticOptions.h b/include/swift/Basic/DiagnosticOptions.h index 2be70d7e1a43a..356cbebfe6ad4 100644 --- a/include/swift/Basic/DiagnosticOptions.h +++ b/include/swift/Basic/DiagnosticOptions.h @@ -41,6 +41,11 @@ class DiagnosticOptions { /// \c VerifyMode is not \c NoVerify. bool VerifyIgnoreUnknown = false; + /// Indicates whether to allow diagnostics for locations outside files parsed + /// for 'expected' diagnostics if \c VerifyMode is not \c NoVerify. Does not + /// allow diagnostics at , that is controlled by VerifyIgnoreUnknown. + bool VerifyIgnoreUnrelated = false; + /// Indicates whether diagnostic passes should be skipped. bool SkipDiagnosticPasses = false; @@ -58,6 +63,9 @@ class DiagnosticOptions { /// Suppress all warnings bool SuppressWarnings = false; + /// Suppress all notes + bool SuppressNotes = false; + /// Suppress all remarks bool SuppressRemarks = false; diff --git a/include/swift/Basic/EncodedSequence.h b/include/swift/Basic/EncodedSequence.h index 8b041731d5de3..81b5bc5ad32ec 100644 --- a/include/swift/Basic/EncodedSequence.h +++ b/include/swift/Basic/EncodedSequence.h @@ -102,14 +102,12 @@ class EncodedSequenceBase { } MutableArrayRef chunkStorage() { - return {getTrailingObjects(), Capacity}; + return getTrailingObjects(Capacity); } ArrayRef chunkStorage() const { - return {getTrailingObjects(), Capacity}; - } - ArrayRef chunks() const { - return {getTrailingObjects(), Size}; + return getTrailingObjects(Capacity); } + ArrayRef chunks() const { return getTrailingObjects(Size); } }; OutOfLineStorage *getOutOfLineStorage() { assert(hasOutOfLineStorage()); diff --git a/include/swift/Basic/Feature.h b/include/swift/Basic/Feature.h index d0727bd239efe..b05080bbf3a86 100644 --- a/include/swift/Basic/Feature.h +++ b/include/swift/Basic/Feature.h @@ -10,8 +10,10 @@ // //===----------------------------------------------------------------------===// -#ifndef SWIFT_BASIC_FEATURES_H -#define SWIFT_BASIC_FEATURES_H +#ifndef SWIFT_BASIC_FEATURE_H +#define SWIFT_BASIC_FEATURE_H + +#include "swift/Basic/LLVM.h" #include "llvm/ADT/StringRef.h" #include @@ -21,53 +23,72 @@ namespace swift { class LangOptions; /// Enumeration describing all of the named features. -enum class Feature : uint16_t { +struct Feature { + enum class InnerKind : uint16_t { #define LANGUAGE_FEATURE(FeatureName, SENumber, Description) FeatureName, #include "swift/Basic/Features.def" -}; + }; + + InnerKind kind; -constexpr unsigned numFeatures() { - enum Features { + constexpr Feature(InnerKind kind) : kind(kind) {} + constexpr Feature(unsigned inputKind) : kind(InnerKind(inputKind)) {} + + constexpr operator InnerKind() const { return kind; } + constexpr explicit operator unsigned() const { return unsigned(kind); } + constexpr explicit operator size_t() const { return size_t(kind); } + + static constexpr unsigned getNumFeatures() { + enum Features { #define LANGUAGE_FEATURE(FeatureName, SENumber, Description) FeatureName, #include "swift/Basic/Features.def" - NumFeatures - }; - return NumFeatures; -} + NumFeatures + }; + return NumFeatures; + } -/// Check whether the given feature is available in production compilers. -bool isFeatureAvailableInProduction(Feature feature); +#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \ + static const Feature FeatureName; +#include "swift/Basic/Features.def" -/// Determine the in-source name of the given feature. -llvm::StringRef getFeatureName(Feature feature); + /// Check whether the given feature is available in production compilers. + bool isAvailableInProduction() const; -/// Determine whether the first feature is more recent (and thus implies -/// the existence of) the second feature. Only meaningful for suppressible -/// features. -inline bool featureImpliesFeature(Feature feature, Feature implied) { - // Suppressible features are expected to be listed in order of - // addition in Features.def. - return (unsigned) feature < (unsigned) implied; -} + /// Determine the in-source name of the given feature. + llvm::StringRef getName() const; -/// Get the feature corresponding to this "future" feature, if there is one. -std::optional getUpcomingFeature(llvm::StringRef name); + /// Determine whether the given feature supports migration mode. + bool isMigratable() const; -/// Get the feature corresponding to this "experimental" feature, if there is -/// one. -std::optional getExperimentalFeature(llvm::StringRef name); + /// Determine whether this feature should be included in the + /// module interface + bool includeInModuleInterface() const; -/// Get the major language version in which this feature was introduced, or -/// \c None if it does not have such a version. -std::optional getFeatureLanguageVersion(Feature feature); + /// Determine whether the first feature is more recent (and thus implies + /// the existence of) the second feature. Only meaningful for suppressible + /// features. + constexpr bool featureImpliesFeature(Feature implied) const { + // Suppressible features are expected to be listed in order of + // addition in Features.def. + return (unsigned)kind < (unsigned)implied.kind; + } -/// Determine whether the given feature supports adoption mode. -bool isFeatureAdoptable(Feature feature); + /// Get the feature corresponding to this "future" feature, if there is one. + static std::optional getUpcomingFeature(StringRef name); -/// Determine whether this feature should be included in the -/// module interface -bool includeInModuleInterface(Feature feature); + /// Get the feature corresponding to this "experimental" feature, if there is + /// one. + static std::optional getExperimentalFeature(StringRef name); + /// Get the major language version in which this feature was introduced, or + /// \c None if it does not have such a version. + std::optional getLanguageVersion() const; +}; + +#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \ + constexpr inline Feature Feature::FeatureName = \ + Feature::InnerKind::FeatureName; +#include "swift/Basic/Features.def" } #endif // SWIFT_BASIC_FEATURES_H diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 2f889e2fefc7b..defc01e490be8 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -121,40 +121,45 @@ LANGUAGE_FEATURE(FeatureName, SENumber, Description) #endif -// An upcoming feature that supports adoption mode. +// An upcoming feature that supports migration mode. // // If the feature is source-breaking and provides for a -// mechanical code migration, it should implement adoption mode. +// mechanical code migration, it should implement migration mode. // -// Adoption mode is a feature-oriented code migration mechanism: a mode +// Migration mode is a feature-oriented code migration mechanism: a mode // of operation that should produce compiler warnings with attached // fix-its that can be applied to preserve the behavior of the code once // the upcoming feature is enacted. // These warnings must belong to a diagnostic group named after the -// feature. Adoption mode itself *and* the fix-its it produces must be +// feature. Migration mode itself *and* the fix-its it produces must be // source and binary compatible with how the code is compiled when the // feature is disabled. -#ifndef ADOPTABLE_UPCOMING_FEATURE +#ifndef MIGRATABLE_UPCOMING_FEATURE #if defined(UPCOMING_FEATURE) - #define ADOPTABLE_UPCOMING_FEATURE(FeatureName, SENumber, Version) \ + #define MIGRATABLE_UPCOMING_FEATURE(FeatureName, SENumber, Version) \ UPCOMING_FEATURE(FeatureName, SENumber, Version) #else - #define ADOPTABLE_UPCOMING_FEATURE(FeatureName, SENumber, Version) \ + #define MIGRATABLE_UPCOMING_FEATURE(FeatureName, SENumber, Version) \ LANGUAGE_FEATURE(FeatureName, SENumber, #FeatureName) #endif #endif -// See `ADOPTABLE_UPCOMING_FEATURE`. -#ifndef ADOPTABLE_EXPERIMENTAL_FEATURE +// See `MIGRATABLE_UPCOMING_FEATURE`. +#ifndef MIGRATABLE_EXPERIMENTAL_FEATURE #if defined(EXPERIMENTAL_FEATURE) - #define ADOPTABLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \ + #define MIGRATABLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \ EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) #else - #define ADOPTABLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \ + #define MIGRATABLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \ LANGUAGE_FEATURE(FeatureName, 0, #FeatureName) #endif #endif +#ifndef MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE + #define MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Name) \ + OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, #Name) +#endif + #ifndef UPCOMING_FEATURE #define UPCOMING_FEATURE(FeatureName, SENumber, Version) \ LANGUAGE_FEATURE(FeatureName, SENumber, #FeatureName) @@ -241,19 +246,36 @@ BASELINE_LANGUAGE_FEATURE(BitwiseCopyable, 426, "BitwiseCopyable protocol") BASELINE_LANGUAGE_FEATURE(NoncopyableGenerics, 427, "Noncopyable generics") BASELINE_LANGUAGE_FEATURE(NoncopyableGenerics2, 427, "Noncopyable generics alias") BASELINE_LANGUAGE_FEATURE(ConformanceSuppression, 426, "Suppressible inferred conformances") -SUPPRESSIBLE_LANGUAGE_FEATURE(BitwiseCopyable2, 426, "BitwiseCopyable feature") +BASELINE_LANGUAGE_FEATURE(BitwiseCopyable2, 426, "BitwiseCopyable feature") BASELINE_LANGUAGE_FEATURE(BodyMacros, 415, "Function body macros") -SUPPRESSIBLE_LANGUAGE_FEATURE(SendingArgsAndResults, 430, "Sending arg and results") +LANGUAGE_FEATURE(SendingArgsAndResults, 430, "Sending arg and results") BASELINE_LANGUAGE_FEATURE(BorrowingSwitch, 432, "Noncopyable type pattern matching") -CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(IsolatedAny, 431, "@isolated(any) function types") +BASELINE_LANGUAGE_FEATURE(IsolatedAny, 431, "@isolated(any) function types") LANGUAGE_FEATURE(IsolatedAny2, 431, "@isolated(any) function types") LANGUAGE_FEATURE(ObjCImplementation, 436, "@objc @implementation extensions") -LANGUAGE_FEATURE(NonescapableTypes, 446, "Nonescapable types") -LANGUAGE_FEATURE(BuiltinEmplaceTypedThrows, 0, "Builtin.emplace typed throws") -SUPPRESSIBLE_LANGUAGE_FEATURE(MemorySafetyAttributes, 458, "@unsafe attribute") -LANGUAGE_FEATURE(ValueGenerics, 452, "Value generics feature (integer generics)") +BASELINE_LANGUAGE_FEATURE(NonescapableTypes, 446, "Nonescapable types") +BASELINE_LANGUAGE_FEATURE(BuiltinEmplaceTypedThrows, 0, "Builtin.emplace typed throws") +BASELINE_LANGUAGE_FEATURE(MemorySafetyAttributes, 458, "@unsafe attribute") +BASELINE_LANGUAGE_FEATURE(ValueGenerics, 452, "Value generics feature (integer generics)") LANGUAGE_FEATURE(RawIdentifiers, 451, "Raw identifiers") LANGUAGE_FEATURE(SendableCompletionHandlers, 463, "Objective-C completion handler parameters are imported as @Sendable") +LANGUAGE_FEATURE(AsyncExecutionBehaviorAttributes, 0, "@concurrent and nonisolated(nonsending)") +LANGUAGE_FEATURE(IsolatedConformances, 407, "Global-actor isolated conformances") +BASELINE_LANGUAGE_FEATURE(ValueGenericsNameLookup, 452, "Value generics appearing as static members for namelookup") +LANGUAGE_FEATURE(GeneralizedIsSameMetaTypeBuiltin, 465, "Builtin.is_same_metatype with support for noncopyable/nonescapable types") +SUPPRESSIBLE_LANGUAGE_FEATURE(ABIAttributeSE0479, 479, "@abi attribute on functions, initializers, properties, and subscripts") +LANGUAGE_FEATURE(AlwaysInheritActorContext, 472, "@_inheritActorContext(always)") +LANGUAGE_FEATURE(BuiltinSelect, 0, "Builtin.select") +LANGUAGE_FEATURE(BuiltinInterleave, 0, "Builtin.interleave and Builtin.deinterleave") +LANGUAGE_FEATURE(BuiltinVectorsExternC, 0, "Extern C support for Builtin vector types") +LANGUAGE_FEATURE(AddressOfProperty2, 0, "Builtin.unprotectedAddressOf properties") +LANGUAGE_FEATURE(NonescapableAccessorOnTrivial, 0, "Support UnsafeMutablePointer.mutableSpan") +BASELINE_LANGUAGE_FEATURE(LayoutPrespecialization, 0, "Layout pre-specialization") +BASELINE_LANGUAGE_FEATURE(IsolatedDeinit, 371, "isolated deinit") +LANGUAGE_FEATURE(InlineArrayTypeSugar, 483, "Type sugar for InlineArray") +LANGUAGE_FEATURE(LifetimeDependenceMutableAccessors, 0, "Support mutable accessors returning ~Escapable results") +LANGUAGE_FEATURE(InoutLifetimeDependence, 0, "Support @_lifetime(&)") +SUPPRESSIBLE_LANGUAGE_FEATURE(NonexhaustiveAttribute, 487, "Nonexhaustive Enums") // Swift 6 UPCOMING_FEATURE(ConciseMagicFile, 274, 6) @@ -273,15 +295,20 @@ UPCOMING_FEATURE(NonfrozenEnumExhaustivity, 192, 6) UPCOMING_FEATURE(GlobalActorIsolatedTypesUsability, 0434, 6) // Swift 7 -ADOPTABLE_UPCOMING_FEATURE(ExistentialAny, 335, 7) +MIGRATABLE_UPCOMING_FEATURE(ExistentialAny, 335, 7) UPCOMING_FEATURE(InternalImportsByDefault, 409, 7) -UPCOMING_FEATURE(MemberImportVisibility, 444, 7) +MIGRATABLE_UPCOMING_FEATURE(MemberImportVisibility, 444, 7) +MIGRATABLE_UPCOMING_FEATURE(InferIsolatedConformances, 470, 7) +MIGRATABLE_UPCOMING_FEATURE(NonisolatedNonsendingByDefault, 461, 7) +UPCOMING_FEATURE(ImmutableWeakCaptures, 481, 7) // Optional language features / modes /// Diagnose uses of language constructs and APIs that can violate memory /// safety. -OPTIONAL_LANGUAGE_FEATURE(StrictMemorySafety, 458, "Strict memory safety") +MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE(StrictMemorySafety, 458, "Strict memory safety") + +OPTIONAL_LANGUAGE_FEATURE(LibraryEvolution, 0, "Library evolution") // Experimental features @@ -299,6 +326,12 @@ EXPERIMENTAL_FEATURE(KeyPathWithMethodMembers, false) // Whether to enable @_used and @_section attributes EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true) +// Whether to emit an Embedded Swift module with "deferred" code generation, +// meaning that the only code that will be emitted into the object file is +// code that was marked as "never emit into client". For everything else, +// Swift still store the SIL and emit it into the client only when used. +EXPERIMENTAL_FEATURE(DeferredCodeGen, true) + // Whether to compile scripts lazily in immediate mode EXPERIMENTAL_FEATURE(LazyImmediate, false) @@ -313,10 +346,12 @@ EXPERIMENTAL_FEATURE(MoveOnlyTuples, true) EXPERIMENTAL_FEATURE(MoveOnlyPartialReinitialization, true) EXPERIMENTAL_FEATURE(ConsumeSelfInDeinit, true) -EXPERIMENTAL_FEATURE(LayoutPrespecialization, true) - EXPERIMENTAL_FEATURE(AccessLevelOnImport, true) +/// Disable the special behavior of of explicit 'nonisolated' vs. being +/// implicitly nonisolated. +EXPERIMENTAL_FEATURE(NoExplicitNonIsolated, true) + /// Enables a module to allow non resilient access from other /// modules within a package if built from source. EXPERIMENTAL_FEATURE(AllowNonResilientAccessInPackage, false) @@ -370,11 +405,6 @@ EXPERIMENTAL_FEATURE(ParserASTGen, false) /// corresponding syntax tree. EXPERIMENTAL_FEATURE(BuiltinMacros, false) -/// Import C++ class templates as semantically-meaningless symbolic -/// Swift types and C++ methods as symbolic functions with blank -/// signatures. -EXPERIMENTAL_FEATURE(ImportSymbolicCXXDecls, false) - /// Generate bindings for functions that 'throw' in the C++ section of the generated Clang header. EXPERIMENTAL_FEATURE(GenerateBindingsForThrowingFunctionsInCXX, false) @@ -394,6 +424,9 @@ EXPERIMENTAL_FEATURE(ThenStatements, false) /// Enable 'do' expressions. EXPERIMENTAL_FEATURE(DoExpressions, false) +/// Enable 'for' expressions. +EXPERIMENTAL_FEATURE(ForExpressions, false) + /// Enable implicitly treating the last expression in a function, closure, /// and 'if'/'switch' expression as the result. EXPERIMENTAL_FEATURE(ImplicitLastExprResults, false) @@ -419,6 +452,9 @@ EXPERIMENTAL_FEATURE(LifetimeDependence, true) /// Enable the `@_staticExclusiveOnly` attribute. EXPERIMENTAL_FEATURE(StaticExclusiveOnly, true) +/// Enable the `@_manualOwnership` attribute. +EXPERIMENTAL_FEATURE(ManualOwnership, false) + /// Enable the @extractConstantsFromMembers attribute. EXPERIMENTAL_FEATURE(ExtractConstantsFromMembers, false) @@ -454,7 +490,7 @@ EXPERIMENTAL_FEATURE(TrailingComma, false) // Import bounds safety and lifetime attributes from interop headers to // generate Swift wrappers with safe pointer types. -EXPERIMENTAL_FEATURE(SafeInteropWrappers, false) +EXPERIMENTAL_FEATURE(SafeInteropWrappers, true) /// Ignore resilience errors due to C++ types. EXPERIMENTAL_FEATURE(AssumeResilientCxxTypes, true) @@ -462,12 +498,14 @@ EXPERIMENTAL_FEATURE(AssumeResilientCxxTypes, true) /// Import inherited non-public members when importing C++ classes. EXPERIMENTAL_FEATURE(ImportNonPublicCxxMembers, true) -/// Synthesize static factory methods for C++ foreign reference types and import -/// them as Swift initializers. -EXPERIMENTAL_FEATURE(CXXForeignReferenceTypeInitializers, true) +/// Suppress the synthesis of static factory methods for C++ foreign reference +/// types and importing them as Swift initializers. +EXPERIMENTAL_FEATURE(SuppressCXXForeignReferenceTypeInitializers, true) -// Isolated deinit -SUPPRESSIBLE_LANGUAGE_FEATURE(IsolatedDeinit, 371, "isolated deinit") +/// Emit a warning when a C++ API returns a SWIFT_SHARED_REFERENCE type +/// without being explicitly annotated with either SWIFT_RETURNS_RETAINED +/// or SWIFT_RETURNS_UNRETAINED. +EXPERIMENTAL_FEATURE(WarnUnannotatedReturnOfCxxFrt, true) /// modify/read single-yield coroutines SUPPRESSIBLE_EXPERIMENTAL_FEATURE(CoroutineAccessors, true) @@ -478,54 +516,55 @@ EXPERIMENTAL_FEATURE(CoroutineAccessorsUnwindOnCallerError, false) EXPERIMENTAL_FEATURE(AddressableParameters, true) SUPPRESSIBLE_EXPERIMENTAL_FEATURE(AddressableTypes, true) -/// Allow the @abi attribute. -SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ABIAttribute, true) - -/// Allow the @execution attribute. This is also connected to -/// AsyncCallerExecution feature. -SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ExecutionAttribute, false) - -/// Functions with nonisolated isolation inherit their isolation from the -/// calling context. -ADOPTABLE_EXPERIMENTAL_FEATURE(AsyncCallerExecution, false) - /// Allow custom availability domains to be defined and referenced. -SUPPRESSIBLE_EXPERIMENTAL_FEATURE(CustomAvailability, true) - -/// Allow public enumerations to be extensible by default -/// regardless of whether the module they are declared in -/// is resilient or not. -EXPERIMENTAL_FEATURE(ExtensibleEnums, true) - -/// Allow isolated conformances. -EXPERIMENTAL_FEATURE(IsolatedConformances, true) - -/// Infer conformance isolation on global-actor-conforming types. -EXPERIMENTAL_FEATURE(InferIsolatedConformances, true) - -/// Allow SwiftSettings -EXPERIMENTAL_FEATURE(SwiftSettings, false) +EXPERIMENTAL_FEATURE(CustomAvailability, true) /// Syntax sugar features for concurrency. EXPERIMENTAL_FEATURE(ConcurrencySyntaxSugar, true) -/// Enable syntax sugar type '[3 x Int]' for Inline Array -EXPERIMENTAL_FEATURE(InlineArrayTypeSugar, false) - /// Allow declaration of compile-time values EXPERIMENTAL_FEATURE(CompileTimeValues, true) +/// Allow declaration of compile-time values +EXPERIMENTAL_FEATURE(CompileTimeValuesPreview, false) + /// Allow function body macros applied to closures. EXPERIMENTAL_FEATURE(ClosureBodyMacro, true) /// Allow declarations of Swift runtime symbols using @_silgen_name. EXPERIMENTAL_FEATURE(AllowRuntimeSymbolDeclarations, true) +/// Allow use of `@c` +EXPERIMENTAL_FEATURE(CDecl, false) + +/// Allow use of `Module::name` syntax +EXPERIMENTAL_FEATURE(ModuleSelector, false) + +/// Allow use of `using` declaration that control default isolation +/// in a file scope. +EXPERIMENTAL_FEATURE(DefaultIsolationPerFile, false) + +/// Enable @_lifetime attribute +SUPPRESSIBLE_EXPERIMENTAL_FEATURE(Lifetimes, true) + +/// Allow macro based aliases to be imported into Swift +EXPERIMENTAL_FEATURE(ImportMacroAliases, true) + +/// Enable borrow/mutate accessors +EXPERIMENTAL_FEATURE(BorrowAndMutateAccessors, false) + +/// Allow use of @inline(always) attribute +SUPPRESSIBLE_EXPERIMENTAL_FEATURE(InlineAlways, false) + +/// Allow use of 'Swift' (Swift runtime version) in @available attributes +EXPERIMENTAL_FEATURE(SwiftRuntimeAvailability, true) + #undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE #undef EXPERIMENTAL_FEATURE #undef UPCOMING_FEATURE -#undef ADOPTABLE_UPCOMING_FEATURE -#undef ADOPTABLE_EXPERIMENTAL_FEATURE +#undef MIGRATABLE_UPCOMING_FEATURE +#undef MIGRATABLE_EXPERIMENTAL_FEATURE +#undef MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE #undef BASELINE_LANGUAGE_FEATURE #undef OPTIONAL_LANGUAGE_FEATURE #undef CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE diff --git a/include/swift/Basic/FileTypes.def b/include/swift/Basic/FileTypes.def index ae18991130eca..4ca4c87520b20 100644 --- a/include/swift/Basic/FileTypes.def +++ b/include/swift/Basic/FileTypes.def @@ -78,8 +78,8 @@ TYPE("fine-module-trace", FineModuleTrace, "", "") // Complete dependency information for the given Swift files as JSON. TYPE("json-dependencies", JSONDependencies, "dependencies.json", "") -// Complete feature information for the given Swift compiler. -TYPE("json-features", JSONFeatures, "features.json", "") +// Complete supported argument information for the given Swift compiler. +TYPE("json-arguments", JSONArguments, "arguments.json", "") // Gathered compile-time-known value information for the given Swift input file as JSON. TYPE("const-values", ConstValues, "swiftconstvalues", "") diff --git a/include/swift/Basic/InlineBitfield.h b/include/swift/Basic/InlineBitfield.h index b41ae018d4edf..1378115aee012 100644 --- a/include/swift/Basic/InlineBitfield.h +++ b/include/swift/Basic/InlineBitfield.h @@ -41,7 +41,7 @@ namespace swift { uint64_t : 64 - (C); /* Better code gen */ \ } T; \ LLVM_PACKED_END \ - enum { Num##T##Bits = (C) }; \ + enum { NumberOf##T##Bits = (C) }; \ static_assert(sizeof(T##Bitfield) <= 8, "Bitfield overflow") /// Define an bitfield for type 'T' with parent class 'U' and 'C' bits used. @@ -49,11 +49,11 @@ namespace swift { LLVM_PACKED_START \ class T##Bitfield { \ friend class T; \ - uint64_t : Num##U##Bits, __VA_ARGS__; \ - uint64_t : 64 - (Num##U##Bits + (HC) + (C)); /* Better code gen */ \ + uint64_t : NumberOf##U##Bits, __VA_ARGS__; \ + uint64_t : 64 - (NumberOf##U##Bits + (HC) + (C)); /* Better code gen */ \ } T; \ LLVM_PACKED_END \ - enum { Num##T##Bits = Num##U##Bits + (C) }; \ + enum { NumberOf##T##Bits = NumberOf##U##Bits + (C) }; \ static_assert(sizeof(T##Bitfield) <= 8, "Bitfield overflow") #define SWIFT_INLINE_BITFIELD(T, U, C, ...) \ @@ -75,8 +75,8 @@ namespace swift { LLVM_PACKED_START \ class T##Bitfield { \ friend class T; \ - enum { NumPadBits = 64 - (Num##U##Bits + (C)) }; \ - uint64_t : Num##U##Bits, __VA_ARGS__; \ + enum { NumPadBits = 64 - (NumberOf##U##Bits + (C)) }; \ + uint64_t : NumberOf##U##Bits, __VA_ARGS__; \ } T; \ LLVM_PACKED_END \ static_assert(sizeof(T##Bitfield) <= 8, "Bitfield overflow") @@ -101,15 +101,15 @@ namespace swift { class T##Bitfield { \ template \ friend class T; \ - enum { NumPadBits = 64 - (Num##U##Bits + (C)) }; \ - uint64_t : Num##U##Bits, __VA_ARGS__; \ + enum { NumPadBits = 64 - (NumberOf##U##Bits + (C)) }; \ + uint64_t : NumberOf##U##Bits, __VA_ARGS__; \ } T; \ LLVM_PACKED_END \ static_assert(sizeof(T##Bitfield) <= 8, "Bitfield overflow") /// Define an empty bitfield for type 'T'. #define SWIFT_INLINE_BITFIELD_EMPTY(T, U) \ - enum { Num##T##Bits = Num##U##Bits } + enum { NumberOf##T##Bits = NumberOf##U##Bits } // XXX/HACK: templated max() doesn't seem to work in a bitfield size context. constexpr unsigned bitmax(unsigned a, unsigned b) { diff --git a/include/swift/Basic/LLVMInitialize.h b/include/swift/Basic/LLVMInitialize.h index 930a44d3cb450..a27f698e8259b 100644 --- a/include/swift/Basic/LLVMInitialize.h +++ b/include/swift/Basic/LLVMInitialize.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 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -19,14 +19,16 @@ #ifndef SWIFT_BASIC_LLVMINITIALIZE_H #define SWIFT_BASIC_LLVMINITIALIZE_H +#include "swift/Basic/Compiler.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" -#define PROGRAM_START(argc, argv) \ - llvm::InitLLVM _INITIALIZE_LLVM(argc, argv) +#define PROGRAM_START(argc, argv) \ + llvm::InitLLVM _INITIALIZE_LLVM(argc, argv); \ + llvm::setBugReportMsg(SWIFT_CRASH_BUG_REPORT_MESSAGE "\n") #define INITIALIZE_LLVM() \ do { \ diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 01e0162a9a546..4687beea7d70d 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -22,6 +22,7 @@ #include "swift/Basic/Feature.h" #include "swift/Basic/FunctionBodySkipping.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/Platform.h" #include "swift/Basic/PlaygroundOption.h" #include "swift/Basic/Version.h" #include "swift/Config.h" @@ -45,6 +46,7 @@ namespace swift { struct DiagnosticBehavior; class DiagnosticEngine; + class FrontendOptions; /// Kind of implicit platform conditions. enum class PlatformConditionKind { @@ -66,22 +68,24 @@ namespace swift { Complete, }; - /// Access or distribution level of a library. + /// Intended distribution level of a module. + /// + /// Ordered from more private to more public. enum class LibraryLevel : uint8_t { - /// Application Programming Interface that is publicly distributed so - /// public decls are really public and only @_spi decls are SPI. - API, + /// This isn't a library or the library distribution intent is unknown. + Other, - /// System Programming Interface that has restricted distribution - /// all decls in the module are considered to be SPI including public ones. - SPI, - - /// Internal Programming Interface that is not distributed and only usable - /// from within a project. + /// Internal Programming Interface: the module is not distributed and + /// only usable from within its project. IPI, - /// The library has some other undefined distribution. - Other + /// System Programming Interface: the module has restricted distribution, + /// all public decls in the module are considered to be SPI. + SPI, + + /// Application Programming Interface: the module is distributed publicly, + /// public decls are really public and only @_spi decls are SPI. + API, }; enum class AccessNoteDiagnosticBehavior : uint8_t { @@ -184,6 +188,9 @@ namespace swift { /// Swift runtime version to compile for. version::Version RuntimeVersion = version::Version::getCurrentLanguageVersion(); + /// The minimum Swift runtime version that the progam can be deployed to. + version::Version MinSwiftRuntimeVersion; + /// PackageDescription version to compile for. version::Version PackageDescriptionVersion; @@ -269,9 +276,6 @@ namespace swift { /// Emit a remark on early exit in explicit interface build bool EnableSkipExplicitInterfaceModuleBuildRemarks = false; - /// Emit a remark when \c \@abi infers an attribute or modifier. - bool EnableABIInferenceRemarks = false; - /// /// Support for alternate usage modes /// @@ -323,6 +327,8 @@ namespace swift { /// Flags for use by tests /// + bool UseStaticStandardLibrary = false; + /// Enable Objective-C Runtime interop code generation and build /// configuration options. bool EnableObjCInterop = true; @@ -342,7 +348,8 @@ namespace swift { std::optional FormalCxxInteropMode; void setCxxInteropFromArgs(llvm::opt::ArgList &Args, - swift::DiagnosticEngine &Diags); + swift::DiagnosticEngine &Diags, + const FrontendOptions &FrontendOpts); /// The C++ standard library used for the current build. This can differ /// from the default C++ stdlib on a particular platform when `-Xcc @@ -354,8 +361,6 @@ namespace swift { return CXXStdlib == PlatformDefaultCXXStdlib; } - bool CForeignReferenceTypes = false; - /// Imports getters and setters as computed properties. bool CxxInteropGettersSettersAsProperties = false; @@ -409,10 +414,6 @@ namespace swift { /// Specifies how strict concurrency checking will be. StrictConcurrency StrictConcurrencyLevel = StrictConcurrency::Minimal; - /// Specifies the name of the executor factory to use to create the - /// default executors for Swift Concurrency. - std::optional ExecutorFactory; - /// Enable experimental concurrency model. bool EnableExperimentalConcurrency = false; @@ -497,6 +498,9 @@ namespace swift { std::shared_ptr OptimizationRemarkPassedPattern; std::shared_ptr OptimizationRemarkMissedPattern; + /// The path to load access notes from. + std::string AccessNotesPath; + /// How should we emit diagnostics about access notes? AccessNoteDiagnosticBehavior AccessNoteBehavior = AccessNoteDiagnosticBehavior::RemarkOnFailureOrSuccess; @@ -583,10 +587,18 @@ namespace swift { /// algorithm. unsigned RequirementMachineMaxRuleLength = 12; - /// Maximum concrete type nesting depth for requirement machine property map - /// algorithm. + /// Maximum concrete type nesting depth (when type is viewed as a tree) for + /// requirement machine property map algorithm. unsigned RequirementMachineMaxConcreteNesting = 30; + /// Maximum concrete type size (total number of nodes in the type tree) for + /// requirement machine property map algorithm. + unsigned RequirementMachineMaxConcreteSize = 4000; + + /// Maximum number of "type difference" structures for the requirement machine + /// property map algorithm. + unsigned RequirementMachineMaxTypeDifferences = 13000; + /// Maximum number of attempts to make when splitting concrete equivalence /// classes. unsigned RequirementMachineMaxSplitConcreteEquivClassAttempts = 2; @@ -609,6 +621,14 @@ namespace swift { /// rewrite system. bool EnableRequirementMachineOpaqueArchetypes = false; + /// Maximum nesting depth for type substitution operations, to prevent + /// runaway recursion. + unsigned MaxSubstitutionDepth = 500; + + /// Maximum step count for type substitution operations, to prevent + /// runaway recursion. + unsigned MaxSubstitutionCount = 120000; + /// Enable implicit lifetime dependence for ~Escapable return types. bool EnableExperimentalLifetimeDependenceInference = false; @@ -631,12 +651,19 @@ namespace swift { /// Enables dumping macro expansions. bool DumpMacroExpansions = false; + /// Enables dumping imports for each SourceFile. + bool DumpSourceFileImports = false; + /// The model of concurrency to be used. ConcurrencyModel ActiveConcurrencyModel = ConcurrencyModel::Standard; /// All block list configuration files to be honored in this compilation. std::vector BlocklistConfigFilePaths; + /// List of top level modules to be considered as if they had require ObjC + /// in their module map. + llvm::SmallVector ModulesRequiringObjC; + /// Whether to ignore checks that a module is resilient during /// type-checking, SIL verification, and IR emission, bool BypassResilienceChecks = false; @@ -678,18 +705,7 @@ namespace swift { /// This is only implemented on certain OSs. If no target has been /// configured, returns v0.0.0. llvm::VersionTuple getMinPlatformVersion() const { - if (Target.isMacOSX()) { - llvm::VersionTuple OSVersion; - Target.getMacOSXVersion(OSVersion); - return OSVersion; - } else if (Target.isiOS()) { - return Target.getiOSVersion(); - } else if (Target.isWatchOS()) { - return Target.getOSVersion(); - } else if (Target.isXROS()) { - return Target.getOSVersion(); - } - return llvm::VersionTuple(/*Major=*/0, /*Minor=*/0, /*Subminor=*/0); + return getVersionForTriple(Target); } /// Sets an implicit platform condition. @@ -837,7 +853,7 @@ namespace swift { /// A wrapper around the feature state enumeration. struct FeatureState { - enum class Kind : uint8_t { Off, EnabledForAdoption, Enabled }; + enum class Kind : uint8_t { Off, EnabledForMigration, Enabled }; private: Feature feature; @@ -850,9 +866,9 @@ namespace swift { /// Returns whether the feature is enabled. bool isEnabled() const; - /// Returns whether the feature is enabled in adoption mode. Should only + /// Returns whether the feature is enabled in migration mode. Should only /// be called if the feature is known to support this mode. - bool isEnabledForAdoption() const; + bool isEnabledForMigration() const; operator Kind() const { return state; } }; @@ -879,15 +895,21 @@ namespace swift { FeatureState getFeatureState(Feature feature) const; /// Returns whether the given feature is enabled. - bool hasFeature(Feature feature) const; + /// + /// If allowMigration is set, also returns true when the feature has been + /// enabled for migration. + bool hasFeature(Feature feature, bool allowMigration = false) const; /// Returns whether a feature with the given name is enabled. Returns /// `false` if a feature by this name is not known. bool hasFeature(llvm::StringRef featureName) const; - /// Enables the given feature (enables in adoption mode if `forAdoption` is - /// `true`). - void enableFeature(Feature feature, bool forAdoption = false); + /// Returns whether the given feature is enabled for migration. + bool isMigratingToFeature(Feature feature) const; + + /// Enables the given feature (enables in migration mode if `forMigration` + /// is `true`). + void enableFeature(Feature feature, bool forMigration = false); /// Disables the given feature. void disableFeature(Feature feature); @@ -931,6 +953,10 @@ namespace swift { /// (It's arbitrary, but will keep the compiler from taking too much time.) unsigned SwitchCheckingInvocationThreshold = 200000; + /// The maximum number of `@dynamicMemberLookup`s that can be chained to + /// resolve a member reference. + unsigned DynamicMemberLookupDepthLimit = 100; + /// If true, the time it takes to type-check each function will be dumped /// to llvm::errs(). bool DebugTimeFunctionBodies = false; @@ -972,14 +998,11 @@ namespace swift { /// is for testing purposes. std::vector DebugForbidTypecheckPrefixes; - /// Disable the shrink phase of the expression type checker. - bool SolverDisableShrink = false; - /// Enable experimental operator designated types feature. bool EnableOperatorDesignatedTypes = false; - /// Disable constraint system performance hacks. - bool DisableConstraintSolverPerformanceHacks = false; + /// Enable old constraint system performance hacks. + bool EnableConstraintSolverPerformanceHacks = false; /// See \ref FrontendOptions.PrintFullConvention bool PrintFullConvention = false; @@ -993,6 +1016,9 @@ namespace swift { /// Disable the component splitter phase of the expression type checker. bool SolverDisableSplitter = false; + + /// Enable the experimental "prepared overloads" optimization. + bool SolverEnablePreparedOverloads = false; }; /// Options for controlling the behavior of the Clang importer. @@ -1028,6 +1054,10 @@ namespace swift { /// The bridging header PCH file. std::string BridgingHeaderPCH; + /// Whether the bridging header and PCH file are considered to be + /// internal imports. + bool BridgingHeaderIsInternal = false; + /// When automatically generating a precompiled header from the bridging /// header, place it in this directory. std::string PrecompiledHeaderOutputDir; @@ -1088,6 +1118,10 @@ namespace swift { /// When set, don't enforce warnings with -Werror. bool DebuggerSupport = false; + /// Prefer the serialized preprocessed header over the one on disk. + /// Used by LLDB. + bool PreferSerializedBridgingHeader = false; + /// When set, ClangImporter is disabled, and all requests go to the /// DWARFImporter delegate. bool DisableSourceImport = false; @@ -1101,16 +1135,19 @@ namespace swift { /// built and provided to the compiler invocation. bool DisableImplicitClangModules = false; - /// Enable ClangIncludeTree for explicit module builds scanning. - bool UseClangIncludeTree = false; - - /// Using ClangIncludeTreeRoot for compilation. - bool HasClangIncludeTreeRoot = false; - /// Whether the dependency scanner should construct all swift-frontend /// invocations directly from clang cc1 args. bool ClangImporterDirectCC1Scan = false; + /// Whether we should import values (initializer expressions) of constant + /// globals. + bool EnableConstValueImporting = true; + + /// Whether the importer should expect all APINotes to be wrapped + /// in versioned attributes, where the importer must select the appropriate + /// ones to apply. + bool LoadVersionIndependentAPINotes = false; + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h index f9f5b2296456f..e0db2f0385f89 100644 --- a/include/swift/Basic/Located.h +++ b/include/swift/Basic/Located.h @@ -71,7 +71,7 @@ struct DenseMapInfo> { } static unsigned getHashValue(const swift::Located &LocatedVal) { - return combineHashValue(DenseMapInfo::getHashValue(LocatedVal.Item), + return detail::combineHashValue(DenseMapInfo::getHashValue(LocatedVal.Item), DenseMapInfo::getHashValue(LocatedVal.Loc)); } @@ -82,4 +82,12 @@ struct DenseMapInfo> { }; } // namespace llvm +namespace swift { + template + llvm::hash_code hash_value(const Located &LocatedVal) { + return llvm::DenseMapInfo>::getHashValue(LocatedVal); + } +} // namespace swift + + #endif // SWIFT_BASIC_LOCATED_H diff --git a/include/swift/Basic/OwnedString.h b/include/swift/Basic/OwnedString.h index 109eb003e6819..c37370d18c052 100644 --- a/include/swift/Basic/OwnedString.h +++ b/include/swift/Basic/OwnedString.h @@ -35,8 +35,7 @@ class OwnedString { class TextOwner final : public llvm::ThreadSafeRefCountedBase, public llvm::TrailingObjects { TextOwner(StringRef Text) { - std::uninitialized_copy(Text.begin(), Text.end(), - getTrailingObjects()); + std::uninitialized_copy(Text.begin(), Text.end(), getTrailingObjects()); } public: @@ -50,7 +49,7 @@ class OwnedString { /// data. void operator delete(void *p) { ::operator delete(p); } - const char *getText() const { return getTrailingObjects(); } + const char *getText() const { return getTrailingObjects(); } }; /// The text this owned string represents diff --git a/include/swift/Basic/Platform.h b/include/swift/Basic/Platform.h index 94515f6dbd890..bf40edad76b33 100644 --- a/include/swift/Basic/Platform.h +++ b/include/swift/Basic/Platform.h @@ -85,6 +85,9 @@ namespace swift { /// returned. StringRef getPlatformNameForTriple(const llvm::Triple &triple); + /// Returns the version tuple for a given target triple + llvm::VersionTuple getVersionForTriple(const llvm::Triple &triple); + /// Returns the platform Kind for Darwin triples. DarwinPlatformKind getDarwinPlatformKind(const llvm::Triple &triple); @@ -123,6 +126,16 @@ namespace swift { llvm::VersionTuple getTargetSDKVersion(clang::DarwinSDKInfo &SDKInfo, const llvm::Triple &triple); + /// Compute a target triple that is canonicalized using the passed triple. + /// \returns nullopt if computation fails. + std::optional getCanonicalTriple(const llvm::Triple &triple); + + /// Compare triples for equality but also including OSVersion. + inline bool areTriplesStrictlyEqual(const llvm::Triple &lhs, + const llvm::Triple &rhs) { + return (lhs == rhs) && (lhs.getOSVersion() == rhs.getOSVersion()); + } + /// Get SDK build version. std::string getSDKBuildVersion(StringRef SDKPath); std::string getSDKBuildVersionFromPlist(StringRef Path); diff --git a/include/swift/Basic/PossiblyUniquePtr.h b/include/swift/Basic/PossiblyUniquePtr.h new file mode 100644 index 0000000000000..04f4ff3e7ede3 --- /dev/null +++ b/include/swift/Basic/PossiblyUniquePtr.h @@ -0,0 +1,173 @@ +//===--- PossiblyUniquePtr.h - A dynamic smart pointer ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 PossiblyUniquePtr, a smart pointer template that +/// typically owns its pointee but can dynamically just contain an unowned +/// pointer. +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BASIC_POSSIBLY_UNIQUE_PTR_H +#define SWIFT_BASIC_POSSIBLY_UNIQUE_PTR_H + +#include "llvm/ADT/PointerIntPair.h" +#include "swift/Basic/Compiler.h" + +namespace swift { + +enum PointerIsOwned_t: bool { + PointerIsNotOwned = false, + PointerIsOwned = true +}; + +/// Essentially std::unique_ptr except it dynamically tracks whether the +/// pointer is actually owned. +/// +/// The operations which possibly transfer ownership into a PossiblyUniquePtr +/// (the constructor and `reset`) privilege the owning case. This allows +/// PossiblyUniquePtr to serve as a drop-in replacement for +/// std::unique_ptr. +template > +class SWIFT_TRIVIAL_ABI PossiblyUniquePtr { + llvm::PointerIntPair Value; + + // Befriend all other specializations of this class, for the use of the + // converting constructor and assignment operator. + template + friend class PossiblyUniquePtr; + +public: + PossiblyUniquePtr() {} + + // Allow implicit conversion from a null pointer. + PossiblyUniquePtr(std::nullptr_t) {} + + // Require conversion from any other pointer to be explicit. + explicit PossiblyUniquePtr(T *pointer, + PointerIsOwned_t owned = PointerIsOwned) + : Value(pointer, owned) { + assert((pointer != nullptr || !owned) && "claiming ownership of null pointer"); + } + + PossiblyUniquePtr(PossiblyUniquePtr &&other) + : Value(other.Value) { + other.dropValue(); + } + + PossiblyUniquePtr &operator=(PossiblyUniquePtr &&other) { + destroyValue(); + Value = other.Value; + other.dropValue(); + return *this; + } + + ~PossiblyUniquePtr() { + destroyValue(); + } + + // Converting constructor. + template + PossiblyUniquePtr(PossiblyUniquePtr &&other) + : Value(other.Value.getPointer(), other.Value.getInt()) { + other.dropValue(); + } + + // Converting assignment operator. + template + PossiblyUniquePtr &operator=(PossiblyUniquePtr &&other) { + destroyValue(); + Value.setPointerAndInt(other.Value.getPointer(), + other.Value.getInt()); + other.dropValue(); + return *this; + } + + template + static PossiblyUniquePtr make(Args &&...args) { + return PossiblyUniquePtr(new ObjectType(std::forward(args)...)); + } + + /// As a unique pointer, PossiblyUniquePtr is non-copyable. + PossiblyUniquePtr(const PossiblyUniquePtr &other) = delete; + PossiblyUniquePtr &operator=(const PossiblyUniquePtr &other) = delete; + + explicit operator bool() const { + return Value.getPointer() != nullptr; + } + + T &operator*() const { + return *get_nonnull(); + } + T *operator->() const { + return get_nonnull(); + } + + /// Reset the value of this object to the given pointer. + /// + /// Destroys the current value, if any. + void reset(T *pointer = nullptr, PointerIsOwned_t owned = PointerIsOwned) & { + destroyValue(); + Value.setPointerAndInt(pointer, owned); + } + + /// Borrow the current pointer value, leaving ownership (if any) with this object. + T *get() const { + return Value.getPointer(); + } + + /// Borrow the current pointer value, leaving ownership (if any) with this object. + /// Asserts that the pointer is non-null. + T *get_nonnull() const { + auto value = get(); + assert(value); + return value; + } + + /// Determines whether this object owns its pointer value. + PointerIsOwned_t isOwned() const { + return PointerIsOwned_t(Value.getInt()); + } + + /// Release ownership of the pointer, making the caller responsible for it. + /// This can only be called on an owned pointer. + T *releaseOwned() { + assert(isOwned()); + auto value = get(); + dropValue(); + return value; + } + +private: + /// Destroy the value, if we own it. + void destroyValue() { + if (Value.getInt()) { + delete Value.getPointer(); + } + } + + /// Clear the stored value without asserting ownership. + void dropValue() & { + Value.setPointerAndInt(nullptr, 0); + } +}; + +template +PossiblyUniquePtr make_possibly_unique(As &&...args) { + return PossiblyUniquePtr:: + template make(std::forward(args)...); +} + +} // end namespace swift + +#endif diff --git a/include/swift/Basic/RelativePointer.h b/include/swift/Basic/RelativePointer.h index a42801d0d7400..4d53652a949e7 100644 --- a/include/swift/Basic/RelativePointer.h +++ b/include/swift/Basic/RelativePointer.h @@ -132,7 +132,10 @@ #ifndef SWIFT_BASIC_RELATIVEPOINTER_H #define SWIFT_BASIC_RELATIVEPOINTER_H +#include #include +#include +#include namespace swift { diff --git a/include/swift/Basic/SimpleDisplay.h b/include/swift/Basic/SimpleDisplay.h index 349da1db5f661..742de8f8af583 100644 --- a/include/swift/Basic/SimpleDisplay.h +++ b/include/swift/Basic/SimpleDisplay.h @@ -19,6 +19,7 @@ #ifndef SWIFT_BASIC_SIMPLE_DISPLAY_H #define SWIFT_BASIC_SIMPLE_DISPLAY_H +#include "swift/Basic/LLVM.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/raw_ostream.h" #include @@ -167,7 +168,7 @@ namespace swift { if (const auto t = ptrUnion.template dyn_cast()) simple_display(out, t); else - simple_display(out, ptrUnion.template get()); + simple_display(out, cast(ptrUnion)); } } diff --git a/include/swift/Basic/SourceLoc.h b/include/swift/Basic/SourceLoc.h index 355e55763506d..c362c8e1ca229 100644 --- a/include/swift/Basic/SourceLoc.h +++ b/include/swift/Basic/SourceLoc.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 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -17,59 +17,98 @@ #ifndef SWIFT_BASIC_SOURCELOC_H #define SWIFT_BASIC_SOURCELOC_H +/// `SourceLoc.h` is imported into Swift. Be *very* careful with what you +/// include here and keep these includes minimal! +/// If you don't need to import a header into Swift, include it in the `#ifdef` +/// block below instead. +/// +/// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" +#include +#include + +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/SMLoc.h" -#include #include +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + +SWIFT_BEGIN_NULLABILITY_ANNOTATIONS + namespace swift { - class SourceManager; +class SourceManager; -/// SourceLoc in swift is just an SMLoc. We define it as a different type -/// (instead of as a typedef) just to remove the "getFromPointer" methods and -/// enforce purity in the Swift codebase. +/// `SourceLoc` just wraps a `const char *`. We define it as a different type +/// (instead of as a typedef) from `llvm::SMLoc` to enforce purity in the +/// Swift codebase. class SourceLoc { friend class SourceManager; friend class SourceRange; friend class CharSourceRange; friend class DiagnosticConsumer; - llvm::SMLoc Value; + const char *_Nullable Pointer = nullptr; public: SourceLoc() {} - explicit SourceLoc(llvm::SMLoc Value) : Value(Value) {} - - bool isValid() const { return Value.isValid(); } - bool isInvalid() const { return !isValid(); } +#ifdef COMPILED_WITH_SWIFT + SWIFT_NAME("init(raw:)") + SourceLoc(const void *_Nullable Pointer) : Pointer((const char *)Pointer) {} +#endif + + SWIFT_UNAVAILABLE("Use 'init(raw:)' instead") + static SourceLoc getFromPointer(const char *_Nullable Pointer) { + SourceLoc Loc; + Loc.Pointer = Pointer; + return Loc; + } - /// An explicit bool operator so one can check if a SourceLoc is valid in an - /// if statement: - /// - /// if (auto x = getSourceLoc()) { ... } - explicit operator bool() const { return isValid(); } + SWIFT_UNAVAILABLE("Use 'raw' instead") + const char *_Nullable getPointer() const { return Pointer; } + SWIFT_UNAVAILABLE("Use 'raw' instead") + const void *_Nullable getOpaquePointerValue() const { return Pointer; } +#ifdef COMPILED_WITH_SWIFT + SWIFT_COMPUTED_PROPERTY + const void *_Nullable getRaw() const { return getOpaquePointerValue(); } +#endif + + bool isValid() const { return Pointer != nullptr; } + bool isInvalid() const { return !isValid(); } - bool operator==(const SourceLoc &RHS) const { return RHS.Value == Value; } - bool operator!=(const SourceLoc &RHS) const { return !operator==(RHS); } - /// Return a source location advanced a specified number of bytes. + SWIFT_NAME("advanced(by:)") SourceLoc getAdvancedLoc(int ByteOffset) const { assert(isValid() && "Can't advance an invalid location"); - return SourceLoc( - llvm::SMLoc::getFromPointer(Value.getPointer() + ByteOffset)); + return SourceLoc::getFromPointer(Pointer + ByteOffset); } + SWIFT_UNAVAILABLE("Unavailable in Swift") SourceLoc getAdvancedLocOrInvalid(int ByteOffset) const { if (isValid()) return getAdvancedLoc(ByteOffset); return SourceLoc(); } - const void *getOpaquePointerValue() const { return Value.getPointer(); } +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + + /// An explicit bool operator so one can check if a SourceLoc is valid in an + /// if statement: + /// + /// if (auto x = getSourceLoc()) { ... } + explicit operator bool() const { return isValid(); } + + operator llvm::SMLoc() const { return llvm::SMLoc::getFromPointer(Pointer); } + + bool operator==(const SourceLoc &RHS) const { return RHS.Pointer == Pointer; } + bool operator!=(const SourceLoc &RHS) const { return !operator==(RHS); } /// Print out the SourceLoc. If this location is in the same buffer /// as specified by \c LastBufferID, then we don't print the filename. If @@ -88,13 +127,15 @@ class SourceLoc { SWIFT_DEBUG_DUMPER(dump(const SourceManager &SM)); - friend size_t hash_value(SourceLoc loc) { - return reinterpret_cast(loc.getOpaquePointerValue()); - } + friend size_t hash_value(SourceLoc loc) { + return reinterpret_cast(loc.getOpaquePointerValue()); + } + + friend void simple_display(raw_ostream &OS, const SourceLoc &loc) { + // Nothing meaningful to print. + } - friend void simple_display(raw_ostream &OS, const SourceLoc &loc) { - // Nothing meaningful to print. - } +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE }; /// SourceRange in swift is a pair of locations. However, note that the end @@ -103,10 +144,18 @@ class SourceLoc { /// sure that proper conversions happen where important. class SourceRange { public: - SourceLoc Start, End; + SWIFT_NAME("start") + SourceLoc Start; + + SWIFT_NAME("end") + SourceLoc End; SourceRange() {} + + SWIFT_NAME("init(start:)") SourceRange(SourceLoc Loc) : Start(Loc), End(Loc) {} + + SWIFT_NAME("init(start:end:)") SourceRange(SourceLoc Start, SourceLoc End) : Start(Start), End(End) { assert(Start.isValid() == End.isValid() && "Start and end should either both be valid or both be invalid!"); @@ -115,6 +164,9 @@ class SourceRange { bool isValid() const { return Start.isValid(); } bool isInvalid() const { return !isValid(); } +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + /// An explicit bool operator so one can check if a SourceRange is valid in an /// if statement: /// @@ -172,6 +224,7 @@ class SourceRange { // Nothing meaningful to print. } +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE }; /// A half-open character-based source range. @@ -183,9 +236,13 @@ class CharSourceRange { /// Constructs an invalid range. CharSourceRange() = default; + SWIFT_NAME("init(start:byteLength:)") CharSourceRange(SourceLoc Start, unsigned ByteLength) : Start(Start), ByteLength(ByteLength) {} +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + /// Constructs a character range which starts and ends at the /// specified character locations. CharSourceRange(const SourceManager &SM, SourceLoc Start, SourceLoc End); @@ -193,44 +250,51 @@ class CharSourceRange { /// Use Lexer::getCharSourceRangeFromSourceRange() instead. CharSourceRange(const SourceManager &SM, SourceRange Range) = delete; +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + bool isValid() const { return Start.isValid(); } bool isInvalid() const { return !isValid(); } - bool operator==(const CharSourceRange &other) const { - return Start == other.Start && ByteLength == other.ByteLength; - } - bool operator!=(const CharSourceRange &other) const { - return !operator==(other); - } - + SWIFT_COMPUTED_PROPERTY SourceLoc getStart() const { return Start; } + SWIFT_COMPUTED_PROPERTY SourceLoc getEnd() const { return Start.getAdvancedLocOrInvalid(ByteLength); } + /// Return the length of this valid range in bytes. Can be zero. + SWIFT_COMPUTED_PROPERTY + unsigned getByteLength() const { + assert(isValid() && "length does not make sense for an invalid range"); + return ByteLength; + } + +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + /// Returns true if the given source location is contained in the range. bool contains(SourceLoc loc) const { auto less = std::less(); auto less_equal = std::less_equal(); - return less_equal(getStart().Value.getPointer(), loc.Value.getPointer()) && - less(loc.Value.getPointer(), getEnd().Value.getPointer()); + return less_equal(getStart().Pointer, loc.Pointer) && + less(loc.Pointer, getEnd().Pointer); } bool contains(CharSourceRange Other) const { auto less_equal = std::less_equal(); return contains(Other.getStart()) && - less_equal(Other.getEnd().Value.getPointer(), getEnd().Value.getPointer()); + less_equal(Other.getEnd().Pointer, getEnd().Pointer); } /// expands *this to cover Other void widen(CharSourceRange Other) { - auto Diff = Other.getEnd().Value.getPointer() - getEnd().Value.getPointer(); + auto Diff = Other.getEnd().Pointer - getEnd().Pointer; if (Diff > 0) { ByteLength += Diff; } - const auto MyStartPtr = getStart().Value.getPointer(); - Diff = MyStartPtr - Other.getStart().Value.getPointer(); + const auto MyStartPtr = getStart().Pointer; + Diff = MyStartPtr - Other.getStart().Pointer; if (Diff > 0) { ByteLength += Diff; - Start = SourceLoc(llvm::SMLoc::getFromPointer(MyStartPtr - Diff)); + Start = SourceLoc::getFromPointer(MyStartPtr - Diff); } } @@ -239,16 +303,15 @@ class CharSourceRange { return contains(Other.getStart()) || Other.contains(getStart()); } - StringRef str() const { - return StringRef(Start.Value.getPointer(), ByteLength); - } + StringRef str() const { return StringRef(Start.Pointer, ByteLength); } - /// Return the length of this valid range in bytes. Can be zero. - unsigned getByteLength() const { - assert(isValid() && "length does not make sense for an invalid range"); - return ByteLength; + bool operator==(const CharSourceRange &other) const { + return Start == other.Start && ByteLength == other.ByteLength; } - + bool operator!=(const CharSourceRange &other) const { + return !operator==(other); + } + /// Print out the CharSourceRange. If the locations are in the same buffer /// as specified by LastBufferID, then we don't print the filename. If not, /// we do print the filename, and then update LastBufferID with the BufferID @@ -261,26 +324,31 @@ class CharSourceRange { unsigned Tmp = ~0U; print(OS, SM, Tmp, PrintText); } - + SWIFT_DEBUG_DUMPER(dump(const SourceManager &SM)); + +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE }; } // end namespace swift +// Not imported into Swift in pure bridging mode. +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + namespace llvm { template struct DenseMapInfo; template <> struct DenseMapInfo { static swift::SourceLoc getEmptyKey() { - return swift::SourceLoc( - SMLoc::getFromPointer(DenseMapInfo::getEmptyKey())); + return swift::SourceLoc::getFromPointer( + DenseMapInfo::getEmptyKey()); } static swift::SourceLoc getTombstoneKey() { // Make this different from empty key. See for context: // http://lists.llvm.org/pipermail/llvm-dev/2015-July/088744.html - return swift::SourceLoc( - SMLoc::getFromPointer(DenseMapInfo::getTombstoneKey())); + return swift::SourceLoc::getFromPointer( + DenseMapInfo::getTombstoneKey()); } static unsigned getHashValue(const swift::SourceLoc &Val) { @@ -296,15 +364,15 @@ template <> struct DenseMapInfo { template <> struct DenseMapInfo { static swift::SourceRange getEmptyKey() { - return swift::SourceRange(swift::SourceLoc( - SMLoc::getFromPointer(DenseMapInfo::getEmptyKey()))); + return swift::SourceRange(swift::SourceLoc::getFromPointer( + DenseMapInfo::getEmptyKey())); } static swift::SourceRange getTombstoneKey() { // Make this different from empty key. See for context: // http://lists.llvm.org/pipermail/llvm-dev/2015-July/088744.html - return swift::SourceRange(swift::SourceLoc( - SMLoc::getFromPointer(DenseMapInfo::getTombstoneKey()))); + return swift::SourceRange(swift::SourceLoc::getFromPointer( + DenseMapInfo::getTombstoneKey())); } static unsigned getHashValue(const swift::SourceRange &Val) { @@ -319,4 +387,8 @@ template <> struct DenseMapInfo { }; } // namespace llvm +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE + +SWIFT_END_NULLABILITY_ANNOTATIONS + #endif // SWIFT_BASIC_SOURCELOC_H diff --git a/include/swift/Basic/SourceManager.h b/include/swift/Basic/SourceManager.h index 60cbb2b093d21..3fde295946973 100644 --- a/include/swift/Basic/SourceManager.h +++ b/include/swift/Basic/SourceManager.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 - 2025 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 @@ -13,6 +13,7 @@ #ifndef SWIFT_BASIC_SOURCEMANAGER_H #define SWIFT_BASIC_SOURCEMANAGER_H +#include "swift/AST/ClangNode.h" #include "swift/Basic/FileSystem.h" #include "swift/Basic/SourceLoc.h" #include "clang/Basic/FileManager.h" @@ -22,6 +23,7 @@ #include "llvm/Support/SourceMgr.h" #include #include +#include #include namespace swift { @@ -122,6 +124,10 @@ class GeneratedSourceInfo { /// Contains the ancestors of this source buffer, starting with the root source /// buffer and ending at this source buffer. mutable llvm::ArrayRef ancestors = llvm::ArrayRef(); + + /// Clang node where this buffer comes from. This should be set when this is + /// an 'AttributeFromClang'. + ClangNode clangNode = ClangNode(); }; /// This class manages and owns source buffers. @@ -305,7 +311,7 @@ class SourceManager { /// Returns true if \c LHS is before \c RHS in the same source buffer. bool isBeforeInBuffer(SourceLoc LHS, SourceLoc RHS) const { - return LHS.Value.getPointer() < RHS.Value.getPointer(); + return LHS.getPointer() < RHS.getPointer(); } /// Returns true if \c range contains the location \c loc. The location @@ -479,7 +485,7 @@ class SourceManager { assert(Loc.isValid()); int LineOffset = getLineOffset(Loc); int l, c; - std::tie(l, c) = LLVMSourceMgr.getLineAndColumn(Loc.Value, BufferID); + std::tie(l, c) = LLVMSourceMgr.getLineAndColumn(Loc, BufferID); assert(LineOffset+l > 0 && "bogus line offset"); return { LineOffset + l, c }; } @@ -492,7 +498,7 @@ class SourceManager { std::pair getLineAndColumnInBuffer(SourceLoc Loc, unsigned BufferID = 0) const { assert(Loc.isValid()); - return LLVMSourceMgr.getLineAndColumn(Loc.Value, BufferID); + return LLVMSourceMgr.getLineAndColumn(Loc, BufferID); } /// Returns the column for the given source location in the given buffer. diff --git a/include/swift/Basic/Statistic.h b/include/swift/Basic/Statistic.h index 8204e826bb9b7..1e5de730f4649 100644 --- a/include/swift/Basic/Statistic.h +++ b/include/swift/Basic/Statistic.h @@ -411,10 +411,10 @@ template FrontendStatsTracer make_tracer_pointerunion(UnifiedStatsReporter *Reporter, StringRef Name, llvm::PointerUnion Value) { - if (Value.template is()) - return make_tracer_direct(Reporter, Name, Value.template get()); + if (isa(Value)) + return make_tracer_direct(Reporter, Name, cast(Value)); else - return make_tracer_direct(Reporter, Name, Value.template get()); + return make_tracer_direct(Reporter, Name, cast(Value)); } template diff --git a/include/swift/Basic/SupportedFeatures.h b/include/swift/Basic/SupportedFeatures.h new file mode 100644 index 0000000000000..e078792b4dbc6 --- /dev/null +++ b/include/swift/Basic/SupportedFeatures.h @@ -0,0 +1,28 @@ +//===--- SupportedFeatures.h - Supported Features Output --------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 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 a high-level API for supported features info +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SUPPORTEDFEATURES_H +#define SWIFT_SUPPORTEDFEATURES_H + +#include "swift/Basic/LLVM.h" + +namespace swift { +namespace features { +void printSupportedFeatures(llvm::raw_ostream &out); +} // namespace features +} // namespace swift + +#endif diff --git a/include/swift/Basic/SwiftBridging.h b/include/swift/Basic/SwiftBridging.h new file mode 100644 index 0000000000000..6cf10c8d90c0b --- /dev/null +++ b/include/swift/Basic/SwiftBridging.h @@ -0,0 +1,124 @@ +//===--- Basic/SwiftBridging.h ----------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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 is a wrapper around `` that redefines `SWIFT_NAME` to +/// accept a string literal, and some other helpful macros, including fallbacks +/// for when `` is unavailable (e.g. during bootstrapping). +/// +/// String literals enable us to properly format the long Swift declaration +/// names specified via `SWIFT_NAME` that many of our bridging functions have. +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BASIC_SWIFT_BRIDGING_H +#define SWIFT_BASIC_SWIFT_BRIDGING_H + +#include "swift/Basic/Compiler.h" +#include "swift/Basic/Nullability.h" +#if __has_include() +#include +#else + +#if __has_attribute(swift_attr) + +/// Specifies that a C++ `class` or `struct` owns and controls the lifetime of +/// all of the objects it references. Such type should not reference any +/// objects whose lifetime is controlled externally. This annotation allows +/// Swift to import methods that return a `class` or `struct` type that is +/// annotated with this macro. +#define SWIFT_SELF_CONTAINED __attribute__((swift_attr("import_owned"))) + +/// Specifies that a specific C++ method should be imported as a computed +/// property. If this macro is specified on a getter, a getter will be +/// synthesized. If this macro is specified on a setter, both a getter and +/// setter will be synthesized. +/// +/// For example: +/// ``` +/// int getX() SWIFT_COMPUTED_PROPERTY; +/// ``` +/// Will be imported as `var x: CInt {...}`. +#define SWIFT_COMPUTED_PROPERTY \ + __attribute__((swift_attr("import_computed_property"))) + +#define _CXX_INTEROP_STRINGIFY(_x) #_x + +/// Specifies that a specific C++ `class` or `struct` conforms to a +/// a specific Swift protocol. +/// +/// This example shows how to use this macro to conform a class template to a +/// Swift protocol: +/// ``` +/// template +/// class SWIFT_CONFORMS_TO_PROTOCOL(SwiftModule.ProtocolName) +/// CustomClass {}; +/// ``` +// clang-format off +#define SWIFT_CONFORMS_TO_PROTOCOL(_moduleName_protocolName) \ + __attribute__((swift_attr( \ + _CXX_INTEROP_STRINGIFY(conforms_to:_moduleName_protocolName)))) +// clang-format on + +#else // #if __has_attribute(swift_attr) + +#define SWIFT_SELF_CONTAINED +#define SWIFT_COMPUTED_PROPERTY +#define SWIFT_CONFORMS_TO_PROTOCOL(_moduleName_protocolName) + +#endif // #if __has_attribute(swift_attr) + +#endif // #if __has_include() + +// Redefine SWIFT_NAME. +#ifdef SWIFT_NAME +#undef SWIFT_NAME +#endif + +#if __has_attribute(swift_name) +/// Specifies a name that will be used in Swift for this declaration instead of +/// its original name. +#define SWIFT_NAME(_name) __attribute__((swift_name(_name))) +#else +#define SWIFT_NAME(_name) +#endif + +#if __has_attribute(availability) +#define SWIFT_UNAVAILABLE(msg) \ + __attribute__((availability(swift, unavailable, message = msg))) +#else +#define SWIFT_UNAVAILABLE(msg) +#endif + +#if !(defined(COMPILED_WITH_SWIFT) && defined(PURE_BRIDGING_MODE)) +/// Use this macro in a `#ifdef`/`#endif` fashion to wrap code that should not +/// be imported into Swift in pure bridging mode, e.g. because an API is +/// irrelevant on the Swift side, or because it requires std/llvm headers, which +/// we don't want to import in this mode. +/// +/// - Important: Do not put a constructor inside a +/// `NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE` block unless there already is +/// another unconditionally available user-defined constructor! +/// +/// Note: On Windows ARM64, how a C++ struct/class value type is +/// returned is sensitive to conditions including whether a +/// user-defined constructor exists, etc. See +/// https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#return-values +/// +/// So, if a C++ struct/class type is returned as a value between Swift +/// and C++, we need to be careful to match the return convention +/// matches between the `NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE` (C++) side +/// and the non-`NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE` (Swift) side. +#define NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE +#endif + +#endif // SWIFT_BASIC_SWIFT_BRIDGING_H + diff --git a/include/swift/Basic/TaskQueue.h b/include/swift/Basic/TaskQueue.h index 758815601be9c..f40af64169098 100644 --- a/include/swift/Basic/TaskQueue.h +++ b/include/swift/Basic/TaskQueue.h @@ -205,8 +205,8 @@ class TaskQueue { /// \param Context an optional context which will be associated with the task /// \param SeparateErrors Controls whether error output is reported separately virtual void addTask(const char *ExecPath, ArrayRef Args, - ArrayRef Env = std::nullopt, - void *Context = nullptr, bool SeparateErrors = false); + ArrayRef Env = {}, void *Context = nullptr, + bool SeparateErrors = false); /// Synchronously executes the tasks in the TaskQueue. /// @@ -240,8 +240,8 @@ class DummyTaskQueue : public TaskQueue { bool SeparateErrors; DummyTask(const char *ExecPath, ArrayRef Args, - ArrayRef Env = std::nullopt, - void *Context = nullptr, bool SeparateErrors = false) + ArrayRef Env = {}, void *Context = nullptr, + bool SeparateErrors = false) : ExecPath(ExecPath), Args(Args), Env(Env), Context(Context), SeparateErrors(SeparateErrors) {} }; @@ -254,8 +254,8 @@ class DummyTaskQueue : public TaskQueue { virtual ~DummyTaskQueue(); void addTask(const char *ExecPath, ArrayRef Args, - ArrayRef Env = std::nullopt, - void *Context = nullptr, bool SeparateErrors = false) override; + ArrayRef Env = {}, void *Context = nullptr, + bool SeparateErrors = false) override; bool execute(TaskBeganCallback Began = TaskBeganCallback(), diff --git a/include/swift/Basic/Version.h b/include/swift/Basic/Version.h index 092a4e626e518..e43ce5c7b01e5 100644 --- a/include/swift/Basic/Version.h +++ b/include/swift/Basic/Version.h @@ -131,6 +131,13 @@ class Version { /// SWIFT_VERSION_MINOR. static Version getCurrentLanguageVersion(); + /// Returns a major version to represent the next future language mode. This + /// exists to make it easier to find and update clients when a new language + /// mode is added. + static constexpr unsigned getFutureMajorLanguageVersion() { + return 7; + } + // List of backward-compatibility versions that we permit passing as // -swift-version static std::array getValidEffectiveVersions() { @@ -164,11 +171,6 @@ std::string getSwiftFullVersion(Version effectiveLanguageVersion = /// this Swift was built. StringRef getSwiftRevision(); -/// Is the running compiler built with a version tag for distribution? -/// When true, \c version::getCurrentCompilerVersion returns a valid version -/// and \c getCurrentCompilerTag returns the version tuple in string format. -bool isCurrentCompilerTagged(); - /// Retrieves the distribution tag of the running compiler, if any. StringRef getCurrentCompilerTag(); diff --git a/include/swift/Bridging/ASTGen.h b/include/swift/Bridging/ASTGen.h index afef5603a99c7..857a82edf42e7 100644 --- a/include/swift/Bridging/ASTGen.h +++ b/include/swift/Bridging/ASTGen.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2023 - 2025 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,13 +28,21 @@ void swift_ASTGen_addQueuedSourceFile( void swift_ASTGen_addQueuedDiagnostic( void *_Nonnull queued, void *_Nonnull state, BridgedStringRef text, - BridgedDiagnosticSeverity severity, - BridgedSourceLoc sourceLoc, + swift::DiagnosticKind severity, + swift::SourceLoc sourceLoc, BridgedStringRef categoryName, BridgedStringRef documentationPath, - const BridgedCharSourceRange *_Nullable highlightRanges, + const swift::CharSourceRange *_Nullable highlightRanges, ptrdiff_t numHighlightRanges, BridgedArrayRef /*BridgedFixIt*/ fixIts); +void swift_ASTGen_renderSingleDiagnostic( + void *_Nonnull state, + BridgedStringRef text, + swift::DiagnosticKind severity, + BridgedStringRef categoryName, + BridgedStringRef documentationPath, + ssize_t colorize, + BridgedStringRef *_Nonnull renderedString); void swift_ASTGen_renderQueuedDiagnostics( void *_Nonnull queued, ssize_t contextSize, ssize_t colorize, BridgedStringRef *_Nonnull renderedString); @@ -89,7 +97,7 @@ bool swift_ASTGen_parseRegexLiteral( BridgedStringRef inputPtr, size_t *_Nonnull versionOut, void *_Nonnull UnsafeMutableRawPointer, size_t captureStructureSize, BridgedRegexLiteralPatternFeatures *_Nonnull featuresOut, - BridgedSourceLoc diagLoc, BridgedDiagnosticEngine diagEngine); + swift::SourceLoc diagLoc, BridgedDiagnosticEngine diagEngine); void swift_ASTGen_freeBridgedRegexLiteralPatternFeatures( BridgedRegexLiteralPatternFeatures features); @@ -109,10 +117,15 @@ intptr_t swift_ASTGen_configuredRegions( void swift_ASTGen_freeConfiguredRegions( BridgedIfConfigClauseRangeInfo *_Nullable regions, intptr_t numRegions); +intptr_t swift_ASTGen_activeInEmbeddedSwift( + BridgedASTContext astContext, + void *_Nonnull sourceFile, + swift::SourceLoc location); + bool swift_ASTGen_validateUnqualifiedLookup( void *_Nonnull sourceFile, BridgedASTContext astContext, - BridgedSourceLoc sourceLoc, + swift::SourceLoc sourceLoc, bool finishInSequentialScope, BridgedArrayRef astScopeResultRef); diff --git a/include/swift/Bridging/BasicSwift.h b/include/swift/Bridging/BasicSwift.h new file mode 100644 index 0000000000000..a2cb3f8fea606 --- /dev/null +++ b/include/swift/Bridging/BasicSwift.h @@ -0,0 +1,32 @@ +//===--- BasicSwift.h -------------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 - 2025 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_BRIDGING_BASICSWIFT_H +#define SWIFT_BRIDGING_BASICSWIFT_H + +#include "swift/Basic/BasicBridging.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Create a new static build configuration for the given language options. +void * _Nonnull swift_Basic_createStaticBuildConfiguration(BridgedLangOptions cLangOpts); + +/// Free the given static build configuration. +void swift_Basic_freeStaticBuildConfiguration(void * _Nonnull staticBuildConfiguration); + +#ifdef __cplusplus +} +#endif + +#endif // SWIFT_BRIDGING_BASICSWIFT_H diff --git a/include/swift/Bridging/MacroEvaluation.h b/include/swift/Bridging/MacroEvaluation.h index 1df6e130ee1db..650accc61122c 100644 --- a/include/swift/Bridging/MacroEvaluation.h +++ b/include/swift/Bridging/MacroEvaluation.h @@ -14,6 +14,7 @@ #define SWIFT_BRIDGING_MACROS_H #include "swift/Basic/BasicBridging.h" +#include "swift/AST/ASTBridging.h" #ifdef __cplusplus extern "C" { @@ -40,13 +41,13 @@ void swift_Macros_freeExpansionReplacements( ptrdiff_t *_Nullable replacementsPtr, ptrdiff_t numReplacements); ptrdiff_t swift_Macros_expandFreestandingMacro( - void *_Nonnull diagEngine, const void *_Nonnull macro, + BridgedASTContext cContext, const void *_Nonnull macro, const char *_Nonnull discriminator, uint8_t rawMacroRole, void *_Nonnull sourceFile, const void *_Nullable sourceLocation, BridgedStringRef *_Nonnull evaluatedSourceOut); ptrdiff_t swift_Macros_expandAttachedMacro( - void *_Nonnull diagEngine, const void *_Nonnull macro, + BridgedASTContext cContext, const void *_Nonnull macro, const char *_Nonnull discriminator, const char *_Nonnull qualifiedType, const char *_Nonnull conformances, uint8_t rawMacroRole, void *_Nonnull customAttrSourceFile, diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index de1091ba12cd5..ae932bf689192 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -19,6 +19,7 @@ #include "swift/AST/Attr.h" #include "swift/AST/AttrKind.h" #include "swift/AST/ClangModuleLoader.h" +#include "clang/AST/Attr.h" #include "clang/Basic/Specifiers.h" #include "llvm/Support/VirtualFileSystem.h" @@ -64,12 +65,14 @@ namespace tooling { namespace dependencies { struct ModuleDeps; struct TranslationUnitDeps; + enum class ModuleOutputKind; using ModuleDepsGraph = std::vector; } } } namespace swift { +enum class ResultConvention : uint8_t; class ASTContext; class CompilerInvocation; class ClangImporterOptions; @@ -162,8 +165,7 @@ class ClangImporter final : public ClangModuleLoader { bool requiresBuiltinHeadersInSystemModules = false; - ClangImporter(ASTContext &ctx, - DependencyTracker *tracker, + ClangImporter(ASTContext &ctx, DependencyTracker *tracker, DWARFImporterDelegate *dwarfImporterDelegate); /// Creates a clone of Clang importer's compiler instance that has been @@ -195,8 +197,8 @@ class ClangImporter final : public ClangModuleLoader { /// \returns a new Clang module importer, or null (with a diagnostic) if /// an error occurred. static std::unique_ptr - create(ASTContext &ctx, - std::string swiftPCHHash = "", DependencyTracker *tracker = nullptr, + create(ASTContext &ctx, std::string swiftPCHHash = "", + DependencyTracker *tracker = nullptr, DWARFImporterDelegate *dwarfImporterDelegate = nullptr, bool ignoreFileMapping = false); @@ -209,8 +211,7 @@ class ClangImporter final : public ClangModuleLoader { bool ignoreClangTarget = false); std::vector - getClangDepScanningInvocationArguments(ASTContext &ctx, - std::optional sourceFileName = std::nullopt); + getClangDepScanningInvocationArguments(ASTContext &ctx); static std::unique_ptr createClangInvocation(ClangImporter *importer, @@ -428,6 +429,16 @@ class ClangImporter final : public ClangModuleLoader { bool trackParsedSymbols = false, bool implicitImport = false); + /// Bind the bridging header content to the module. + /// + /// \param adapter The module that depends on the contents of this header. + /// \param diagLoc A location to attach any diagnostics to if import fails. + /// + /// \returns true if there was an error importing the header. + /// + /// \sa importBridgingHeader + bool bindBridgingHeader(ModuleDecl *adapter, SourceLoc diagLoc); + /// Returns the module that contains imports and declarations from all loaded /// Objective-C header files. /// @@ -462,11 +473,6 @@ class ClangImporter final : public ClangModuleLoader { /// Reads the original source file name from PCH. std::string getOriginalSourceFile(StringRef PCHFilename); - /// Add clang dependency file names. - /// - /// \param files The list of file to append dependencies to. - void addClangInvovcationDependencies(std::vector &files); - /// Makes a temporary replica of the ClangImporter's CompilerInstance, reads a /// module map into the replica and emits a PCM file for one of the modules it /// declares. Delegates to clang for everything except construction of the @@ -486,55 +492,11 @@ class ClangImporter final : public ClangModuleLoader { void verifyAllModules() override; - using RemapPathCallback = llvm::function_ref; - llvm::SmallVector, 1> - bridgeClangModuleDependencies( - clang::tooling::dependencies::DependencyScanningTool &clangScanningTool, - clang::tooling::dependencies::ModuleDepsGraph &clangDependencies, - StringRef moduleOutputPath, StringRef stableModuleOutputPath, - RemapPathCallback remapPath = nullptr); - - llvm::SmallVector, 1> - getModuleDependencies(Identifier moduleName, StringRef moduleOutputPath, StringRef sdkModuleOutputPath, - const llvm::DenseSet &alreadySeenClangModules, - clang::tooling::dependencies::DependencyScanningTool &clangScanningTool, - InterfaceSubContextDelegate &delegate, - llvm::PrefixMapper *mapper, - bool isTestableImport = false) override; - - void recordBridgingHeaderOptions( - ModuleDependencyInfo &MDI, - const clang::tooling::dependencies::TranslationUnitDeps &deps); - - void getBridgingHeaderOptions( + static void getBridgingHeaderOptions( + const ASTContext &ctx, const clang::tooling::dependencies::TranslationUnitDeps &deps, std::vector &swiftArgs); - /// Query dependency information for header dependencies - /// of a binary Swift module. - /// - /// \param moduleID the name of the Swift module whose dependency - /// information will be augmented with information about the given - /// textual header inputs. - /// - /// \param headerPath the path to the header to be scanned. - /// - /// \param clangScanningTool The clang dependency scanner. - /// - /// \param cache The module dependencies cache to update, with information - /// about new Clang modules discovered along the way. - /// - /// \returns \c true if an error occurred, \c false otherwise - bool getHeaderDependencies( - ModuleDependencyID moduleID, std::optional headerPath, - std::optional sourceBuffer, - clang::tooling::dependencies::DependencyScanningTool &clangScanningTool, - ModuleDependenciesCache &cache, - ModuleDependencyIDSetVector &headerClangModuleDependencies, - std::vector &headerFileInputs, - std::vector &bridgingHeaderCommandLine, - std::optional &includeTreeID); - clang::TargetInfo &getModuleAvailabilityTarget() const override; clang::ASTContext &getClangASTContext() const override; clang::Preprocessor &getClangPreprocessor() const override; @@ -654,6 +616,8 @@ class ClangImporter final : public ClangModuleLoader { FuncDecl *getDefaultArgGenerator(const clang::ParmVarDecl *param) override; + FuncDecl *getAvailabilityDomainPredicate(const clang::VarDecl *var) override; + bool isAnnotatedWith(const clang::CXXMethodDecl *method, StringRef attr); /// Find the lookup table that corresponds to the given Clang module. @@ -672,6 +636,8 @@ class ClangImporter final : public ClangModuleLoader { ValueDecl *importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext, ClangInheritanceInfo inheritance) override; + ValueDecl *getOriginalForClonedMember(const ValueDecl *decl) override; + /// Emits diagnostics for any declarations named name /// whose direct declaration context is a TU. void diagnoseTopLevelValue(const DeclName &name) override; @@ -680,12 +646,6 @@ class ClangImporter final : public ClangModuleLoader { /// of the provided baseType. void diagnoseMemberValue(const DeclName &name, const Type &baseType) override; - /// Enable the symbolic import experimental feature for the given callback. - void withSymbolicFeatureEnabled(llvm::function_ref callback); - - /// Returns true when the symbolic import experimental feature is enabled. - bool isSymbolicImportEnabled() const; - const clang::TypedefType *getTypeDefForCXXCFOptionsDefinition( const clang::Decl *candidateDecl) override; @@ -710,6 +670,24 @@ getModuleCachePathFromClang(const clang::CompilerInstance &Instance); bool isCompletionHandlerParamName(StringRef paramName); namespace importer { +/// Returns true if the given C/C++ reference type uses "immortal" +/// retain/release functions. +bool hasImmortalAttrs(const clang::RecordDecl *decl); + +struct ReturnOwnershipInfo { + ReturnOwnershipInfo(const clang::NamedDecl *decl); + + bool hasRetainAttr() const { + return hasReturnsRetained || hasReturnsUnretained; + } + bool hasConflictingAttr() const { + return hasReturnsRetained && hasReturnsUnretained; + } + +private: + bool hasReturnsRetained = false; + bool hasReturnsUnretained = false; +}; /// Returns true if the given module has a 'cplusplus' requirement. bool requiresCPlusPlus(const clang::Module *module); @@ -732,12 +710,13 @@ getCxxReferencePointeeTypeOrNone(const clang::Type *type); /// Returns true if the given type is a C++ `const` reference type. bool isCxxConstReferenceType(const clang::Type *type); +/// Determine whether the given Clang record declaration has one of the +/// attributes that makes it import as a reference types. +bool hasImportAsRefAttr(const clang::RecordDecl *decl); + /// Determine whether this typedef is a CF type. bool isCFTypeDecl(const clang::TypedefNameDecl *Decl); -/// Determine whether type is a c++ foreign reference type. -bool isForeignReferenceTypeWithoutImmortalAttrs(const clang::QualType type); - /// Determine the imported CF type for the given typedef-name, or the empty /// string if this is not an imported CF type name. llvm::StringRef getCFTypeName(const clang::TypedefNameDecl *decl); @@ -773,6 +752,116 @@ getPrivateFileIDAttrs(const clang::CXXRecordDecl *decl); /// /// Returns false if \a decl was not imported by ClangImporter. bool declIsCxxOnly(const Decl *decl); + +/// Is this DeclContext an `enum` that represents a C++ namespace? +bool isClangNamespace(const DeclContext *dc); + +/// For some \a templatedClass that inherits from \a base, whether they are +/// derived from the same class template. +/// +/// This kind of circular inheritance can happen when a templated class inherits +/// from a specialization of itself, e.g.: +/// +/// template class C; +/// template <> class C { /* ... */ }; +/// template class C : C { /* ... */ }; +/// +/// Checking for this kind of scenario is necessary for avoiding infinite +/// recursion during symbolic imports (importSymbolicCXXDecls), where +/// specialized class templates are instead imported as unspecialized. +bool isSymbolicCircularBase(const clang::CXXRecordDecl *templatedClass, + const clang::RecordDecl *base); + +/// Match a `[[swift_attr("...")]]` annotation on the given Clang decl. +/// +/// \param decl The Clang declaration to inspect. +/// \param patterns List of (attribute name, value) pairs. +/// \returns The value for the first matching attribute, or `std::nullopt`. +template +std::optional +matchSwiftAttr(const clang::Decl *decl, + llvm::ArrayRef> patterns) { + if (!decl || !decl->hasAttrs()) + return std::nullopt; + + for (const auto *attr : decl->getAttrs()) { + if (const auto *swiftAttr = llvm::dyn_cast(attr)) { + for (const auto &p : patterns) { + if (swiftAttr->getAttribute() == p.first) + return p.second; + } + } + } + return std::nullopt; +} + +/// Like `matchSwiftAttr`, but also searches C++ base classes. +/// +/// \param decl The Clang declaration to inspect. +/// \param patterns List of (attribute name, value) pairs. +/// \returns The matched value from this decl or its bases, or `std::nullopt`. +template +std::optional matchSwiftAttrConsideringInheritance( + const clang::Decl *decl, + llvm::ArrayRef> patterns) { + if (!decl) + return std::nullopt; + + if (auto match = matchSwiftAttr(decl, patterns)) + return match; + + if (const auto *recordDecl = llvm::dyn_cast(decl)) { + std::optional result; + if (recordDecl->isCompleteDefinition()) { + recordDecl->forallBases([&](const clang::CXXRecordDecl *base) -> bool { + if (auto baseMatch = matchSwiftAttr(base, patterns)) { + result = baseMatch; + return false; + } + + return true; + }); + + return result; + } + } + + return std::nullopt; +} + +/// Matches a `swift_attr("...")` on the record type pointed to by the given +/// Clang type, searching base classes if it's a C++ class. +/// +/// \param type A Clang pointer or reference type. +/// \param patterns List of attribute name-value pairs to match. +/// \returns Matched value or std::nullopt. +template +std::optional matchSwiftAttrOnRecordPtr( + const clang::QualType &type, + llvm::ArrayRef> patterns) { + clang::QualType pointeeType; + if (const auto *ptrType = type->getAs()) { + pointeeType = ptrType->getPointeeType(); + } else if (const auto *refType = type->getAs()) { + pointeeType = refType->getPointeeType(); + } else { + return std::nullopt; + } + + if (const auto *recordDecl = pointeeType->getAsRecordDecl()) { + return matchSwiftAttrConsideringInheritance(recordDecl, patterns); + } + return std::nullopt; +} + +/// Determines the C++ reference ownership convention for the return value +/// using `SWIFT_RETURNS_(UN)RETAINED` on the API; falls back to +/// `SWIFT_RETURNED_AS_(UN)RETAINED_BY_DEFAULT` on the pointee record type. +/// +/// \param decl The Clang function or method declaration to inspect. +/// \returns Matched `ResultConvention`, or `std::nullopt` if none applies. +std::optional +getCxxRefConventionWithAttrs(const clang::Decl *decl); } // namespace importer struct ClangInvocationFileMapping { @@ -793,10 +882,19 @@ struct ClangInvocationFileMapping { /// `suppressDiagnostic` prevents us from emitting warning messages when we /// are unable to find headers. ClangInvocationFileMapping getClangInvocationFileMapping( - ASTContext &ctx, + const ASTContext &ctx, llvm::IntrusiveRefCntPtr vfs = nullptr, bool suppressDiagnostic = false); +/// Apply the given file mapping to the specified 'fileSystem', used +/// primarily to inject modulemaps on platforms with non-modularized +/// platform libraries. +ClangInvocationFileMapping applyClangInvocationMapping( + const ASTContext &ctx, + llvm::IntrusiveRefCntPtr baseVFS, + llvm::IntrusiveRefCntPtr &fileSystem, + bool suppressDiagnostics = false); + /// Information used to compute the access level of inherited C++ members. class ClangInheritanceInfo { /// The cumulative inheritance access specifier, that is used to compute the diff --git a/include/swift/ClangImporter/ClangImporterRequests.h b/include/swift/ClangImporter/ClangImporterRequests.h index e05db6edf2378..fe83d0fae0405 100644 --- a/include/swift/ClangImporter/ClangImporterRequests.h +++ b/include/swift/ClangImporter/ClangImporterRequests.h @@ -342,15 +342,9 @@ class ObjCInterfaceAndImplementationRequest }; enum class CxxRecordSemanticsKind { - Trivial, - Owned, - MoveOnly, + Value, Reference, Iterator, - // A record that is either not copyable/movable or not destructible. - MissingLifetimeOperation, - // A record that has no copy and no move operations - UnavailableConstructors, // A C++ record that represents a Swift class type exposed to C++ from Swift. SwiftClassType }; @@ -576,10 +570,91 @@ class ClangTypeEscapability void simple_display(llvm::raw_ostream &out, EscapabilityLookupDescriptor desc); SourceLoc extractNearestSourceLoc(EscapabilityLookupDescriptor desc); +// Swift value semantics of C++ types +// These are usually equivalent, with the exception of references. +// When a reference type is copied, the pointer’s value is copied rather than +// the object’s storage. This means reference types can be imported as +// copyable to Swift, even when they are non-copyable in C++. +enum class CxxValueSemanticsKind { + Unknown, + Copyable, + MoveOnly, + // A record that is either not copyable/movable or not destructible. + MissingLifetimeOperation, + // A record that has no copy and no move operations + UnavailableConstructors, +}; + +struct CxxValueSemanticsDescriptor final { + const clang::Type *type; + ClangImporter::Implementation *importerImpl; + + friend llvm::hash_code hash_value(const CxxValueSemanticsDescriptor &desc) { + return llvm::hash_combine(desc.type); + } + + friend bool operator==(const CxxValueSemanticsDescriptor &lhs, + const CxxValueSemanticsDescriptor &rhs) { + return lhs.type == rhs.type; + } + + friend bool operator!=(const CxxValueSemanticsDescriptor &lhs, + const CxxValueSemanticsDescriptor &rhs) { + return !(lhs == rhs); + } +}; + +class CxxValueSemantics + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + + bool isCached() const { return true; } + +private: + friend SimpleRequest; + + CxxValueSemanticsKind evaluate(Evaluator &evaluator, + CxxValueSemanticsDescriptor desc) const; +}; + +void simple_display(llvm::raw_ostream &out, CxxValueSemanticsDescriptor desc); +SourceLoc extractNearestSourceLoc(CxxValueSemanticsDescriptor desc); + +struct CxxDeclExplicitSafetyDescriptor final { + const clang::Decl *decl; + bool isClass; + + CxxDeclExplicitSafetyDescriptor(const clang::Decl *decl, bool isClass) + : decl(decl), isClass(isClass) {} + + friend llvm::hash_code + hash_value(const CxxDeclExplicitSafetyDescriptor &desc) { + return llvm::hash_combine(desc.decl, desc.isClass); + } + + friend bool operator==(const CxxDeclExplicitSafetyDescriptor &lhs, + const CxxDeclExplicitSafetyDescriptor &rhs) { + return lhs.decl == rhs.decl && lhs.isClass == rhs.isClass; + } + + friend bool operator!=(const CxxDeclExplicitSafetyDescriptor &lhs, + const CxxDeclExplicitSafetyDescriptor &rhs) { + return !(lhs == rhs); + } +}; + +void simple_display(llvm::raw_ostream &out, + CxxDeclExplicitSafetyDescriptor desc); +SourceLoc extractNearestSourceLoc(CxxDeclExplicitSafetyDescriptor desc); + /// Determine the safety of the given Clang declaration. class ClangDeclExplicitSafety : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -592,7 +667,8 @@ class ClangDeclExplicitSafety friend SimpleRequest; // Evaluation. - ExplicitSafety evaluate(Evaluator &evaluator, SafeUseOfCxxDeclDescriptor desc) const; + ExplicitSafety evaluate(Evaluator &evaluator, + CxxDeclExplicitSafetyDescriptor desc) const; }; #define SWIFT_TYPEID_ZONE ClangImporter diff --git a/include/swift/ClangImporter/ClangImporterTypeIDZone.def b/include/swift/ClangImporter/ClangImporterTypeIDZone.def index b4ea68a4053a2..0d562311a9c72 100644 --- a/include/swift/ClangImporter/ClangImporterTypeIDZone.def +++ b/include/swift/ClangImporter/ClangImporterTypeIDZone.def @@ -45,6 +45,9 @@ SWIFT_REQUEST(ClangImporter, CustomRefCountingOperation, SWIFT_REQUEST(ClangImporter, ClangTypeEscapability, CxxEscapability(EscapabilityLookupDescriptor), Cached, NoLocationInfo) +SWIFT_REQUEST(ClangImporter, CxxValueSemantics, + CxxValueSemanticsKind(CxxValueSemanticsDescriptor), Cached, + NoLocationInfo) SWIFT_REQUEST(ClangImporter, ClangDeclExplicitSafety, - ExplicitSafety(SafeUseOfCxxDeclDescriptor), Cached, + ExplicitSafety(CxxDeclExplicitSafetyDescriptor), Cached, NoLocationInfo) diff --git a/include/swift/ClangImporter/ClangModule.h b/include/swift/ClangImporter/ClangModule.h index c27cc2ecf2a2a..85536be1ec5a1 100644 --- a/include/swift/ClangImporter/ClangModule.h +++ b/include/swift/ClangImporter/ClangModule.h @@ -141,6 +141,10 @@ class ClangModuleUnit final : public LoadedFile { } }; +// Strips the inline namespaces from inner until we reach outer or a +// non-inline namespace. Returns the stripped nominal type or null when +// something unexpected happened during stripping. +NominalType *stripInlineNamespaces(NominalType *outer, NominalType *inner); } #endif diff --git a/include/swift/ClangImporter/SwiftAbstractBasicReader.h b/include/swift/ClangImporter/SwiftAbstractBasicReader.h index b5063c1c43493..8e31db659d2c1 100644 --- a/include/swift/ClangImporter/SwiftAbstractBasicReader.h +++ b/include/swift/ClangImporter/SwiftAbstractBasicReader.h @@ -64,6 +64,11 @@ class DataStreamBasicReader return uint32_t(asImpl().readUInt64()); } + clang::UnsignedOrNone readUnsignedOrNone() { + return clang::UnsignedOrNone::fromInternalRepresentation( + unsigned(asImpl().readUInt64())); + } + clang::Selector readSelector() { uint64_t numArgsPlusOne = asImpl().readUInt64(); diff --git a/include/swift/ClangImporter/SwiftAbstractBasicWriter.h b/include/swift/ClangImporter/SwiftAbstractBasicWriter.h index be8e2bf3c0e57..f636cba5907ba 100644 --- a/include/swift/ClangImporter/SwiftAbstractBasicWriter.h +++ b/include/swift/ClangImporter/SwiftAbstractBasicWriter.h @@ -61,6 +61,10 @@ class DataStreamBasicWriter asImpl().writeUInt64(uint64_t(value)); } + void writeUnsignedOrNone(clang::UnsignedOrNone value) { + asImpl().writeUInt64(uint64_t(value.toInternalRepresentation())); + } + void writeSelector(clang::Selector selector) { if (selector.isNull()) { asImpl().writeUInt64(0); diff --git a/include/swift/Demangling/Demangle.h b/include/swift/Demangling/Demangle.h index 5e1d153b77545..12606c6520c03 100644 --- a/include/swift/Demangling/Demangle.h +++ b/include/swift/Demangling/Demangle.h @@ -19,9 +19,11 @@ #ifndef SWIFT_DEMANGLING_DEMANGLE_H #define SWIFT_DEMANGLING_DEMANGLE_H +#include "swift/Demangling/Demangle.h" #include "swift/Demangling/Errors.h" #include "swift/Demangling/ManglingFlavor.h" #include "swift/Demangling/NamespaceMacros.h" +#include "swift/Strings.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" @@ -99,6 +101,7 @@ struct DemangleOptions { class Node; using NodePointer = Node *; +class NodePrinter; enum class FunctionSigSpecializationParamKind : unsigned { // Option Flags use bits 0-5. This give us 6 bits implying 64 entries to @@ -113,6 +116,8 @@ enum class FunctionSigSpecializationParamKind : unsigned { BoxToStack = 7, InOutToOut = 8, ConstantPropKeyPath = 9, + ConstantPropStruct = 10, + ClosurePropPreviousArg = 11, // Option Set Flags use bits 6-31. This gives us 26 bits to use for option // flags. @@ -190,6 +195,7 @@ class Node { }; using IndexType = uint64_t; + using RemoteAddressType = std::pair; friend class NodeFactory; @@ -206,14 +212,20 @@ class Node { IndexType Index; NodePointer InlineChildren[2]; NodeVector Children; + RemoteAddressType RemoteAddress; }; Kind NodeKind; enum class PayloadKind : uint8_t { - None = 0, OneChild = 1, TwoChildren = 2, - Text, Index, ManyChildren + None = 0, + OneChild = 1, + TwoChildren = 2, + Text, + Index, + ManyChildren, + RemoteAddress }; PayloadKind NodePayloadKind; @@ -228,6 +240,10 @@ class Node { : NodeKind(k), NodePayloadKind(PayloadKind::Index) { Index = index; } + Node(Kind k, uint64_t remoteAddress, uint8_t addressSpace) + : NodeKind(k), NodePayloadKind(PayloadKind::RemoteAddress) { + RemoteAddress = {remoteAddress, addressSpace}; + } Node(const Node &) = delete; Node &operator=(const Node &) = delete; @@ -281,6 +297,14 @@ class Node { return Index; } + bool hasRemoteAddress() const { + return NodePayloadKind == PayloadKind::RemoteAddress; + } + std::pair getRemoteAddress() const { + assert(hasRemoteAddress()); + return RemoteAddress; + } + using iterator = const NodePointer *; size_t getNumChildren() const { @@ -465,16 +489,26 @@ class Context { /// The lifetime of the returned node tree ends with the lifetime of the /// context or with a call of clear(). NodePointer demangleTypeAsNode(llvm::StringRef MangledName); - + /// Demangle the given symbol and return the readable name. /// /// \param MangledName The mangled symbol string, which start a mangling /// prefix: _T, _T0, $S, _$S. /// /// \returns The demangled string. - std::string demangleSymbolAsString( - llvm::StringRef MangledName, - const DemangleOptions &Options = DemangleOptions()); + std::string + demangleSymbolAsString(llvm::StringRef MangledName, + const DemangleOptions &Options = DemangleOptions()); + + /// Demangle the given symbol and store the result in the `printer`. + /// + /// \param MangledName The mangled symbol string, which start a mangling + /// prefix: _T, _T0, $S, _$S. + /// \param Printer The NodePrinter that will be used to demangle the symbol. + /// + /// \returns The demangled string. + void demangleSymbolAsString(llvm::StringRef MangledName, + NodePrinter &Printer); /// Demangle the given type and return the readable name. /// @@ -533,6 +567,16 @@ std::string demangleSymbolAsString(const char *mangledName, size_t mangledNameLength, const DemangleOptions &options = DemangleOptions()); +/// Standalone utility function to demangle the given symbol as string. The +/// demangled string is stored in the `printer`. +/// +/// If performance is an issue when demangling multiple symbols, +/// \param mangledName The mangled name string pointer. +/// \param mangledNameLength The length of the mangledName string. +/// \param printer The NodePrinter that will be used to demangle the symbol. +void demangleSymbolAsString(const llvm::StringRef mangledName, + NodePrinter &printer); + /// Standalone utility function to demangle the given symbol as string. /// /// If performance is an issue when demangling multiple symbols, @@ -545,7 +589,7 @@ demangleSymbolAsString(const std::string &mangledName, return demangleSymbolAsString(mangledName.data(), mangledName.size(), options); } - + /// Standalone utility function to demangle the given symbol as string. /// /// If performance is an issue when demangling multiple symbols, @@ -626,7 +670,7 @@ struct [[nodiscard]] ManglingError { InvalidImplCoroutineKind, InvalidImplFunctionAttribute, InvalidImplParameterConvention, - InvalidImplParameterSending, + InvalidImplParameterAttr, InvalidMetatypeRepresentation, MultiByteRelatedEntity, BadValueWitnessKind, @@ -725,13 +769,19 @@ ManglingErrorOr mangleNodeAsObjcCString(NodePointer node, /// \endcode /// /// \param Root A pointer to a parse tree generated by the demangler. -/// \param Options An object encapsulating options to use to perform this demangling. +/// \param Options An object encapsulating options to use to perform this +/// demangling. /// /// \returns A string representing the demangled name. -/// std::string nodeToString(NodePointer Root, const DemangleOptions &Options = DemangleOptions()); +/// Transform the node structure to a string, which is stored in the `Printer`. +/// +/// \param Root A pointer to a parse tree generated by the demangler. +/// \param Printer A NodePrinter used to pretty print the demangled Node. +void nodeToString(NodePointer Root, NodePrinter &Printer); + /// Transforms a mangled key path accessor thunk helper /// into the identfier/subscript that would be used to invoke it in swift code. std::string keyPathSourceString(const char *MangledName, @@ -777,11 +827,14 @@ class DemanglerPrinter { llvm::StringRef getStringRef() const { return Stream; } + size_t getStreamLength() { return Stream.length(); } + /// Shrinks the buffer. void resetSize(size_t toPos) { assert(toPos <= Stream.size()); Stream.resize(toPos); } + private: std::string Stream; }; @@ -818,6 +871,162 @@ std::string mangledNameForTypeMetadataAccessor( llvm::StringRef moduleName, llvm::StringRef typeName, Node::Kind typeKind, Mangle::ManglingFlavor Flavor = Mangle::ManglingFlavor::Default); +/// Base class for printing a Swift demangled node tree. +/// +/// NodePrinter is used to convert demangled Swift symbol nodes into +/// human-readable string representations. It handles formatting, indentation, +/// and Swift-specific syntax. +/// +/// The virtual methods in this class are meant to be overriden to allow +/// external consumers (e.g lldb) to track the ranges of components of the +/// demangled name. +class NodePrinter { +protected: + DemanglerPrinter Printer; + DemangleOptions Options; + bool SpecializationPrefixPrinted = false; + bool isValid = true; + +public: + NodePrinter(DemangleOptions options) : Options(options) {} + + virtual ~NodePrinter() = default; + + virtual void printRoot(NodePointer root) { + isValid = true; + print(root, 0); + } + + std::string takeString() { + if (isValid) + return std::move(Printer).str(); + return ""; + } + +protected: + static const unsigned MaxDepth = 768; + + size_t getStreamLength() { return Printer.getStreamLength(); } + + /// Called when the node tree in valid. + /// + /// The demangler already catches most error cases and mostly produces valid + /// node trees. But some cases are difficult to catch in the demangler and + /// instead the NodePrinter bails. + void setInvalid() { isValid = false; } + + void printChildren(Node::iterator begin, Node::iterator end, unsigned depth, + const char *sep = nullptr); + + void printChildren(NodePointer Node, unsigned depth, + const char *sep = nullptr); + + NodePointer getFirstChildOfKind(NodePointer Node, Node::Kind kind); + + void printBoundGenericNoSugar(NodePointer Node, unsigned depth); + + void printOptionalIndex(NodePointer node); + + static bool isSwiftModule(NodePointer node) { + return (node->getKind() == Node::Kind::Module && + node->getText() == STDLIB_NAME); + } + + static bool isIdentifier(NodePointer node, StringRef desired) { + return (node->getKind() == Node::Kind::Identifier && + node->getText() == desired); + } + + bool printContext(NodePointer Context); + + enum class SugarType { + None, + Optional, + ImplicitlyUnwrappedOptional, + Array, + Dictionary + }; + + enum class TypePrinting { NoType, WithColon, FunctionStyle }; + + /// Determine whether this is a "simple" type, from the type-simple + /// production. + bool isSimpleType(NodePointer Node); + + void printWithParens(NodePointer type, unsigned depth); + + SugarType findSugar(NodePointer Node); + + void printBoundGeneric(NodePointer Node, unsigned depth); + + NodePointer getChildIf(NodePointer Node, Node::Kind Kind); + + virtual void printFunctionParameters(NodePointer LabelList, + NodePointer ParameterType, + unsigned depth, bool showTypes); + + void printFunctionType(NodePointer LabelList, NodePointer node, + unsigned depth); + + void printImplFunctionType(NodePointer fn, unsigned depth); + + virtual void printGenericSignature(NodePointer Node, unsigned depth); + + void printFunctionSigSpecializationParams(NodePointer Node, unsigned depth); + void printNextParamChildNode(NodePointer nd, unsigned &idx, + FunctionSigSpecializationParamKind kind, + unsigned depth); + + void printSpecializationPrefix(NodePointer node, StringRef Description, + unsigned depth, + StringRef ParamPrefix = StringRef()); + + /// The main big print function. + NodePointer print(NodePointer Node, unsigned depth, + bool asPrefixContext = false); + + NodePointer printAbstractStorage(NodePointer Node, unsigned depth, + bool asPrefixContent, StringRef ExtraName); + + /// Utility function to print entities. + /// + /// \param Entity The entity node to print + /// \param depth The depth in the print() call tree. + /// \param asPrefixContext Should the entity printed as a context which as a + /// prefix to another entity, e.g. the Abc in Abc.def() + /// \param TypePr How should the type of the entity be printed, if at all. + /// E.g. with a colon for properties or as a function type. + /// \param hasName Does the entity has a name, e.g. a function in contrast to + /// an initializer. + /// \param ExtraName An extra name added to the entity name (if any). + /// \param ExtraIndex An extra index added to the entity name (if any), + /// e.g. closure #1 + /// \param OverwriteName If non-empty, print this name instead of the one + /// provided by the node. Gets printed even if hasName is false. + /// \return If a non-null node is returned it's a context which must be + /// printed in postfix-form after the entity: " in ". + NodePointer printEntity(NodePointer Entity, unsigned depth, + bool asPrefixContext, TypePrinting TypePr, + bool hasName, StringRef ExtraName = "", + int ExtraIndex = -1, StringRef OverwriteName = ""); + + virtual void printFunctionName(bool hasName, llvm::StringRef &OverwriteName, + llvm::StringRef &ExtraName, bool MultiWordName, + int &ExtraIndex, + swift::Demangle::NodePointer Entity, + unsigned int depth); + + /// Print the type of an entity. + /// + /// \param Entity The entity. + /// \param type The type of the entity. + /// \param genericFunctionTypeList If not null, the generic argument types + /// which is printed in the generic signature. + /// \param depth The depth in the print() call tree. + void printEntityType(NodePointer Entity, NodePointer type, + NodePointer genericFunctionTypeList, unsigned depth); +}; + SWIFT_END_INLINE_NAMESPACE } // end namespace Demangle } // end namespace swift diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index df41580cf465c..d74f6a902ae89 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -75,6 +75,7 @@ NODE(DependentPseudogenericSignature) NODE(DependentProtocolConformanceRoot) NODE(DependentProtocolConformanceInherited) NODE(DependentProtocolConformanceAssociated) +NODE(DependentProtocolConformanceOpaque) CONTEXT_NODE(Destructor) CONTEXT_NODE(DidSet) NODE(Directness) @@ -137,6 +138,8 @@ NODE(ImplErasedIsolation) NODE(ImplSendingResult) NODE(ImplParameterResultDifferentiability) NODE(ImplParameterSending) +NODE(ImplParameterIsolated) +NODE(ImplParameterImplicitLeading) NODE(ImplFunctionAttribute) NODE(ImplFunctionConvention) NODE(ImplFunctionConventionName) @@ -210,6 +213,7 @@ NODE(PrefixOperator) NODE(PrivateDeclName) NODE(PropertyDescriptor) CONTEXT_NODE(PropertyWrapperBackingInitializer) +CONTEXT_NODE(PropertyWrappedFieldInitAccessor) CONTEXT_NODE(PropertyWrapperInitFromProjectedValue) CONTEXT_NODE(Protocol) CONTEXT_NODE(ProtocolSymbolicReference) @@ -399,6 +403,7 @@ NODE(AsyncRemoved) // Added in Swift 5.TBD NODE(ObjectiveCProtocolSymbolicReference) +NODE(OutlinedInitializeWithTakeNoValueWitness) NODE(OutlinedInitializeWithCopyNoValueWitness) NODE(OutlinedAssignWithTakeNoValueWitness) NODE(OutlinedAssignWithCopyNoValueWitness) @@ -406,7 +411,7 @@ NODE(OutlinedDestroyNoValueWitness) NODE(DependentGenericInverseConformanceRequirement) -// Added in Swift 6.TBD +// Added in Swift 6.2 NODE(Integer) NODE(NegativeInteger) NODE(DependentGenericParamValueMarker) @@ -414,5 +419,9 @@ NODE(CoroFunctionPointer) NODE(DefaultOverride) NODE(ConstValue) +// Added in Swift 6.TBD +CONTEXT_NODE(BorrowAccessor) +CONTEXT_NODE(MutateAccessor) + #undef CONTEXT_NODE #undef NODE diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index f634e5a86181b..62f0298cf6e93 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -249,6 +249,12 @@ class NodeFactory { /// Creates a node of kind \p K with an \p Index payload. NodePointer createNode(Node::Kind K, Node::IndexType Index); + /// Creates a node of kind \p K with a \p RemoteAddress payload. + /// + /// These nodes are created and consumed by the reflection library. + NodePointer createNode(Node::Kind K, uint64_t RemoteAddress, + uint8_t AddressSpace); + /// Creates a node of kind \p K with a \p Text payload. /// /// The \p Text string must be already allocated with the Factory and therefore @@ -570,6 +576,8 @@ class Demangler : public NodeFactory { NodePointer demangleImplParamConvention(Node::Kind ConvKind); NodePointer demangleImplResultConvention(Node::Kind ConvKind); NodePointer demangleImplParameterSending(); + NodePointer demangleImplParameterIsolated(); + NodePointer demangleImplParameterImplicitLeading(); NodePointer demangleImplParameterResultDifferentiability(); NodePointer demangleImplFunctionType(); NodePointer demangleClangType(); @@ -596,6 +604,7 @@ class Demangler : public NodeFactory { NodePointer demangleDependentProtocolConformanceInherited(); NodePointer popDependentAssociatedConformance(); NodePointer demangleDependentProtocolConformanceAssociated(); + NodePointer demangleDependentProtocolConformanceOpaque(); NodePointer demangleThunkOrSpecialization(); NodePointer demangleGenericSpecialization(Node::Kind SpecKind, NodePointer droppedArguments); diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index 724f6d01d019f..77bb3bc6ccd8e 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -118,6 +118,8 @@ enum class ImplParameterConvention { enum class ImplParameterInfoFlags : uint8_t { NotDifferentiable = 0x1, Sending = 0x2, + Isolated = 0x4, + ImplicitLeading = 0x8 }; using ImplParameterInfoOptions = OptionSet; @@ -192,6 +194,22 @@ class ImplFunctionParam { return result; } + static OptionsType getIsolated() { + OptionsType result; + + result |= ImplParameterInfoFlags::Isolated; + + return result; + } + + static OptionsType getImplicitLeading() { + OptionsType result; + + result |= ImplParameterInfoFlags::ImplicitLeading; + + return result; + } + ImplFunctionParam(BuiltType type, ImplParameterConvention convention, OptionsType options) : Type(type), Convention(convention), Options(options) {} @@ -555,6 +573,43 @@ void decodeRequirement( } } +/// Extract the protocol and requirement nodes from a shape symbol. +static inline std::pair +decodeShape(NodePointer Node) { + if (!Node || Node->getKind() != Node::Kind::Global || + Node->getNumChildren() != 1) + return {nullptr, nullptr}; + Node = Node->getChild(0); + if (Node && (Node->getKind() == Node::Kind::Uniquable) && + Node->getNumChildren() == 1) + Node = Node->getChild(0); + if (!Node || Node->getKind() != Node::Kind::ExtendedExistentialTypeShape || + Node->getNumChildren() != 2) + return {nullptr, nullptr}; + Node = Node->getChild(1); + if (!Node || Node->getKind() != Node::Kind::Type || + Node->getNumChildren() != 1) + return {nullptr, nullptr}; + Node = Node->getChild(0); + if (!Node || Node->getKind() != Node::Kind::ConstrainedExistential || + Node->getNumChildren() != 2) + return {nullptr, nullptr}; + NodePointer Requirements = Node->getChild(1); + if (!Requirements || Requirements->getKind() != + Node::Kind::ConstrainedExistentialRequirementList) + return {nullptr, nullptr}; + + Node = Node->getChild(0); + if (!Node || Node->getKind() != Node::Kind::Type || + Node->getNumChildren() != 1) + return {nullptr, nullptr}; + NodePointer Protocol = Node; + if (!Protocol) + return {nullptr, nullptr}; + + return {Protocol, Requirements}; +} + #define MAKE_NODE_TYPE_ERROR(Node, Fmt, ...) \ TYPE_LOOKUP_ERROR_FMT("TypeDecoder.h:%u: Node kind %u \"%.*s\" - " Fmt, \ __LINE__, (unsigned)Node->getKind(), \ @@ -1143,11 +1198,11 @@ class TypeDecoder { return MAKE_NODE_TYPE_ERROR0(child, "failed to decode function yields"); } else if (child->getKind() == NodeKind::ImplResult) { - if (decodeImplFunctionParam(child, depth + 1, results)) + if (decodeImplFunctionResult(child, depth + 1, results)) return MAKE_NODE_TYPE_ERROR0(child, "failed to decode function results"); } else if (child->getKind() == NodeKind::ImplErrorResult) { - if (decodeImplFunctionPart(child, depth + 1, errorResults)) + if (decodeImplFunctionResult(child, depth + 1, errorResults)) return MAKE_NODE_TYPE_ERROR0(child, "failed to decode function part"); } else { @@ -1638,40 +1693,70 @@ class TypeDecoder { } template - bool decodeImplFunctionPart(Demangle::NodePointer node, unsigned depth, - llvm::SmallVectorImpl &results) { + bool decodeImplFunctionParam(Demangle::NodePointer node, unsigned depth, + llvm::SmallVectorImpl &results) { if (depth > TypeDecoder::MaxDepth) return true; - if (node->getNumChildren() != 2) + // Children: `convention, attrs, type` + // attrs: `differentiability?, sending?, isolated?, implicit_leading?` + if (node->getNumChildren() < 2) return true; - - if (node->getChild(0)->getKind() != Node::Kind::ImplConvention || - node->getChild(1)->getKind() != Node::Kind::Type) + + auto *conventionNode = node->getChild(0); + auto *typeNode = node->getLastChild(); + if (conventionNode->getKind() != Node::Kind::ImplConvention || + typeNode->getKind() != Node::Kind::Type) return true; - StringRef conventionString = node->getChild(0)->getText(); - std::optional convention = - T::getConventionFromString(conventionString); + StringRef conventionString = conventionNode->getText(); + auto convention = T::getConventionFromString(conventionString); if (!convention) return true; - auto type = decodeMangledType(node->getChild(1), depth + 1); - if (type.isError()) + auto result = decodeMangledType(typeNode, depth + 1); + if (result.isError()) return true; - results.emplace_back(type.getType(), *convention); + typename T::OptionsType options; + for (unsigned i = 1; i < node->getNumChildren() - 1; ++i) { + auto child = node->getChild(i); + switch (child->getKind()) { + case Node::Kind::ImplParameterResultDifferentiability: { + auto optDiffOptions = + T::getDifferentiabilityFromString(child->getText()); + if (!optDiffOptions) + return true; + options |= *optDiffOptions; + break; + } + case Node::Kind::ImplParameterSending: + options |= T::getSending(); + break; + case Node::Kind::ImplParameterIsolated: + options |= T::getIsolated(); + break; + case Node::Kind::ImplParameterImplicitLeading: + options |= T::getImplicitLeading(); + break; + default: + return true; + } + } + + results.emplace_back(result.getType(), *convention, options); + return false; } template - bool decodeImplFunctionParam(Demangle::NodePointer node, unsigned depth, - llvm::SmallVectorImpl &results) { + bool decodeImplFunctionResult(Demangle::NodePointer node, unsigned depth, + llvm::SmallVectorImpl &results) { if (depth > TypeDecoder::MaxDepth) return true; - // Children: `convention, differentiability?, sending?, type` - if (node->getNumChildren() != 2 && node->getNumChildren() != 3 && - node->getNumChildren() != 4) + // Children: `convention, attrs, type` + // attrs: `differentiability?, sending?, isolated?, implicit_leading?` + if (node->getNumChildren() < 2) return true; auto *conventionNode = node->getChild(0); @@ -1689,23 +1774,23 @@ class TypeDecoder { return true; typename T::OptionsType options; - if (node->getNumChildren() == 3 || node->getNumChildren() == 4) { - auto diffKindNode = node->getChild(1); - if (diffKindNode->getKind() != - Node::Kind::ImplParameterResultDifferentiability) - return true; - auto optDiffOptions = - T::getDifferentiabilityFromString(diffKindNode->getText()); - if (!optDiffOptions) - return true; - options |= *optDiffOptions; - } - - if (node->getNumChildren() == 4) { - auto sendingKindNode = node->getChild(2); - if (sendingKindNode->getKind() != Node::Kind::ImplParameterSending) + for (unsigned i = 1; i < node->getNumChildren() - 1; ++i) { + auto child = node->getChild(i); + switch (child->getKind()) { + case Node::Kind::ImplParameterResultDifferentiability: { + auto optDiffOptions = + T::getDifferentiabilityFromString(child->getText()); + if (!optDiffOptions) + return true; + options |= *optDiffOptions; + break; + } + case Node::Kind::ImplParameterSending: + options |= T::getSending(); + break; + default: return true; - options |= T::getSending(); + } } results.emplace_back(result.getType(), *convention, options); diff --git a/include/swift/DependencyScan/DependencyScanImpl.h b/include/swift/DependencyScan/DependencyScanImpl.h index c5507ea7c731d..13408e2c98bcd 100644 --- a/include/swift/DependencyScan/DependencyScanImpl.h +++ b/include/swift/DependencyScan/DependencyScanImpl.h @@ -43,7 +43,6 @@ struct swiftscan_dependency_info_s { /// "swiftInterface" /// "swiftSource" /// "swiftBinary" - /// "swiftPlaceholder" /// "clang"" swiftscan_string_ref_t module_name; @@ -63,6 +62,12 @@ struct swiftscan_dependency_info_s { /// The list of link libraries for this module. swiftscan_link_library_set_t *link_libraries; + /// The list of source import infos. + swiftscan_import_info_set_t *imports; + + /// The list of source optional import infos. + swiftscan_import_info_set_t *optional_imports; + /// Specific details of a particular kind of module. swiftscan_module_details_t details; }; @@ -74,10 +79,16 @@ struct swiftscan_link_library_info_s { bool forceLoad; }; +struct swiftscan_import_info_s { + swiftscan_string_ref_t import_identifier; + swiftscan_source_location_set_t *source_locations; + swiftscan_access_level_t access_level; +}; + struct swiftscan_macro_dependency_s { - swiftscan_string_ref_t moduleName; - swiftscan_string_ref_t libraryPath; - swiftscan_string_ref_t executablePath; + swiftscan_string_ref_t module_name; + swiftscan_string_ref_t library_path; + swiftscan_string_ref_t executable_path; }; /// Swift modules to be built from a module interface, may have a bridging @@ -102,6 +113,9 @@ typedef struct { /// Clang module dependencies swiftscan_string_set_t *swift_overlay_module_dependencies; + /// Directly-imported in source module dependencies + swiftscan_string_set_t *source_import_module_dependencies; + /// Options to the compile command required to build this module interface swiftscan_string_set_t *command_line; @@ -180,19 +194,6 @@ typedef struct { swiftscan_string_ref_t user_module_version; } swiftscan_swift_binary_details_t; -/// Swift placeholder modules carry additional details that specify their -/// module doc path and source info paths. -typedef struct { - /// The path to the pre-compiled binary module - swiftscan_string_ref_t compiled_module_path; - - /// The path to the .swiftModuleDoc file. - swiftscan_string_ref_t module_doc_path; - - /// The path to the .swiftSourceInfo file. - swiftscan_string_ref_t module_source_info_path; -} swiftscan_swift_placeholder_details_t; - /// Clang modules are built from a module map file. typedef struct { /// The path to the module map used to build this module. @@ -219,7 +220,6 @@ struct swiftscan_module_details_s { union { swiftscan_swift_textual_details_t swift_textual_details; swiftscan_swift_binary_details_t swift_binary_details; - swiftscan_swift_placeholder_details_t swift_placeholder_details; swiftscan_clang_details_t clang_details; }; }; diff --git a/include/swift/DependencyScan/DependencyScanningTool.h b/include/swift/DependencyScan/DependencyScanningTool.h index 7129b1503c781..1544109083252 100644 --- a/include/swift/DependencyScan/DependencyScanningTool.h +++ b/include/swift/DependencyScan/DependencyScanningTool.h @@ -15,57 +15,94 @@ #include "swift-c/DependencyScan/DependencyScan.h" #include "swift/Frontend/Frontend.h" +#include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/ModuleDependencies.h" #include "swift/DependencyScan/ScanDependencies.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/Frontend/SerializedDiagnosticConsumer.h" #include "llvm/Support/Error.h" #include "llvm/Support/StringSaver.h" namespace swift { namespace dependencies { class DependencyScanningTool; -class DependencyScanDiagnosticCollector; +class DepScanInMemoryDiagnosticCollector; -struct ScanQueryInstance { +struct ScanQueryContext { + /// Primary CompilerInstance configured for this scanning action std::unique_ptr ScanInstance; - std::shared_ptr ScanDiagnostics; + /// An thread-safe diagnostic consumer which collects all emitted + /// diagnostics in the scan to be reporte via libSwiftScan API + std::unique_ptr InMemoryDiagnosticCollector; + /// A thread-safe serialized diagnostics consumer. + /// Note, although type-erased, this must be an instance of + /// 'ThreadSafeSerializedDiagnosticConsumer' + std::unique_ptr SerializedDiagnosticConsumer; + + ScanQueryContext( + std::unique_ptr ScanInstance, + std::unique_ptr + InMemoryDiagnosticCollector, + std::unique_ptr SerializedDiagnosticConsumer) + : ScanInstance(std::move(ScanInstance)), + InMemoryDiagnosticCollector(std::move(InMemoryDiagnosticCollector)), + SerializedDiagnosticConsumer(std::move(SerializedDiagnosticConsumer)) {} + + ScanQueryContext(ScanQueryContext &&other) + : ScanInstance(std::move(other.ScanInstance)), + InMemoryDiagnosticCollector( + std::move(other.InMemoryDiagnosticCollector)), + SerializedDiagnosticConsumer( + std::move(other.SerializedDiagnosticConsumer)) {} + + ~ScanQueryContext() { + if (SerializedDiagnosticConsumer) + SerializedDiagnosticConsumer->finishProcessing(); + } }; -/// Diagnostic consumer that simply collects the diagnostics emitted so-far -class DependencyScanDiagnosticCollector : public DiagnosticConsumer { +/// Pure virtual Diagnostic consumer intended for collecting +/// emitted diagnostics in a thread-safe fashion +class ThreadSafeDiagnosticCollector : public DiagnosticConsumer { private: + llvm::sys::SmartMutex DiagnosticConsumerStateLock; + void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) final; + +protected: + virtual void addDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) = 0; + +public: + ThreadSafeDiagnosticCollector() {} + bool finishProcessing() final { return false; } +}; + +/// Diagnostic consumer that simply collects the diagnostics emitted so-far +/// and uses a representation agnostic from any specific CompilerInstance state +/// which may have been used to emit the diagnostic +class DepScanInMemoryDiagnosticCollector + : public ThreadSafeDiagnosticCollector { +public: struct ScannerDiagnosticInfo { std::string Message; llvm::SourceMgr::DiagKind Severity; - std::optional ImportLocation; + std::optional + ImportLocation; }; std::vector Diagnostics; - void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override; - protected: - virtual void addDiagnostic(SourceManager &SM, const DiagnosticInfo &Info); + void addDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override; public: friend DependencyScanningTool; - DependencyScanDiagnosticCollector() {} + DepScanInMemoryDiagnosticCollector() {} void reset() { Diagnostics.clear(); } - const std::vector &getDiagnostics() const { + std::vector getDiagnostics() const { + return Diagnostics; + } + const std::vector &getDiagnosticsRef() const { return Diagnostics; } -}; - -/// Locking variant of the above diagnostic collector that guards accesses to -/// its state with a lock. -class LockingDependencyScanDiagnosticCollector - : public DependencyScanDiagnosticCollector { -private: - void addDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override; - llvm::sys::SmartMutex ScanningDiagnosticConsumerStateLock; - -public: - friend DependencyScanningTool; - LockingDependencyScanDiagnosticCollector() {} }; /// Given a set of arguments to a print-target-info frontend tool query, produce the @@ -80,14 +117,12 @@ class DependencyScanningTool { /// Construct a dependency scanning tool. DependencyScanningTool(); - /// Collect the full module dependency graph for the input, ignoring any - /// placeholder modules. + /// Collect the full module dependency graph for the input. /// /// \returns a \c StringError with the diagnostic output if errors /// occurred, \c swiftscan_dependency_result_t otherwise. llvm::ErrorOr getDependencies(ArrayRef Command, - const llvm::StringSet<> &PlaceholderModules, StringRef WorkingDirectory); /// Collect the set of imports for the input module @@ -97,17 +132,12 @@ class DependencyScanningTool { llvm::ErrorOr getImports(ArrayRef Command, StringRef WorkingDirectory); - /// Query diagnostics consumed so far. - std::vector getDiagnostics(); - /// Discared the collection of diagnostics encountered so far. - void resetDiagnostics(); - /// Using the specified invocation command, instantiate a CompilerInstance /// that will be used for this scan. - llvm::ErrorOr - initCompilerInstanceForScan(ArrayRef Command, - StringRef WorkingDirectory, - std::shared_ptr scannerDiagnosticsCollector); + llvm::ErrorOr createScanQueryContext( + ArrayRef Command, StringRef WorkingDirectory, + std::vector + &initializationDiagnostics); private: /// Shared cache of module dependencies, re-used by individual full-scan queries @@ -116,14 +146,13 @@ class DependencyScanningTool { /// Shared state mutual-exclusivity lock llvm::sys::SmartMutex DependencyScanningToolStateLock; - - /// A shared consumer that accumulates encountered diagnostics. - LockingDependencyScanDiagnosticCollector CDC; llvm::BumpPtrAllocator Alloc; llvm::StringSaver Saver; }; -swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput(const DependencyScanDiagnosticCollector *diagnosticCollector); +swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput( + ArrayRef + diagnostics); } // end namespace dependencies } // end namespace swift diff --git a/include/swift/DependencyScan/ModuleDependencyScanner.h b/include/swift/DependencyScan/ModuleDependencyScanner.h index 6065ccc6d4998..df768db99374b 100644 --- a/include/swift/DependencyScan/ModuleDependencyScanner.h +++ b/include/swift/DependencyScan/ModuleDependencyScanner.h @@ -1,5 +1,4 @@ -//===--- ModuleDependencyScanner.h - Import Swift modules --------*- C++ -//-*-===// +//===--- ModuleDependencyScanner.h - Import Swift modules ------*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -15,7 +14,8 @@ #include "swift/AST/Identifier.h" #include "swift/AST/ModuleDependencies.h" #include "swift/Frontend/ModuleInterfaceLoader.h" -#include "swift/Serialization/SerializedModuleLoader.h" +#include "swift/Serialization/ScanningLoaders.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" #include "llvm/CAS/CASReference.h" #include "llvm/Support/ThreadPool.h" @@ -25,6 +25,12 @@ class DependencyTracker; namespace swift { +/// A callback to lookup module outputs for "-fmodule-file=", "-o" etc. +using LookupModuleOutputCallback = llvm::function_ref; +using RemapPathCallback = llvm::function_ref; + /// A dependency scanning worker which performs filesystem lookup /// of a named module dependency. class ModuleDependencyScanningWorker { @@ -33,22 +39,66 @@ class ModuleDependencyScanningWorker { SwiftDependencyScanningService &globalScanningService, const CompilerInvocation &ScanCompilerInvocation, const SILOptions &SILOptions, ASTContext &ScanASTContext, - DependencyTracker &DependencyTracker, DiagnosticEngine &diags); + DependencyTracker &DependencyTracker, + std::shared_ptr CAS, + std::shared_ptr ActionCache, + llvm::PrefixMapper *mapper, DiagnosticEngine &diags); private: - /// Retrieve the module dependencies for the Clang module with the given name. - ModuleDependencyVector scanFilesystemForClangModuleDependency( - Identifier moduleName, StringRef moduleOutputPath, - StringRef sdkModuleOutputPath, + /// Query dependency information for a named Clang module + /// + /// \param moduleName moduel identifier for the query + /// + /// \param lookupModuleCallback a callback to compute a client-specific + /// module-cache-relative output path for discovered Clang module dependencies. + /// + /// \param alreadySeenModules a set of module dependencies previously seen + /// by the scanner, as to avoid processing them all over again + /// + /// \returns Clang dependency scanner's \c TranslationUnitDeps result + std::optional + scanFilesystemForClangModuleDependency( + Identifier moduleName, + LookupModuleOutputCallback lookupModuleCallback, + const llvm::DenseSet + &alreadySeenModules); + + /// Query dependency information for header dependencies + /// of a binary Swift module. + /// + /// \param moduleID the name of the Swift module whose dependency + /// information will be augmented with information about the given + /// textual header inputs. + /// + /// \param headerPath optional path to the header to be scanned. + /// + /// \param sourceBuffer optional in-memory buffer of a header to be scanned. + /// + /// \param lookupModuleCallback a callback to compute a client-specific + /// module-cache-relative output path for discovered Clang module dependencies. + /// + /// \param alreadySeenModules a set of module dependencies previously seen + /// by the scanner, as to avoid processing them all over again + /// + /// \returns Clang dependency scanner's \c TranslationUnitDeps result + std::optional + scanHeaderDependenciesOfSwiftModule( + ModuleDependencyID moduleID, std::optional headerPath, + std::optional sourceBuffer, + LookupModuleOutputCallback lookupModuleCallback, const llvm::DenseSet - &alreadySeenModules, - llvm::PrefixMapper *prefixMapper); + &alreadySeenModules); - /// Retrieve the module dependencies for the Swift module with the given name. - ModuleDependencyVector scanFilesystemForSwiftModuleDependency( - Identifier moduleName, StringRef moduleOutputPath, - StringRef sdkModuleOutputPath, llvm::PrefixMapper *prefixMapper, - bool isTestableImport = false); + /// Query dependency information for a named Swift module + /// + /// \param moduleName moduel identifier for the query + /// + /// \param isTestableImport a boolean flag which indicates whether + /// this is an @testable dependency + /// + /// \returns a struct containing query results + SwiftModuleScannerQueryResult scanFilesystemForSwiftModuleDependency( + Identifier moduleName, bool isTestableImport = false); /// Store cache entry for include tree. llvm::Error @@ -57,6 +107,8 @@ class ModuleDependencyScanningWorker { // Worker-specific instance of CompilerInvocation std::unique_ptr workerCompilerInvocation; + // Worker-specific diagnostic engine + std::unique_ptr workerDiagnosticEngine; // Worker-specific instance of ASTContext std::unique_ptr workerASTContext; // An AST delegate for interface scanning. @@ -64,11 +116,89 @@ class ModuleDependencyScanningWorker { // The Clang scanner tool used by this worker. clang::tooling::dependencies::DependencyScanningTool clangScanningTool; // Swift and Clang module loaders acting as scanners. - std::unique_ptr swiftScannerModuleLoader; - std::unique_ptr clangScannerModuleLoader; + std::unique_ptr swiftModuleScannerLoader; + // CAS instance. std::shared_ptr CAS; std::shared_ptr ActionCache; + + // Base command line invocation for clang scanner queries (both module and header) + std::vector clangScanningBaseCommandLineArgs; + // Command line invocation for clang by-name module lookups + std::vector clangScanningModuleCommandLineArgs; + // Clang-specific (-Xcc) command-line flags to include on + // Swift module compilation commands + std::vector swiftModuleClangCC1CommandLineArgs; + // Working directory for clang module lookup queries + std::string clangScanningWorkingDirectoryPath; + // Restrict access to the parent scanner class. + friend class ModuleDependencyScanner; +}; + +// MARK: SwiftDependencyTracker +/// Track swift dependency +class SwiftDependencyTracker { +public: + SwiftDependencyTracker(std::shared_ptr CAS, + llvm::PrefixMapper *Mapper, + const CompilerInvocation &CI); + + void startTracking(bool includeCommonDeps = true); + void trackFile(const Twine &path); + llvm::Expected createTreeFromDependencies(); + +private: + llvm::IntrusiveRefCntPtr FS; + std::shared_ptr CAS; + llvm::PrefixMapper *Mapper; + + struct FileEntry { + llvm::cas::ObjectRef FileRef; + size_t Size; + + FileEntry(llvm::cas::ObjectRef FileRef, size_t Size) + : FileRef(FileRef), Size(Size) {} + }; + llvm::StringMap CommonFiles; + std::map TrackedFiles; +}; + +class ModuleDependencyIssueReporter { +private: + ModuleDependencyIssueReporter(DiagnosticEngine &Diagnostics) + : Diagnostics(Diagnostics) {} + + /// Diagnose scanner failure and attempt to reconstruct the dependency + /// path from the main module to the missing dependency + void diagnoseModuleNotFoundFailure( + const ScannerImportStatementInfo &moduleImport, + const ModuleDependenciesCache &cache, + std::optional dependencyOf, + std::optional> + resolvingSerializedSearchPath, + std::optional< + std::vector> + foundIncompatibleCandidates = std::nullopt); + + /// Upon query failure, if incompatible binary module + /// candidates were found, emit a failure diagnostic + void diagnoseFailureOnOnlyIncompatibleCandidates( + const ScannerImportStatementInfo &moduleImport, + const std::vector + &candidates, + const ModuleDependenciesCache &cache, + std::optional dependencyOf); + + /// Emit warnings for each discovered binary Swift module + /// which was incompatible with the current compilation + /// when querying \c moduleName + void warnOnIncompatibleCandidates( + StringRef moduleName, + const std::vector + &candidates); + + DiagnosticEngine &Diagnostics; + std::unordered_set ReportedMissing; // Restrict access to the parent scanner class. friend class ModuleDependencyScanner; }; @@ -80,6 +210,8 @@ class ModuleDependencyScanner { const SILOptions &SILOptions, ASTContext &ScanASTContext, DependencyTracker &DependencyTracker, + std::shared_ptr CAS, + std::shared_ptr ActionCache, DiagnosticEngine &diags, bool ParallelScan); /// Identify the scanner invocation's main module's dependencies @@ -92,22 +224,36 @@ class ModuleDependencyScanner { performDependencyScan(ModuleDependencyID rootModuleID, ModuleDependenciesCache &cache); - /// Query the module dependency info for the Clang module with the given name. - /// Explicit by-name lookups are useful for batch mode scanning. - std::optional - getNamedClangModuleDependencyInfo(StringRef moduleName, - ModuleDependenciesCache &cache, - ModuleDependencyIDSetVector &discoveredClangModules); - - /// Query the module dependency info for the Swift module with the given name. - /// Explicit by-name lookups are useful for batch mode scanning. - std::optional - getNamedSwiftModuleDependencyInfo(StringRef moduleName, - ModuleDependenciesCache &cache); - /// How many filesystem lookups were performed by the scanner unsigned getNumLookups() { return NumLookups; } + /// CAS Dependency Tracker. + std::optional + createSwiftDependencyTracker(const CompilerInvocation &CI) { + if (!CAS) + return std::nullopt; + + return SwiftDependencyTracker(CAS, PrefixMapper.get(), CI); + } + + /// PrefixMapper for scanner. + bool hasPathMapping() const { + return PrefixMapper && !PrefixMapper->getMappings().empty(); + } + llvm::PrefixMapper *getPrefixMapper() const { return PrefixMapper.get(); } + std::string remapPath(StringRef Path) const; + + /// CAS options. + llvm::cas::ObjectStore &getCAS() const { + assert(CAS && "Expect CAS available"); + return *CAS; + } + + llvm::vfs::FileSystem &getSharedCachingFS() const { + assert(CacheFS && "Expect CacheFS available"); + return *CacheFS; + } + private: /// Main routine that computes imported module dependency transitive /// closure for the given module. @@ -118,8 +264,8 @@ class ModuleDependencyScanner { /// 4. Swift overlay modules of all of the transitively imported Clang modules /// that have one ModuleDependencyIDSetVector - resolveImportedModuleDependencies(const ModuleDependencyID &rootModuleID, - ModuleDependenciesCache &cache); + resolveImportedModuleDependencies( + const ModuleDependencyID &rootModuleID, ModuleDependenciesCache &cache); void resolveSwiftModuleDependencies( const ModuleDependencyID &rootModuleID, ModuleDependenciesCache &cache, ModuleDependencyIDSetVector &discoveredSwiftModules); @@ -156,28 +302,63 @@ class ModuleDependencyScanner { StringRef mainModuleName, ModuleDependenciesCache &cache, llvm::function_ref action); - /// Performance BridgingHeader Chaining. + /// Perform Bridging Header Chaining. llvm::Error performBridgingHeaderChaining(const ModuleDependencyID &rootModuleID, ModuleDependenciesCache &cache, ModuleDependencyIDSetVector &allModules); + /// Bridge Clang dependency scanner's dependency node + /// to the Swift scanner's `ModuleDependencyInfo`. + ModuleDependencyInfo + bridgeClangModuleDependency( + const clang::tooling::dependencies::ModuleDeps &clangDependency); + /// Perform an operation utilizing one of the Scanning workers /// available to this scanner. template auto withDependencyScanningWorker(Function &&F, Args &&...ArgList); + /// Determine cache-relative output path for a given Clang module + std::string clangModuleOutputPathLookup( + const clang::tooling::dependencies::ModuleDeps &clangDep, + clang::tooling::dependencies::ModuleOutputKind moduleOutputKind) const; + + /// Use the scanner's ASTContext to construct an `Identifier` + /// for a given module name. Identifier getModuleImportIdentifier(StringRef moduleName); + /// Assuming the \c `moduleImport` failed to resolve, + /// iterate over all binary Swift module dependencies with serialized + /// search paths and attempt to diagnose if the failed-to-resolve module + /// can be found on any of them. Returns the path containing + /// the module, if one is found. + std::optional> + attemptToFindResolvingSerializedSearchPath( + const ScannerImportStatementInfo &moduleImport, + const ModuleDependenciesCache &cache); + private: const CompilerInvocation &ScanCompilerInvocation; ASTContext &ScanASTContext; - DiagnosticEngine &Diagnostics; + ModuleDependencyIssueReporter IssueReporter; + + /// The location of where the explicitly-built modules will be output to + std::string ModuleOutputPath; + /// The location of where the explicitly-built SDK modules will be output to + std::string SDKModuleOutputPath; /// The available pool of workers for filesystem module search unsigned NumThreads; std::list> Workers; llvm::DefaultThreadPool ScanningThreadPool; + // CAS instance. + std::shared_ptr CAS; + std::shared_ptr ActionCache; + /// File prefix mapper. + std::unique_ptr PrefixMapper; + /// CAS file system for loading file content. + llvm::IntrusiveRefCntPtr CacheFS; /// Protect worker access. std::mutex WorkersLock; /// Count of filesystem queries performed diff --git a/include/swift/DependencyScan/ScanDependencies.h b/include/swift/DependencyScan/ScanDependencies.h index 9a2e275662d37..350680cafa2fb 100644 --- a/include/swift/DependencyScan/ScanDependencies.h +++ b/include/swift/DependencyScan/ScanDependencies.h @@ -22,6 +22,9 @@ namespace llvm { class StringSaver; +namespace cas { +class ObjectStore; +} // namespace cas namespace vfs { class FileSystem; } // namespace vfs @@ -40,7 +43,7 @@ using ModuleDependencyIDSet = class SwiftDependencyScanningService; namespace dependencies { -class DependencyScanDiagnosticCollector; +struct ScanQueryContext; using CompilerArgInstanceCacheMap = llvm::StringMap, @@ -58,15 +61,16 @@ bool prescanDependencies(CompilerInstance &instance); // MARK: Dependency scanning execution /// Scans the dependencies of the main module of \c instance. llvm::ErrorOr -performModuleScan(CompilerInstance &instance, - DependencyScanDiagnosticCollector *diagnostics, - ModuleDependenciesCache &cache); +performModuleScan(SwiftDependencyScanningService &service, + ModuleDependenciesCache &cache, + ScanQueryContext &queryContext); /// Scans the main module of \c instance for all direct module imports llvm::ErrorOr -performModulePrescan(CompilerInstance &instance, - DependencyScanDiagnosticCollector *diagnostics, - ModuleDependenciesCache &cache); +performModulePrescan(SwiftDependencyScanningService &service, + ModuleDependenciesCache &cache, + ScanQueryContext &queryContext); + namespace incremental { /// For the given module dependency graph captured in the 'cache', @@ -76,6 +80,7 @@ namespace incremental { /// be re-scanned. void validateInterModuleDependenciesCache( const ModuleDependencyID &rootModuleID, ModuleDependenciesCache &cache, + std::shared_ptr cas, const llvm::sys::TimePoint<> &cacheTimeStamp, llvm::vfs::FileSystem &fs, DiagnosticEngine &diags, bool emitRemarks = false); @@ -85,6 +90,7 @@ void validateInterModuleDependenciesCache( /// module. void outOfDateModuleScan(const ModuleDependencyID &sourceModuleID, const ModuleDependenciesCache &cache, + std::shared_ptr cas, const llvm::sys::TimePoint<> &cacheTimeStamp, llvm::vfs::FileSystem &fs, DiagnosticEngine &diags, bool emitRemarks, ModuleDependencyIDSet &visited, @@ -94,6 +100,7 @@ void outOfDateModuleScan(const ModuleDependencyID &sourceModuleID, /// are older than the cache serialization time. bool verifyModuleDependencyUpToDate( const ModuleDependencyID &moduleID, const ModuleDependenciesCache &cache, + std::shared_ptr cas, const llvm::sys::TimePoint<> &cacheTimeStamp, llvm::vfs::FileSystem &fs, DiagnosticEngine &diags, bool emitRemarks); } // end namespace incremental diff --git a/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h index fddb8bdd721dc..e1a324ef1b5a8 100644 --- a/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h +++ b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h @@ -39,9 +39,9 @@ using llvm::BCVBR; /// Every .moddepcache file begins with these 4 bytes, for easy identification. const unsigned char MODULE_DEPENDENCY_CACHE_FORMAT_SIGNATURE[] = {'I', 'M', 'D','C'}; -const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MAJOR = 9; +const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MAJOR = 10; /// Increment this on every change. -const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MINOR = 1; +const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MINOR = 4; /// Various identifiers in this format will rely on having their strings mapped /// using this ID. @@ -56,6 +56,8 @@ using IsFrameworkField = BCFixed<1>; using IsSystemField = BCFixed<1>; /// A bit that indicates whether or not a module is that of a static archive using IsStaticField = BCFixed<1>; +/// A bit that indicates whether or not a module is built with C++ interop +using IsBuiltWithCxxInteropField = BCFixed<1>; /// A bit that indicates whether or not a link library is a force-load one using IsForceLoadField = BCFixed<1>; /// A bit that indicates whether or not an import statement is optional @@ -67,6 +69,9 @@ using IsExportedImport = BCFixed<1>; using LineNumberField = BCFixed<32>; using ColumnNumberField = BCFixed<32>; +/// Access level of an import +using AccessLevelField = BCFixed<8>; + /// Arrays of various identifiers, distinguished for readability using IdentifierIDArryField = llvm::BCArray; using ModuleIDArryField = llvm::BCArray; @@ -78,9 +83,9 @@ using ModuleCacheKeyIDField = IdentifierIDField; using ImportArrayIDField = IdentifierIDField; using LinkLibrariesArrayIDField = IdentifierIDField; using MacroDependenciesArrayIDField = IdentifierIDField; +using SearchPathArrayIDField = IdentifierIDField; using FlagIDArrayIDField = IdentifierIDField; using DependencyIDArrayIDField = IdentifierIDField; -using AuxiliaryFilesArrayIDField = IdentifierIDField; using SourceLocationIDArrayIDField = IdentifierIDField; /// The ID of the top-level block containing the dependency graph @@ -102,12 +107,13 @@ enum { LINK_LIBRARY_ARRAY_NODE, MACRO_DEPENDENCY_NODE, MACRO_DEPENDENCY_ARRAY_NODE, + SEARCH_PATH_NODE, + SEARCH_PATH_ARRAY_NODE, IMPORT_STATEMENT_NODE, IMPORT_STATEMENT_ARRAY_NODE, OPTIONAL_IMPORT_STATEMENT_ARRAY_NODE, SWIFT_INTERFACE_MODULE_DETAILS_NODE, SWIFT_SOURCE_MODULE_DETAILS_NODE, - SWIFT_PLACEHOLDER_MODULE_DETAILS_NODE, SWIFT_BINARY_MODULE_DETAILS_NODE, CLANG_MODULE_DETAILS_NODE, IDENTIFIER_NODE, @@ -170,6 +176,17 @@ using MacroDependencyLayout = using MacroDependencyArrayLayout = BCRecordLayout; +// A record for a serialized search pathof a given dependency +// node (Swift binary module dependency only). +using SearchPathLayout = + BCRecordLayout; +using SearchPathArrayLayout = + BCRecordLayout; + // A record capturing information about a given 'import' statement // captured in a dependency node, including its source location. using ImportStatementLayout = @@ -179,7 +196,8 @@ using ImportStatementLayout = LineNumberField, // lineNumber ColumnNumberField, // columnNumber IsOptionalImport, // isOptional - IsExportedImport // isExported + IsExportedImport, // isExported + AccessLevelField // accessLevel >; using ImportStatementArrayLayout = BCRecordLayout; @@ -191,7 +209,6 @@ using OptionalImportStatementArrayLayout = // - SwiftInterfaceModuleDetails // - SwiftSourceModuleDetails // - SwiftBinaryModuleDetails -// - SwiftPlaceholderModuleDetails // - ClangModuleDetails using ModuleInfoLayout = BCRecordLayout; using SwiftInterfaceModuleDetailsLayout = @@ -250,19 +266,14 @@ using SwiftBinaryModuleDetailsLayout = FileIDField, // definingInterfacePath IdentifierIDField, // headerModuleDependencies FileIDArrayIDField, // headerSourceFiles + SearchPathArrayIDField, // serializedSearchPaths IsFrameworkField, // isFramework IsStaticField, // isStatic + IsBuiltWithCxxInteropField, // IsBuiltWithCxxInterop IdentifierIDField, // moduleCacheKey IdentifierIDField // UserModuleVersion >; -using SwiftPlaceholderModuleDetailsLayout = - BCRecordLayout; - using ClangModuleDetailsLayout = BCRecordLayoutgetKind() == Action::Kind::InterpretJob; @@ -209,7 +208,7 @@ class REPLJobAction : public JobAction { Mode RequestedMode; public: REPLJobAction(Mode mode) - : JobAction(Action::Kind::REPLJob, std::nullopt, file_types::TY_Nothing), + : JobAction(Action::Kind::REPLJob, {}, file_types::TY_Nothing), RequestedMode(mode) {} Mode getRequestedMode() const { return RequestedMode; } diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index 8d73ade0b824c..d482681ab4399 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -108,11 +108,11 @@ class Compilation { DiagnosticEngine &Diags; /// The ToolChain this Compilation was built with, that it may reuse to build - /// subsequent BatchJobs. + /// subsequent Jobs. const ToolChain &TheToolChain; /// The OutputInfo, which the Compilation stores a copy of upon - /// construction, and which it may use to build subsequent batch + /// construction, and which it may use to build subsequent /// jobs itself. OutputInfo TheOutputInfo; @@ -167,22 +167,6 @@ class Compilation { /// even if they returned an error status. bool ContinueBuildingAfterErrors = false; - /// Indicates whether groups of parallel frontend jobs should be merged - /// together and run in composite "batch jobs" when possible, to reduce - /// redundant work. - const bool EnableBatchMode; - - /// Provides a randomization seed to batch-mode partitioning, for debugging. - const unsigned BatchSeed; - - /// Overrides parallelism level and \c BatchSizeLimit, sets exact - /// count of batches, if in batch-mode. - const std::optional BatchCount; - - /// Overrides maximum batch size, if in batch-mode and not overridden - /// by \c BatchCount. - const std::optional BatchSizeLimit; - /// True if temporary files should not be deleted. const bool SaveTemps; @@ -244,10 +228,6 @@ class Compilation { std::unique_ptr TranslatedArgs, InputFileList InputsWithTypes, size_t FilelistThreshold, - bool EnableBatchMode = false, - unsigned BatchSeed = 0, - std::optional BatchCount = std::nullopt, - std::optional BatchSizeLimit = std::nullopt, bool SaveTemps = false, bool ShowDriverTimeCompilation = false, std::unique_ptr Stats = nullptr, @@ -302,10 +282,6 @@ class Compilation { return DerivedOutputFileMap; } - bool getBatchModeEnabled() const { - return EnableBatchMode; - } - bool getContinueBuildingAfterErrors() const { return ContinueBuildingAfterErrors; } @@ -352,14 +328,6 @@ class Compilation { return Level; } - unsigned getBatchSeed() const { - return BatchSeed; - } - - std::optional getBatchCount() const { return BatchCount; } - - std::optional getBatchSizeLimit() const { return BatchSizeLimit; } - /// Requests the path to a file containing all input source files. This can /// be shared across jobs. /// @@ -399,7 +367,7 @@ class Compilation { /// Unfortunately the success or failure of a Swift compilation is currently /// sensitive to the order in which files are processed, at least in terms of /// the order of processing extensions (and likely other ways we haven't - /// discovered yet). So long as this is true, we need to make sure any batch + /// discovered yet). So long as this is true, we need to make sure any /// job we build names its inputs in an order that's a subsequence of the /// sequence of inputs the driver was initially invoked with. /// diff --git a/include/swift/Driver/Driver.h b/include/swift/Driver/Driver.h index 5e506712e2335..2bc75f6cfa209 100644 --- a/include/swift/Driver/Driver.h +++ b/include/swift/Driver/Driver.h @@ -67,19 +67,6 @@ class OutputInfo { /// A compilation using a single frontend invocation without -primary-file. SingleCompile, - /// A single process that batches together multiple StandardCompile Jobs. - /// - /// Note: this is a transient value to use _only_ for the individual - /// BatchJobs that are the temporary containers for multiple StandardCompile - /// Jobs built by ToolChain::constructBatchJob. - /// - /// In particular, the driver treats a batch-mode-enabled Compilation as - /// having OutputInfo::CompilerMode == StandardCompile, with the - /// Compilation::BatchModeEnabled flag set to true, _not_ as a - /// BatchModeCompile Compilation. The top-level OutputInfo::CompilerMode for - /// a Compilation should never be BatchModeCompile. - BatchModeCompile, - /// Invoke the REPL REPL, @@ -171,7 +158,7 @@ class Driver { /// allowable OutputInfo::Mode values. enum class DriverKind { Interactive, // swift - Batch, // swiftc + Standard, // swiftc SILOpt, // sil-opt SILFuncExtractor,// sil-func-extractor SILNM, // sil-nm @@ -313,14 +300,12 @@ class Driver { /// /// \param TC The current tool chain. /// \param Args The input arguments. - /// \param BatchMode Whether the driver has been explicitly or implicitly - /// instructed to use batch mode. /// \param Inputs The inputs to the driver. /// \param[out] OI The OutputInfo in which to store the resulting output /// information. void buildOutputInfo(const ToolChain &TC, const llvm::opt::DerivedArgList &Args, - const bool BatchMode, const InputFileList &Inputs, + const InputFileList &Inputs, OutputInfo &OI) const; /// Construct the list of Actions to perform for the given arguments, @@ -472,11 +457,8 @@ class Driver { /// there is an actual conflict. /// \param Args The input arguments. /// \param Inputs The inputs to the driver. - /// \param BatchModeOut An out-parameter flag that indicates whether to - /// batch the jobs of the resulting \c Mode::StandardCompile compilation. OutputInfo::Mode computeCompilerMode(const llvm::opt::DerivedArgList &Args, - const InputFileList &Inputs, - bool &BatchModeOut) const; + const InputFileList &Inputs) const; }; } // end namespace driver diff --git a/include/swift/Driver/Job.h b/include/swift/Driver/Job.h index 3a2c28516732f..4c69181e1a414 100644 --- a/include/swift/Driver/Job.h +++ b/include/swift/Driver/Job.h @@ -389,8 +389,7 @@ class Job { StringRef Terminator = "\n") const; /// Call the provided Callback with any Jobs (and their possibly-quasi-PIDs) - /// contained within this Job; if this job is not a BatchJob, just pass \c - /// this and the provided \p OSPid back to the Callback. + /// contained within this Job; virtual void forEachContainedJobAndPID( llvm::sys::procid_t OSPid, llvm::function_ref Callback) const { @@ -411,51 +410,6 @@ class Job { StringRef getFirstSwiftPrimaryInput() const; }; -/// A BatchJob comprises a _set_ of jobs, each of which is sufficiently similar -/// to the others that the whole set can be combined into a single subprocess -/// (and thus run potentially more-efficiently than running each Job in the set -/// individually). -/// -/// Not all Jobs can be combined into a BatchJob: at present, only those Jobs -/// that come from CompileJobActions, and which otherwise have the exact same -/// input file list and arguments as one another, aside from their primary-file. -/// See ToolChain::jobsAreBatchCombinable for details. - -class BatchJob : public Job { - - /// The set of constituents making up the batch. - const SmallVector CombinedJobs; - - /// A negative number to use as the base value for assigning quasi-PID to Jobs - /// in the \c CombinedJobs array. Quasi-PIDs count _down_ from this value. - const Job::PID QuasiPIDBase; - -public: - BatchJob(const JobAction &Source, SmallVectorImpl &&Inputs, - std::unique_ptr Output, const char *Executable, - llvm::opt::ArgStringList Arguments, - EnvironmentVector ExtraEnvironment, std::vector Infos, - ArrayRef Combined, Job::PID &NextQuasiPID, - std::optional ResponseFile = std::nullopt); - - ArrayRef getCombinedJobs() const { - return CombinedJobs; - } - - /// Call the provided callback for each Job in the batch, passing the - /// corresponding quasi-PID with each Job. - void forEachContainedJobAndPID( - llvm::sys::procid_t OSPid, - llvm::function_ref Callback) const override { - Job::PID QPid = QuasiPIDBase; - assert(QPid < 0); - for (auto const *J : CombinedJobs) { - assert(QPid != std::numeric_limits::min()); - Callback(J, QPid--); - } - } -}; - } // end namespace driver } // end namespace swift diff --git a/include/swift/Driver/ToolChain.h b/include/swift/Driver/ToolChain.h index d438980d9a18e..415b608dc6171 100644 --- a/include/swift/Driver/ToolChain.h +++ b/include/swift/Driver/ToolChain.h @@ -98,7 +98,7 @@ class ToolChain { bool shouldUseSupplementaryOutputFileMapInFrontendInvocation() const; /// Reify the existing behavior that SingleCompile compile actions do not - /// filter, but batch-mode and single-file compilations do. Some clients are + /// filter, single-file compilations do. Some clients are /// relying on this (i.e., they pass inputs that don't have ".swift" as an /// extension.) It would be nice to eliminate this distinction someday. bool shouldFilterFrontendInputsByType() const; @@ -281,33 +281,6 @@ class ToolChain { std::unique_ptr output, const OutputInfo &OI) const; - /// Return true iff the input \c Job \p A is an acceptable candidate for - /// batching together into a BatchJob, via a call to \c - /// constructBatchJob. This is true when the \c Job is a built from a \c - /// CompileJobAction in a \c Compilation \p C running in \c - /// OutputInfo::Mode::StandardCompile output mode, with a single \c TY_Swift - /// \c InputAction. - bool jobIsBatchable(const Compilation &C, const Job *A) const; - - /// Equivalence relation that holds iff the two input Jobs \p A and \p B are - /// acceptable candidates for combining together into a \c BatchJob, via a - /// call to \c constructBatchJob. This is true when each job independently - /// satisfies \c jobIsBatchable, and the two jobs have identical executables, - /// output types and environments (i.e. they are identical aside from their - /// inputs). - bool jobsAreBatchCombinable(const Compilation &C, const Job *A, - const Job *B) const; - - /// Construct a \c BatchJob that subsumes the work of a set of Jobs. Any pair - /// of elements in \p Jobs are assumed to satisfy the equivalence relation \c - /// jobsAreBatchCombinable, i.e. they should all be "the same" job in in all - /// ways other than their choices of inputs. The provided \p NextQuasiPID - /// should be a negative number that persists between calls; this method will - /// decrement it to assign quasi-PIDs to each of the \p Jobs passed. - std::unique_ptr constructBatchJob(ArrayRef Jobs, - int64_t &NextQuasiPID, - Compilation &C) const; - /// Return the default language type to use for the given extension. /// If the extension is empty or is otherwise not recognized, return /// the invalid type \c TY_INVALID. diff --git a/include/swift/Frontend/CachingUtils.h b/include/swift/Frontend/CachingUtils.h index e478c22daf56c..d676ccbcaa4e6 100644 --- a/include/swift/Frontend/CachingUtils.h +++ b/include/swift/Frontend/CachingUtils.h @@ -70,9 +70,9 @@ std::unique_ptr loadCachedCompileResultFromCacheKey( llvm::StringRef Filename = ""); llvm::Expected> -createCASFileSystem(llvm::cas::ObjectStore &CAS, ArrayRef FSRoots, - ArrayRef IncludeTreeRoots, - ArrayRef IncludeTreeFileList); +createCASFileSystem(llvm::cas::ObjectStore &CAS, + const std::string &IncludeTreeRoot, + const std::string &IncludeTreeFileList); std::vector remapPathsFromCommandLine( ArrayRef Args, diff --git a/include/swift/Frontend/CompileJobCacheKey.h b/include/swift/Frontend/CompileJobCacheKey.h index f32208f3a5511..fdc1090604c17 100644 --- a/include/swift/Frontend/CompileJobCacheKey.h +++ b/include/swift/Frontend/CompileJobCacheKey.h @@ -51,6 +51,11 @@ llvm::Error printCompileJobCacheKey(llvm::cas::ObjectStore &CAS, llvm::cas::ObjectRef Key, llvm::raw_ostream &os); +/// Iterating through command-line options in cache key. +llvm::Error iterateCommandLine(llvm::cas::ObjectStore &CAS, + llvm::cas::ObjectRef Key, + std::function Callback); + } // namespace swift #endif diff --git a/include/swift/Frontend/DiagnosticVerifier.h b/include/swift/Frontend/DiagnosticVerifier.h index 5eb293f793137..8cd5ad1a03d0d 100644 --- a/include/swift/Frontend/DiagnosticVerifier.h +++ b/include/swift/Frontend/DiagnosticVerifier.h @@ -18,10 +18,15 @@ #ifndef SWIFT_FRONTEND_DIAGNOSTIC_VERIFIER_H #define SWIFT_FRONTEND_DIAGNOSTIC_VERIFIER_H +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" #include "swift/AST/DiagnosticConsumer.h" #include "swift/Basic/LLVM.h" +namespace { +struct ExpectedDiagnosticInfo; +} + namespace swift { class DependencyTracker; class FileUnit; @@ -94,6 +99,7 @@ class DiagnosticVerifier : public DiagnosticConsumer { ArrayRef AdditionalFilePaths; bool AutoApplyFixes; bool IgnoreUnknown; + bool IgnoreUnrelated; bool UseColor; ArrayRef AdditionalExpectedPrefixes; @@ -101,11 +107,11 @@ class DiagnosticVerifier : public DiagnosticConsumer { explicit DiagnosticVerifier(SourceManager &SM, ArrayRef BufferIDs, ArrayRef AdditionalFilePaths, bool AutoApplyFixes, bool IgnoreUnknown, - bool UseColor, + bool IgnoreUnrelated, bool UseColor, ArrayRef AdditionalExpectedPrefixes) : SM(SM), BufferIDs(BufferIDs), AdditionalFilePaths(AdditionalFilePaths), AutoApplyFixes(AutoApplyFixes), IgnoreUnknown(IgnoreUnknown), - UseColor(UseColor), + IgnoreUnrelated(IgnoreUnrelated), UseColor(UseColor), AdditionalExpectedPrefixes(AdditionalExpectedPrefixes) {} virtual void handleDiagnostic(SourceManager &SM, @@ -126,13 +132,33 @@ class DiagnosticVerifier : public DiagnosticConsumer { void printDiagnostic(const llvm::SMDiagnostic &Diag) const; + /// Check whether there were any diagnostics in files without expected + /// diagnostics + bool verifyUnrelated( + std::vector &CapturedDiagnostics) const; + bool verifyUnknown(std::vector &CapturedDiagnostics) const; + std::vector Errors; + /// verifyFile - After the file has been processed, check to see if we /// got all of the expected diagnostics and check to see if there were any /// unexpected ones. Result verifyFile(unsigned BufferID); + unsigned parseExpectedDiagInfo(unsigned BufferID, StringRef MatchStart, + unsigned &PrevExpectedContinuationLine, + ExpectedDiagnosticInfo &Expected); + void + verifyDiagnostics(std::vector &ExpectedDiagnostics, + unsigned BufferID); + void verifyRemaining(std::vector &ExpectedDiagnostics, + const char *FileStart); + void addError(const char *Loc, const Twine &message, + ArrayRef FixIts = {}); + + std::optional + parseExpectedFixItRange(StringRef &Str, unsigned DiagnosticLineNo); bool checkForFixIt(const std::vector &ExpectedAlts, const CapturedDiagnosticInfo &D, unsigned BufferID) const; @@ -141,6 +167,8 @@ class DiagnosticVerifier : public DiagnosticConsumer { std::string renderFixits(ArrayRef ActualFixIts, unsigned BufferID, unsigned DiagnosticLineNo) const; + llvm::DenseMap Expansions; + void printRemainingDiagnostics() const; }; diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 55d984a53db8a..9459134f6b8f5 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -176,7 +176,8 @@ class CompilerInvocation { } bool requiresCAS() const { - return CASOpts.EnableCaching || IRGenOpts.UseCASBackend; + return CASOpts.EnableCaching || IRGenOpts.UseCASBackend || + CASOpts.ImportModuleFromCAS; } void setClangModuleCachePath(StringRef Path) { @@ -273,6 +274,9 @@ class CompilerInvocation { /// C++ stdlib is the default for the specified target. void computeCXXStdlibOptions(); + /// Compute whether or not we support aarch64TBI + void computeAArch64TBIOptions(); + /// Computes the runtime resource path relative to the given Swift /// executable. static void computeRuntimeResourcePathFromExecutablePath( @@ -709,6 +713,12 @@ class CompilerInstance { } } + SourceFile &getPrimaryOrMainSourceFile() const { + if (SourceFile *SF = getPrimarySourceFile()) + return *SF; + return getMainModule()->getMainSourceFile(); + } + /// Returns true if there was an error during setup. bool setup(const CompilerInvocation &Invocation, std::string &Error, ArrayRef Args = {}); @@ -791,6 +801,9 @@ class CompilerInstance { /// \returns true if any errors occurred. bool performSILProcessing(SILModule *silModule); + /// Dumps any debugging output for the compilation, if requested. + void emitEndOfPipelineDebuggingOutput(); + private: /// Creates a new source file for the main module. SourceFile *createSourceFileForMainModule(ModuleDecl *mod, diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index 79de1de1c4040..200993d3308e0 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -253,6 +253,9 @@ class FrontendInputsAndOutputs { const PrimarySpecificPaths & getPrimarySpecificPathsForAtMostOnePrimary() const; + const PrimarySpecificPaths & + getPrimarySpecificPathsForRemaining(unsigned i) const; + const PrimarySpecificPaths & getPrimarySpecificPathsForPrimary(StringRef) const; diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 29a3f657f8fb0..68101735374a6 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -35,6 +35,21 @@ namespace llvm { namespace swift { enum class IntermoduleDepTrackingMode; +/// Options for debugging the behavior of the frontend. +struct CompilerDebuggingOptions { + /// Indicates whether or not the Clang importer should print statistics upon + /// termination. + bool PrintClangStats = false; + + /// Indicates whether or not the availability scope trees built during + /// compilation should be dumped upon termination. + bool DumpAvailabilityScopes = false; + + /// Indicates whether or not the Clang importer should dump lookup tables + /// upon termination. + bool DumpClangLookupTables = false; +}; + /// Options for controlling the behavior of the frontend. class FrontendOptions { friend class ArgsToFrontendOptionsConverter; @@ -51,14 +66,18 @@ class FrontendOptions { bool isOutputFileDirectory() const; - /// An Objective-C header to import and make implicitly visible. + /// A C header to import and make implicitly visible. std::string ImplicitObjCHeaderPath; - /// An Objective-C pch to import and make implicitly visible. + /// A C pch to import and make implicitly visible. std::string ImplicitObjCPCHPath; + /// Whether the imported C header or precompiled header is considered + /// an internal import (vs. the default, a public import). + bool ImportHeaderAsInternal = false; + /// The map of aliases and real names of imported or referenced modules. - llvm::StringMap ModuleAliasMap; + llvm::StringMap ModuleAliasMap; /// The name of the module that the frontend is building. std::string ModuleName; @@ -92,9 +111,6 @@ class FrontendOptions { /// The path to which we should store indexing data, if any. std::string IndexStorePath; - /// The path to load access notes from. - std::string AccessNotesPath; - /// The path to look in when loading a module interface file, to see if a /// binary module has already been built for use by the compiler. std::string PrebuiltModuleCachePath; @@ -124,6 +140,9 @@ class FrontendOptions { /// A set of modules allowed to import this module. std::set AllowableClients; + /// Options for debugging the compiler. + CompilerDebuggingOptions CompilerDebuggingOpts; + /// Emit index data for imported serialized swift system modules. bool IndexSystemModules = false; @@ -135,6 +154,9 @@ class FrontendOptions { /// Include local definitions/references in the index data. bool IndexIncludeLocals = false; + + /// Whether to compress the record and unit files in the index store. + bool IndexStoreCompress = false; bool SerializeDebugInfoSIL = false; /// If building a module from interface, ignore compiler flags @@ -145,7 +167,7 @@ class FrontendOptions { std::string VerifyGenericSignaturesInModule; /// CacheReplay PrefixMap. - std::vector CacheReplayPrefixMap; + std::vector> CacheReplayPrefixMap; /// Number of retry opening an input file if the previous opening returns /// bad file descriptor error. @@ -164,9 +186,6 @@ class FrontendOptions { /// Parse and dump scope map. DumpScopeMaps, - /// Parse, type-check, and dump availability scopes - DumpAvailabilityScopes, - EmitImportedModules, ///< Emit the modules that this one imports EmitPCH, ///< Emit PCH of imported bridging header @@ -201,7 +220,7 @@ class FrontendOptions { ScanDependencies, ///< Scan dependencies of Swift source files PrintVersion, ///< Print version information. - PrintFeature, ///< Print supported feature of this compiler + PrintArguments, ///< Print supported arguments of this compiler }; /// Indicates the action the user requested that the frontend perform. @@ -279,11 +298,6 @@ class FrontendOptions { /// \see ModuleDecl::isImplicitDynamicEnabled bool EnableImplicitDynamic = false; - /// Enables the "fully resilient" resilience strategy. - /// - /// \see ResilienceStrategy::Resilient - bool EnableLibraryEvolution = false; - /// If set, this module is part of a mixed Objective-C/Swift framework, and /// the Objective-C half should implicitly be visible to the Swift sources. bool ImportUnderlyingModule = false; @@ -292,18 +306,16 @@ class FrontendOptions { /// by the Clang importer as part of semantic analysis. bool ModuleHasBridgingHeader = false; + /// Generate reproducer. + bool GenReproducer = false; + + /// Directory to generate reproducer. + std::string GenReproducerDir; + /// Indicates whether or not the frontend should print statistics upon /// termination. bool PrintStats = false; - /// Indicates whether or not the Clang importer should print statistics upon - /// termination. - bool PrintClangStats = false; - - /// Indicates whether or not the Clang importer should dump lookup tables - /// upon termination. - bool DumpClangLookupTables = false; - /// Indicates whether standard help should be shown. bool PrintHelp = false; @@ -314,6 +326,14 @@ class FrontendOptions { /// exit. bool PrintTargetInfo = false; + /// Indicates that the frontend should print the static build configuration + /// information as JSON. + bool PrintBuildConfig = false; + + /// Indicates that the frontend should print the supported features and then + /// exit. + bool PrintSupportedFeatures = false; + /// See the \ref SILOptions.EmitVerboseSIL flag. bool EmitVerboseSIL = false; @@ -595,6 +615,8 @@ class FrontendOptions { struct CustomAvailabilityDomains { /// Domains defined with `-define-enabled-availability-domain=`. llvm::SmallVector EnabledDomains; + /// Domains defined with `-define-always-enabled-availability-domain=`. + llvm::SmallVector AlwaysEnabledDomains; /// Domains defined with `-define-disabled-availability-domain=`. llvm::SmallVector DisabledDomains; /// Domains defined with `-define-dynamic-availability-domain=`. diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index d6feda266222e..c1cbdb7ed7d85 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -111,6 +111,8 @@ #include "swift/Frontend/Frontend.h" #include "swift/Frontend/ModuleInterfaceSupport.h" #include "swift/Serialization/SerializedModuleLoader.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/YAMLTraits.h" @@ -150,7 +152,7 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase { std::unique_ptr *moduleBuffer, std::unique_ptr *moduleDocBuffer, std::unique_ptr *moduleSourceInfoBuffer, - bool skipBuildingInterface, bool isTestableDependencyLookup, + bool isCanImportLookup, bool isTestableDependencyLookup, bool &isFramework, bool &isSystemModule) override; std::error_code findModuleFilesInDirectory( @@ -160,7 +162,7 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase { std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer, - bool SkipBuildingInterface, bool IsFramework, + bool isCanImportLookup, bool IsFramework, bool IsTestableDependencyLookup = false) override; bool canImportModule(ImportPath::Module named, SourceLoc loc, @@ -176,7 +178,7 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase { create(ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode, StringRef ExplicitSwiftModuleMap, - const std::vector> &ExplicitSwiftModuleInputs, + const llvm::StringMap &ExplicitSwiftModuleInputs, bool IgnoreSwiftSourceInfoFile); /// Append visible module names to \p names. Note that names are possibly @@ -199,7 +201,7 @@ class ExplicitCASModuleLoader : public SerializedModuleLoaderBase { std::unique_ptr *moduleBuffer, std::unique_ptr *moduleDocBuffer, std::unique_ptr *moduleSourceInfoBuffer, - bool skipBuildingInterface, bool isTestableDependencyLookup, + bool isCanImportLookup, bool isTestableDependencyLookup, bool &isFramework, bool &isSystemModule) override; std::error_code findModuleFilesInDirectory( @@ -209,7 +211,7 @@ class ExplicitCASModuleLoader : public SerializedModuleLoaderBase { std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer, - bool SkipBuildingInterface, bool IsFramework, + bool IsCanImportLookup, bool IsFramework, bool IsTestableDependencyLookup = false) override; bool canImportModule(ImportPath::Module named, SourceLoc loc, @@ -224,8 +226,7 @@ class ExplicitCASModuleLoader : public SerializedModuleLoaderBase { create(ASTContext &ctx, llvm::cas::ObjectStore &CAS, llvm::cas::ActionCache &cache, DependencyTracker *tracker, ModuleLoadingMode loadMode, StringRef ExplicitSwiftModuleMap, - const std::vector> - &ExplicitSwiftModuleInputs, + const llvm::StringMap &ExplicitSwiftModuleInputs, bool IgnoreSwiftSourceInfoFile); /// Append visible module names to \p names. Note that names are possibly @@ -318,7 +319,7 @@ class ExplicitModuleMapParser { public: ExplicitModuleMapParser(llvm::BumpPtrAllocator &Allocator) : Saver(Allocator) {} - std::error_code parseSwiftExplicitModuleMap( + llvm::Error parseSwiftExplicitModuleMap( llvm::MemoryBufferRef BufferRef, llvm::StringMap &swiftModuleMap, llvm::StringMap &clangModuleMap, @@ -332,16 +333,15 @@ class ExplicitModuleMapParser { assert(DI != Stream.end() && "Failed to read a document"); if (auto *MN = dyn_cast_or_null(DI->getRoot())) { for (auto &entry : *MN) { - if (parseSingleModuleEntry(entry, swiftModuleMap, clangModuleMap, - moduleAliases)) { - return std::make_error_code(std::errc::invalid_argument); - } + if (auto Err = parseSingleModuleEntry(entry, swiftModuleMap, + clangModuleMap, moduleAliases)) + return Err; } } else { - return std::make_error_code(std::errc::invalid_argument); + return llvm::createStringError("invalid JSON root object"); } } - return std::error_code{}; // success + return llvm::Error::success(); // success } private: @@ -361,7 +361,7 @@ class ExplicitModuleMapParser { llvm_unreachable("Unexpected JSON value for isFramework"); } - bool parseSingleModuleEntry( + llvm::Error parseSingleModuleEntry( llvm::yaml::Node &node, llvm::StringMap &swiftModuleMap, llvm::StringMap &clangModuleMap, @@ -369,7 +369,7 @@ class ExplicitModuleMapParser { using namespace llvm::yaml; auto *mapNode = dyn_cast(&node); if (!mapNode) - return true; + return llvm::createStringError("incorrect entry type"); StringRef moduleName; std::optional swiftModulePath, swiftModuleDocPath, swiftModuleSourceInfoPath, swiftModuleCacheKey, clangModuleCacheKey, @@ -419,7 +419,7 @@ class ExplicitModuleMapParser { } } if (moduleName.empty()) - return true; + return llvm::createStringError("entry is missing module name"); bool didInsert; if (swiftModulePath.has_value()) { @@ -446,17 +446,22 @@ class ExplicitModuleMapParser { clangModuleCacheKey); didInsert = clangModuleMap.try_emplace(moduleName, std::move(entry)).second; } - if (didInsert && moduleAlias.has_value()) { + if (!didInsert) + return llvm::createStringError(llvm::formatv( + "duplicate {0} module with name {1}", + swiftModulePath.has_value() ? "Swift" : "Clang", moduleName)); + + if (moduleAlias.has_value()) { moduleAliases[*moduleAlias] = moduleName; } - // Prevent duplicate module names. - return !didInsert; + return llvm::Error::success(); } llvm::StringSaver Saver; }; -struct ModuleInterfaceLoaderOptions { +class ModuleInterfaceLoaderOptions { +public: FrontendOptions::ActionType requestedAction = FrontendOptions::ActionType::EmitModuleOnly; bool remarkOnRebuildFromInterface = false; @@ -465,48 +470,12 @@ struct ModuleInterfaceLoaderOptions { bool disableBuildingInterface = false; bool downgradeInterfaceVerificationError = false; bool strictImplicitModuleContext = false; + CompilerDebuggingOptions compilerDebuggingOptions; std::string mainExecutablePath; - ModuleInterfaceLoaderOptions(const FrontendOptions &Opts): - remarkOnRebuildFromInterface(Opts.RemarkOnRebuildFromModuleInterface), - disableInterfaceLock(Opts.DisableInterfaceFileLock), - disableImplicitSwiftModule(Opts.DisableImplicitModules), - disableBuildingInterface(Opts.DisableBuildingInterface), - downgradeInterfaceVerificationError(Opts.DowngradeInterfaceVerificationError), - strictImplicitModuleContext(Opts.StrictImplicitModuleContext), - mainExecutablePath(Opts.MainExecutablePath) - { - switch (Opts.RequestedAction) { - case FrontendOptions::ActionType::TypecheckModuleFromInterface: - requestedAction = FrontendOptions::ActionType::Typecheck; - break; - case FrontendOptions::ActionType::ScanDependencies: - requestedAction = Opts.RequestedAction; - break; - default: - requestedAction = FrontendOptions::ActionType::EmitModuleOnly; - break; - } - } - ModuleInterfaceLoaderOptions() = default; -}; - -/// Strongly typed enum that represents if we require all SILModules to have -/// OSSA modules emitted. This is implemented by incorporating this bit into the -/// module cache hash. -struct RequireOSSAModules_t { - enum ValueTy { - No = 0, - Yes = 1, - }; - ValueTy value; - - RequireOSSAModules_t(const SILOptions &opts) - : value(opts.EnableOSSAModules ? RequireOSSAModules_t::Yes - : RequireOSSAModules_t::No) {} - - operator ValueTy() const { return value; } - explicit operator bool() const { return bool(value); } + ModuleInterfaceLoaderOptions(const FrontendOptions &Opts, + bool inheritDebuggingOpts = false); + ModuleInterfaceLoaderOptions() = default; }; class ModuleInterfaceCheckerImpl: public ModuleInterfaceChecker { @@ -516,23 +485,20 @@ class ModuleInterfaceCheckerImpl: public ModuleInterfaceChecker { std::string PrebuiltCacheDir; std::string BackupInterfaceDir; ModuleInterfaceLoaderOptions Opts; - RequireOSSAModules_t RequiresOSSAModules; public: explicit ModuleInterfaceCheckerImpl(ASTContext &Ctx, StringRef cacheDir, StringRef prebuiltCacheDir, StringRef BackupInterfaceDir, - ModuleInterfaceLoaderOptions opts, - RequireOSSAModules_t requiresOSSAModules) + ModuleInterfaceLoaderOptions opts) : Ctx(Ctx), CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir), BackupInterfaceDir(BackupInterfaceDir), - Opts(opts), RequiresOSSAModules(requiresOSSAModules) {} + Opts(opts) {} explicit ModuleInterfaceCheckerImpl(ASTContext &Ctx, StringRef cacheDir, StringRef prebuiltCacheDir, - ModuleInterfaceLoaderOptions opts, - RequireOSSAModules_t requiresOSSAModules): + ModuleInterfaceLoaderOptions opts): ModuleInterfaceCheckerImpl(Ctx, cacheDir, prebuiltCacheDir, StringRef(), - opts, requiresOSSAModules) {} + opts) {} std::vector getCompiledModuleCandidatesForInterface(StringRef moduleName, StringRef interfacePath) override; @@ -573,7 +539,7 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer, - bool SkipBuildingInterface, bool IsFramework, + bool IsCanImportLookup, bool IsFramework, bool IsTestableDependencyLookup = false) override; bool isCached(StringRef DepPath) override; @@ -605,9 +571,9 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { StringRef CacheDir, StringRef PrebuiltCacheDir, StringRef BackupInterfaceDir, StringRef ModuleName, StringRef InPath, StringRef OutPath, StringRef ABIOutputPath, + ArrayRef> replayPrefixMap, bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions Opts, - RequireOSSAModules_t RequireOSSAModules, bool silenceInterfaceDiagnostics); /// Unconditionally build \p InPath (a swiftinterface file) to \p OutPath (as @@ -677,8 +643,7 @@ struct InterfaceSubContextDelegateImpl : InterfaceSubContextDelegate { const LangOptions &LangOpts, const ClangImporterOptions &clangImporterOpts, const CASOptions &casOpts, - bool suppressRemarks, - RequireOSSAModules_t requireOSSAModules); + bool suppressNotes, bool suppressRemarks); bool extractSwiftInterfaceVersionAndArgs(CompilerInvocation &subInvocation, DiagnosticEngine &subInstanceDiags, SwiftInterfaceInfo &interfaceInfo, @@ -692,8 +657,10 @@ struct InterfaceSubContextDelegateImpl : InterfaceSubContextDelegate { const ClangImporterOptions &clangImporterOpts, const CASOptions &casOpts, ModuleInterfaceLoaderOptions LoaderOpts, bool buildModuleCacheDirIfAbsent, StringRef moduleCachePath, StringRef prebuiltCachePath, - StringRef backupModuleInterfaceDir, bool serializeDependencyHashes, - bool trackSystemDependencies, RequireOSSAModules_t requireOSSAModules); + StringRef backupModuleInterfaceDir, + ArrayRef> replayPrefixMap, + bool serializeDependencyHashes, + bool trackSystemDependencies); template static InFlightDiagnostic diagnose(StringRef interfacePath, diff --git a/include/swift/Frontend/SerializedDiagnosticConsumer.h b/include/swift/Frontend/SerializedDiagnosticConsumer.h index 3445ae4054e9a..689cba3827280 100644 --- a/include/swift/Frontend/SerializedDiagnosticConsumer.h +++ b/include/swift/Frontend/SerializedDiagnosticConsumer.h @@ -37,6 +37,15 @@ namespace swift { /// \returns A new diagnostic consumer that serializes diagnostics. std::unique_ptr createConsumer(llvm::StringRef outputPath, bool emitMacroExpansionFiles); + + /// Create a thread-safe DiagnosticConsumer that serializes diagnostics to a file, + /// using Clang's serialized diagnostics format. + /// + /// \param outputPath the file path to write the diagnostics to. + /// + /// \returns A new diagnostic consumer that serializes diagnostics. + std::unique_ptr + createThreadSafeConsumer(llvm::StringRef outputPath, bool emitMacroExpansionFiles); } } diff --git a/include/swift/IDE/ArgumentCompletion.h b/include/swift/IDE/ArgumentCompletion.h index cc1150d372385..1b7ff5c377df8 100644 --- a/include/swift/IDE/ArgumentCompletion.h +++ b/include/swift/IDE/ArgumentCompletion.h @@ -21,6 +21,8 @@ namespace swift { namespace ide { +struct Signature; + class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback { struct Result { /// The type associated with the code completion expression itself. @@ -75,6 +77,12 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback { /// functions is supported. bool IsInAsyncContext; + /// True if the function is an implicitly curried instance method. + bool IsImplicitlyCurried; + + /// True if the call is the second apply of a double-applied function. + bool IsSecondApply; + /// A bitfield to mark whether the parameter at a given index is optional. /// Parameters can be optional if they have a default argument or belong to /// a parameter pack. @@ -117,9 +125,12 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback { /// \param IsLabeledTrailingClosure Whether we are completing the label of a /// labeled trailing closure, ie. if the code completion location is outside /// the call after the first trailing closure of the call. - void collectResults(bool IsLabeledTrailingClosure, - SourceLoc Loc, DeclContext *DC, - CodeCompletionContext &CompletionCtx); + void collectResults(bool IsLabeledTrailingClosure, SourceLoc Loc, + DeclContext *DC, CodeCompletionContext &CompletionCtx); + + /// Collects non-shadowed signature results into \p Signatures + void getSignatures(SourceLoc Loc, DeclContext *DC, + SmallVectorImpl &Signatures); }; } // end namespace ide diff --git a/include/swift/IDE/CodeCompletionContext.h b/include/swift/IDE/CodeCompletionContext.h index b7625f1408d57..00c448ab02a37 100644 --- a/include/swift/IDE/CodeCompletionContext.h +++ b/include/swift/IDE/CodeCompletionContext.h @@ -86,6 +86,9 @@ class CodeCompletionContext { return CurrentResults.addCallWithNoDefaultArgs; } + void setVerifyUSRToDecl(bool flag) { CurrentResults.verifyUSRToDecl = flag; } + bool verifyUSRToDecl() const { return CurrentResults.verifyUSRToDecl; } + /// Allocate a string owned by the code completion context. StringRef copyString(StringRef Str) { return Str.copy(*CurrentResults.Allocator); diff --git a/include/swift/IDE/CodeCompletionResult.h b/include/swift/IDE/CodeCompletionResult.h index 83b067aaa3eae..c9221ea6b30d3 100644 --- a/include/swift/IDE/CodeCompletionResult.h +++ b/include/swift/IDE/CodeCompletionResult.h @@ -16,6 +16,7 @@ #include "swift/Basic/StringExtras.h" #include "swift/IDE/CodeCompletionResultType.h" #include "swift/IDE/CodeCompletionString.h" +#include "swift/IDE/CommentConversion.h" namespace swift { namespace ide { @@ -190,6 +191,7 @@ enum class CodeCompletionKeywordKind : uint8_t { enum class CompletionKind : uint8_t { None, Import, + Using, UnresolvedMember, DotExpr, StmtOrExpr, @@ -378,6 +380,9 @@ class ContextFreeCodeCompletionResult { NullTerminatedStringRef ModuleName; NullTerminatedStringRef BriefDocComment; ArrayRef AssociatedUSRs; + /// The Swift USR for a declaration (including for Clang declarations) used + /// for looking up the \c Decl instance for cached results. + NullTerminatedStringRef SwiftUSR; CodeCompletionResultType ResultType; ContextFreeNotRecommendedReason NotRecommended : 3; @@ -410,7 +415,7 @@ class ContextFreeCodeCompletionResult { NullTerminatedStringRef ModuleName, NullTerminatedStringRef BriefDocComment, ArrayRef AssociatedUSRs, - CodeCompletionResultType ResultType, + NullTerminatedStringRef SwiftUSR, CodeCompletionResultType ResultType, ContextFreeNotRecommendedReason NotRecommended, CodeCompletionDiagnosticSeverity DiagnosticSeverity, NullTerminatedStringRef DiagnosticMessage, @@ -421,8 +426,8 @@ class ContextFreeCodeCompletionResult { HasAsyncAlternative(HasAsyncAlternative), CompletionString(CompletionString), ModuleName(ModuleName), BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs), - ResultType(ResultType), NotRecommended(NotRecommended), - DiagnosticSeverity(DiagnosticSeverity), + SwiftUSR(SwiftUSR), ResultType(ResultType), + NotRecommended(NotRecommended), DiagnosticSeverity(DiagnosticSeverity), DiagnosticMessage(DiagnosticMessage), FilterName(FilterName), NameForDiagnostics(NameForDiagnostics) { this->AssociatedKind.Opaque = AssociatedKind; @@ -485,17 +490,16 @@ class ContextFreeCodeCompletionResult { /// \note The caller must ensure that the \p CompletionString and all /// \c StringRefs outlive this result, typically by storing them in the same /// \c CodeCompletionResultSink as the result itself. - static ContextFreeCodeCompletionResult * - createDeclResult(CodeCompletionResultSink &Sink, - CodeCompletionString *CompletionString, - const Decl *AssociatedDecl, - bool HasAsyncAlternative, NullTerminatedStringRef ModuleName, - NullTerminatedStringRef BriefDocComment, - ArrayRef AssociatedUSRs, - CodeCompletionResultType ResultType, - ContextFreeNotRecommendedReason NotRecommended, - CodeCompletionDiagnosticSeverity DiagnosticSeverity, - NullTerminatedStringRef DiagnosticMessage); + static ContextFreeCodeCompletionResult *createDeclResult( + CodeCompletionResultSink &Sink, CodeCompletionString *CompletionString, + const Decl *AssociatedDecl, bool HasAsyncAlternative, + NullTerminatedStringRef ModuleName, + NullTerminatedStringRef BriefDocComment, + ArrayRef AssociatedUSRs, + NullTerminatedStringRef SwiftUSR, CodeCompletionResultType ResultType, + ContextFreeNotRecommendedReason NotRecommended, + CodeCompletionDiagnosticSeverity DiagnosticSeverity, + NullTerminatedStringRef DiagnosticMessage); CodeCompletionResultKind getKind() const { return Kind; } @@ -539,6 +543,8 @@ class ContextFreeCodeCompletionResult { return AssociatedUSRs; } + NullTerminatedStringRef getSwiftUSR() const { return SwiftUSR; } + const CodeCompletionResultType &getResultType() const { return ResultType; } ContextFreeNotRecommendedReason getNotRecommendedReason() const { @@ -593,6 +599,10 @@ class ContextFreeCodeCompletionResult { /// the completion's usage context. class CodeCompletionResult { const ContextFreeCodeCompletionResult &ContextFree; + /// Contains the associated declaration if fetched; if not, stores the + /// ASTContext to use for finding the associated declaration through the Swift + /// USR. + mutable llvm::PointerUnion DeclOrCtx; SemanticContextKind SemanticContext : 3; static_assert(int(SemanticContextKind::MAX_VALUE) < 1 << 3, ""); @@ -621,13 +631,15 @@ class CodeCompletionResult { /// done by allocating the two in the same sink or adopting the context free /// sink in the sink that allocates this result. CodeCompletionResult(const ContextFreeCodeCompletionResult &ContextFree, + llvm::PointerUnion DeclOrCtx, SemanticContextKind SemanticContext, CodeCompletionFlair Flair, uint8_t NumBytesToErase, CodeCompletionResultTypeRelation TypeDistance, ContextualNotRecommendedReason NotRecommended) - : ContextFree(ContextFree), SemanticContext(SemanticContext), - Flair(Flair.toRaw()), NotRecommended(NotRecommended), - NumBytesToErase(NumBytesToErase), TypeDistance(TypeDistance) {} + : ContextFree(ContextFree), DeclOrCtx(DeclOrCtx), + SemanticContext(SemanticContext), Flair(Flair.toRaw()), + NotRecommended(NotRecommended), NumBytesToErase(NumBytesToErase), + TypeDistance(TypeDistance) {} const ContextFreeCodeCompletionResult &getContextFreeResult() const { return ContextFree; @@ -715,6 +727,9 @@ class CodeCompletionResult { } } + /// Finds the associated declaration on-demand and caches it. + const Decl *getAssociatedDecl() const; + SemanticContextKind getSemanticContext() const { return SemanticContext; } CodeCompletionFlair getFlair() const { return CodeCompletionFlair(Flair); } @@ -740,6 +755,26 @@ class CodeCompletionResult { return getContextFreeResult().getBriefDocComment(); } + /// Prints the full documentation comment as XML to the provided \p OS stream. + /// + /// \returns true if the result has a documentation comment. + bool printFullDocCommentAsXML(raw_ostream &OS) const { + if (auto *D = getAssociatedDecl()) + return ide::getDocumentationCommentAsXML(D, OS); + + return false; + } + + /// Prints the raw documentation comment to the provided \p OS stream. + /// + /// \returns true if the result has a documentation comment. + bool printRawDocComment(raw_ostream &OS) const { + if (auto *D = getAssociatedDecl()) + return ide::getRawDocumentationComment(D, OS); + + return false; + } + ArrayRef getAssociatedUSRs() const { return getContextFreeResult().getAssociatedUSRs(); } diff --git a/include/swift/IDE/CodeCompletionResultSink.h b/include/swift/IDE/CodeCompletionResultSink.h index 4554698915d52..714d98f9d3e20 100644 --- a/include/swift/IDE/CodeCompletionResultSink.h +++ b/include/swift/IDE/CodeCompletionResultSink.h @@ -41,6 +41,9 @@ struct CodeCompletionResultSink { /// Whether to include an item without any default arguments. bool addCallWithNoDefaultArgs = true; + /// Whether to verify USR to \c Decl reconstruction during completion. + bool verifyUSRToDecl = false; + private: /// Whether the code completion results computed for this sink are intended to /// only be stored in the cache. In this case no contextual information is diff --git a/include/swift/IDE/CodeCompletionString.h b/include/swift/IDE/CodeCompletionString.h index bfb9e4fa08762..0cedbae77cb9e 100644 --- a/include/swift/IDE/CodeCompletionString.h +++ b/include/swift/IDE/CodeCompletionString.h @@ -25,11 +25,11 @@ namespace swift { namespace ide { -class CodeCompletionResultBuilder; +class CodeCompletionStringBuilder; namespace detail { class CodeCompletionStringChunk { - friend class swift::ide::CodeCompletionResultBuilder; + friend class swift::ide::CodeCompletionStringBuilder; public: enum class ChunkKind { @@ -329,7 +329,6 @@ class CodeCompletionStringChunk { class alignas(detail::CodeCompletionStringChunk) CodeCompletionString final : private llvm::TrailingObjects { - friend class CodeCompletionResultBuilder; friend TrailingObjects; public: @@ -348,9 +347,7 @@ class alignas(detail::CodeCompletionStringChunk) CodeCompletionString final static CodeCompletionString *create(llvm::BumpPtrAllocator &Allocator, ArrayRef Chunks); - ArrayRef getChunks() const { - return {getTrailingObjects(), NumChunks}; - } + ArrayRef getChunks() const { return getTrailingObjects(NumChunks); } StringRef getFirstTextChunk(bool includeLeadingPunctuation = false) const; std::optional diff --git a/include/swift/IDE/CodeCompletionStringPrinter.h b/include/swift/IDE/CodeCompletionStringPrinter.h index c592886110764..55d12ae058797 100644 --- a/include/swift/IDE/CodeCompletionStringPrinter.h +++ b/include/swift/IDE/CodeCompletionStringPrinter.h @@ -26,7 +26,7 @@ class CodeCompletionStringPrinter : public ASTPrinter { using ChunkKind = CodeCompletionString::Chunk::ChunkKind; private: - CodeCompletionResultBuilder &Builder; + CodeCompletionStringBuilder &Builder; SmallString<16> Buffer; ChunkKind CurrChunkKind = ChunkKind::Text; ChunkKind NextChunkKind = ChunkKind::Text; @@ -54,7 +54,7 @@ class CodeCompletionStringPrinter : public ASTPrinter { void setNextChunkKind(ChunkKind Kind) { NextChunkKind = Kind; } public: - CodeCompletionStringPrinter(CodeCompletionResultBuilder &Builder) + CodeCompletionStringPrinter(CodeCompletionStringBuilder &Builder) : Builder(Builder) {} ~CodeCompletionStringPrinter() { diff --git a/include/swift/IDE/CompletionLookup.h b/include/swift/IDE/CompletionLookup.h index 29a2f911b1913..1f8877da38206 100644 --- a/include/swift/IDE/CompletionLookup.h +++ b/include/swift/IDE/CompletionLookup.h @@ -24,7 +24,6 @@ #include "swift/ClangImporter/ClangImporter.h" #include "swift/IDE/CodeCompletionContext.h" #include "swift/IDE/CodeCompletionResult.h" -#include "swift/IDE/CodeCompletionStringPrinter.h" #include "swift/IDE/PossibleParamInfo.h" #include "swift/Parse/IDEInspectionCallbacks.h" #include "swift/Sema/IDETypeChecking.h" @@ -33,6 +32,8 @@ namespace swift { namespace ide { +class CodeCompletionResultBuilder; + using DeclFilter = std::function; @@ -325,6 +326,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addImportModuleNames(); + void addUsingSpecifiers(); + SemanticContextKind getSemanticContext(const Decl *D, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo); @@ -352,12 +355,6 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { GenericSignature genericSig = GenericSignature(), bool dynamicOrOptional = false); - /// For printing in code completion results, replace archetypes with - /// protocol compositions. - /// - /// FIXME: Perhaps this should be an option in PrintOptions instead. - Type eraseArchetypes(Type type, GenericSignature genericSig); - Type getTypeOfMember(const ValueDecl *VD, DynamicLookupInfo dynamicLookupInfo); @@ -372,32 +369,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addVarDeclRef(const VarDecl *VD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo); - static bool hasInterestingDefaultValue(const ParamDecl *param); - bool shouldAddItemWithoutDefaultArgs(const AbstractFunctionDecl *func); - /// Build argument patterns for calling. Returns \c true if any content was - /// added to \p Builder. If \p declParams is non-empty, the size must match - /// with \p typeParams. - bool addCallArgumentPatterns(CodeCompletionResultBuilder &Builder, - ArrayRef typeParams, - ArrayRef declParams, - GenericSignature genericSig, - bool includeDefaultArgs = true); - - /// Build argument patterns for calling. Returns \c true if any content was - /// added to \p Builder. If \p Params is non-nullptr, \F - bool addCallArgumentPatterns(CodeCompletionResultBuilder &Builder, - const AnyFunctionType *AFT, - const ParameterList *Params, - GenericSignature genericSig, - bool includeDefaultArgs = true); - - static void addEffectsSpecifiers(CodeCompletionResultBuilder &Builder, - const AnyFunctionType *AFT, - const AbstractFunctionDecl *AFD, - bool forceAsync = false); - void addPoundAvailable(std::optional ParentKind); void addPoundSelector(bool needPound); @@ -458,6 +431,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addEnumElementRef(const EnumElementDecl *EED, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo, bool HasTypeContext); + void addMacroCallArguments(const MacroDecl *MD, DeclVisibilityKind Reason, + bool forTrivialTrailingClosure = false); void addMacroExpansion(const MacroDecl *MD, DeclVisibilityKind Reason); void addKeyword( diff --git a/include/swift/IDE/CompletionOverrideLookup.h b/include/swift/IDE/CompletionOverrideLookup.h index 89641e48b78d9..a547f06e8a7d6 100644 --- a/include/swift/IDE/CompletionOverrideLookup.h +++ b/include/swift/IDE/CompletionOverrideLookup.h @@ -21,6 +21,8 @@ namespace swift { namespace ide { +class CodeCompletionResultBuilder; + class CompletionOverrideLookup : public swift::VisibleDeclConsumer { CodeCompletionResultSink &Sink; ASTContext &Ctx; diff --git a/include/swift/IDE/IDEBridging.h b/include/swift/IDE/IDEBridging.h index dbbed37e48cbb..d1dac6b59eee2 100644 --- a/include/swift/IDE/IDEBridging.h +++ b/include/swift/IDE/IDEBridging.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2022 - 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2022 - 2025 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 @@ -15,8 +15,7 @@ #include "swift/Basic/BasicBridging.h" -#ifdef USED_IN_CPP_SOURCE -#include "swift/Basic/SourceLoc.h" +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE #include "llvm/CAS/CASReference.h" #include #include @@ -66,7 +65,7 @@ enum class LabelRangeType { enum class ResolvedLocContext { Default, Selector, Comment, StringLiteral }; -#ifdef USED_IN_CPP_SOURCE +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE struct ResolvedLoc { /// The range of the call's base name. swift::CharSourceRange range; @@ -105,7 +104,7 @@ struct ResolvedLoc { ResolvedLoc(); }; -#endif // USED_IN_CPP_SOURCE +#endif // #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE /// An opaque, heap-allocated `ResolvedLoc`. /// @@ -118,12 +117,12 @@ struct BridgedResolvedLoc { /// This consumes `labelRanges` by calling `takeUnbridged` on it. SWIFT_NAME( "init(range:labelRanges:firstTrailingLabel:labelType:isActive:context:)") - BridgedResolvedLoc(BridgedCharSourceRange range, + BridgedResolvedLoc(swift::CharSourceRange range, BridgedCharSourceRangeVector labelRanges, unsigned firstTrailingLabel, LabelRangeType labelType, bool isActive, ResolvedLocContext context); -#ifdef USED_IN_CPP_SOURCE +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE ResolvedLoc takeUnbridged() { ResolvedLoc *resolvedLocPtr = static_cast(resolvedLoc); ResolvedLoc unbridged = *resolvedLocPtr; @@ -153,7 +152,7 @@ class BridgedResolvedLocVector { SWIFT_NAME("append(_:)") void push_back(BridgedResolvedLoc Loc); -#ifdef USED_IN_CPP_SOURCE +#ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE std::vector takeUnbridged() { std::vector *vectorPtr = static_cast *>(vector); diff --git a/include/swift/IDE/ModuleInterfacePrinting.h b/include/swift/IDE/ModuleInterfacePrinting.h index fc51d5e287d83..178f7758944bc 100644 --- a/include/swift/IDE/ModuleInterfacePrinting.h +++ b/include/swift/IDE/ModuleInterfacePrinting.h @@ -38,11 +38,14 @@ namespace ide { /// Flags used when traversing a module for printing. enum class ModuleTraversal : unsigned { /// Visit modules even if their contents wouldn't be visible to name lookup. - VisitHidden = 0x01, + VisitHidden = 0x01, /// Visit submodules. VisitSubmodules = 0x02, /// Skip the declarations in a Swift overlay module. - SkipOverlay = 0x04, + SkipOverlay = 0x04, + /// Visit exported modules where their public module name matches the current + /// module. + VisitMatchingExported = 0x08, }; /// Options used to describe the traversal of a module for printing. diff --git a/include/swift/IDE/PostfixCompletion.h b/include/swift/IDE/PostfixCompletion.h index 0e7953b5baf50..b9bb8f62a4239 100644 --- a/include/swift/IDE/PostfixCompletion.h +++ b/include/swift/IDE/PostfixCompletion.h @@ -86,10 +86,6 @@ class PostfixCompletionCallback : public TypeCheckCompletionCallback { PostfixCompletionCallback(CodeCompletionExpr *CompletionExpr, DeclContext *DC) : CompletionExpr(CompletionExpr), DC(DC) {} - /// Typecheck the code completion expression in isolation, calling - /// \c sawSolution for each solution formed. - void fallbackTypeCheck(DeclContext *DC) override; - /// Deliver code completion results that were discoverd by \c sawSolution to /// \p Consumer. /// \param DotLoc If we are completing after a dot, the location of the dot, diff --git a/include/swift/IDE/SignatureHelp.h b/include/swift/IDE/SignatureHelp.h new file mode 100644 index 0000000000000..1eeaedc193062 --- /dev/null +++ b/include/swift/IDE/SignatureHelp.h @@ -0,0 +1,72 @@ +//===--- SignatureHelp.h --- ------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_SIGNATURE_HELP_H +#define SWIFT_IDE_SIGNATURE_HELP_H + +#include "swift/AST/Type.h" +#include "swift/Basic/LLVM.h" +#include "swift/IDE/TypeCheckCompletionCallback.h" + +namespace swift { +class IDEInspectionCallbacksFactory; + +namespace ide { + +struct Signature { + /// True if this is a subscript rather than a function call. + bool IsSubscript; + + /// True if the function is an implicitly curried instance method. + bool IsImplicitlyCurried; + + /// True if the call is the second apply of a double-applied function. + bool IsSecondApply; + + /// The FuncDecl or SubscriptDecl associated with the call. + ValueDecl *FuncD; + + /// The type of the function being called. + AnyFunctionType *FuncTy; + + /// The base type of the call/subscript (null for free functions). + Type BaseType; + + /// The index of the parameter corresponding to the completion argument. + std::optional ParamIdx; +}; + +struct SignatureHelpResult { + /// The decl context of the parsed expression. + DeclContext *DC; + + /// Suggested signatures. + SmallVector Signatures; + + SignatureHelpResult(DeclContext *DC) : DC(DC) {} +}; + +/// An abstract base class for consumers of signatures results. +class SignatureHelpConsumer { +public: + virtual ~SignatureHelpConsumer() {} + virtual void handleResult(const SignatureHelpResult &result) = 0; +}; + +/// Create a factory for code completion callbacks. +IDEInspectionCallbacksFactory * +makeSignatureHelpCallbacksFactory(SignatureHelpConsumer &Consumer); + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_SIGNATURE_HELP_H diff --git a/include/swift/IDE/SignatureHelpFormatter.h b/include/swift/IDE/SignatureHelpFormatter.h new file mode 100644 index 0000000000000..03e3bf43b72a6 --- /dev/null +++ b/include/swift/IDE/SignatureHelpFormatter.h @@ -0,0 +1,85 @@ +//===--- SignatureHelpFormatter.h --- ---------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_SIGNATURE_HELP_FORMATTER_H +#define SWIFT_IDE_SIGNATURE_HELP_FORMATTER_H + +#include "swift/IDE/SignatureHelp.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" + +namespace swift { + +class DeclContext; + +namespace ide { + +class CodeCompletionString; + +struct FormattedSignatureHelp { + struct Parameter { + /// The offset of the parameter text in the signature text. + unsigned Offset; + + /// The length of the parameter text in the signature text. + unsigned Length; + + /// The internal parameter name. + llvm::StringRef Name; + + Parameter() {} + }; + + struct Signature { + llvm::StringRef Text; + llvm::StringRef DocComment; + std::optional ActiveParam; + llvm::ArrayRef Params; + + Signature(llvm::StringRef Text, llvm::StringRef DocComment, + std::optional ActiveParam, + llvm::ArrayRef Params) + : Text(Text), DocComment(DocComment), ActiveParam(ActiveParam), + Params(Params) {} + }; + + llvm::ArrayRef Signatures; + unsigned ActiveSignature; + + FormattedSignatureHelp(llvm::ArrayRef Signatures, + unsigned ActiveSignature) + : Signatures(Signatures), ActiveSignature(ActiveSignature) {} +}; + +class SignatureHelpFormatter { +private: + llvm::BumpPtrAllocator &Allocator; + +public: + SignatureHelpFormatter(llvm::BumpPtrAllocator &Allocator) + : Allocator(Allocator) {} + + FormattedSignatureHelp format(ide::SignatureHelpResult Result); + +private: + FormattedSignatureHelp::Signature + formatSignature(const DeclContext *DC, const ide::Signature &Signature); + + CodeCompletionString *createSignatureString(const ide::Signature &Signature, + const DeclContext *DC); +}; + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_SIGNATURE_HELP_FORMATTER_H diff --git a/include/swift/IDE/SourceEntityWalker.h b/include/swift/IDE/SourceEntityWalker.h index 4a65fd970fb81..6a79486ca084a 100644 --- a/include/swift/IDE/SourceEntityWalker.h +++ b/include/swift/IDE/SourceEntityWalker.h @@ -140,7 +140,7 @@ class SourceEntityWalker { /// refers to. /// \param ExtTyRef this is set when the entity is a reference to a type loc /// in \c ExtensionDecl. - virtual bool visitDeclReference(ValueDecl *D, CharSourceRange Range, + virtual bool visitDeclReference(ValueDecl *D, SourceRange Range, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T, ReferenceMetaData Data); @@ -156,7 +156,7 @@ class SourceEntityWalker { /// \param Data whether this is a read, write or read/write access, etc. /// \param IsOpenBracket this is \c true when the method is called on an /// open bracket. - virtual bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range, + virtual bool visitSubscriptReference(ValueDecl *D, SourceRange Range, ReferenceMetaData Data, bool IsOpenBracket); @@ -167,7 +167,7 @@ class SourceEntityWalker { /// \param D the referenced decl. /// \param Range the source range of the source reference. /// \param Data whether this is a read, write or read/write access, etc. - virtual bool visitCallAsFunctionReference(ValueDecl *D, CharSourceRange Range, + virtual bool visitCallAsFunctionReference(ValueDecl *D, SourceRange Range, ReferenceMetaData Data); /// This method is called for each keyword argument in a call expression. diff --git a/include/swift/IDE/TypeCheckCompletionCallback.h b/include/swift/IDE/TypeCheckCompletionCallback.h index 6bf5e3ad4e481..8432f58afb479 100644 --- a/include/swift/IDE/TypeCheckCompletionCallback.h +++ b/include/swift/IDE/TypeCheckCompletionCallback.h @@ -59,10 +59,6 @@ class TypeCheckCompletionCallback { /// True if at least one solution was passed via the \c sawSolution /// callback. bool gotCallback() const { return GotCallback; } - - /// Typecheck the code completion expression in its outermost expression - /// context, calling \c sawSolution for each solution formed. - virtual void fallbackTypeCheck(DeclContext *DC); }; // MARK: - Utility functions for subclasses of TypeCheckCompletionCallback @@ -90,19 +86,16 @@ struct WithSolutionSpecificVarTypesRAII { WithSolutionSpecificVarTypesRAII( llvm::SmallDenseMap SolutionSpecificVarTypes) { - for (auto SolutionVarType : SolutionSpecificVarTypes) { - if (SolutionVarType.first->hasInterfaceType()) { - RestoreVarTypes[SolutionVarType.first] = - SolutionVarType.first->getInterfaceType(); + for (auto &[var, ty] : SolutionSpecificVarTypes) { + if (var->hasInterfaceType()) { + RestoreVarTypes[var] = var->getInterfaceType(); } else { - RestoreVarTypes[SolutionVarType.first] = Type(); + RestoreVarTypes[var] = Type(); } - if (!SolutionVarType.second->hasArchetype()) { - setInterfaceType(const_cast(SolutionVarType.first), - SolutionVarType.second); + if (!ty->hasArchetype()) { + setInterfaceType(const_cast(var), ty); } else { - setInterfaceType(const_cast(SolutionVarType.first), - ErrorType::get(SolutionVarType.second)); + setInterfaceType(const_cast(var), ErrorType::get(ty)); } } } diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index 343a5ca533f38..e7ff7fbc1f720 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -402,7 +402,8 @@ struct ResolvedRangeInfo { /*Single entry*/true, /*UnhandledEffects*/{}, OrphanKind::None, {}, {}, {}) {} ResolvedRangeInfo(): ResolvedRangeInfo(ArrayRef()) {} - void print(llvm::raw_ostream &OS) const; + void print(llvm::raw_ostream &OS, + const PrintOptions &PO = PrintOptions()) const; ExitState exit() const { return ExitInfo.Exit; } Type getType() const { return ExitInfo.ReturnType; } diff --git a/include/swift/IDETool/CompileInstance.h b/include/swift/IDETool/CompileInstance.h index 4d904568517d1..5e6b7b7dc4f95 100644 --- a/include/swift/IDETool/CompileInstance.h +++ b/include/swift/IDETool/CompileInstance.h @@ -33,7 +33,6 @@ namespace ide { class CompileInstance { const std::string &SwiftExecutablePath; const std::string &RuntimeResourcePath; - const std::string &DiagnosticDocumentationPath; const std::shared_ptr Plugins; struct Options { @@ -70,12 +69,10 @@ class CompileInstance { public: CompileInstance(const std::string &SwiftExecutablePath, const std::string &RuntimeResourcePath, - const std::string &DiagnosticDocumentationPath, std::shared_ptr Plugins = nullptr) : SwiftExecutablePath(SwiftExecutablePath), - RuntimeResourcePath(RuntimeResourcePath), - DiagnosticDocumentationPath(DiagnosticDocumentationPath), - Plugins(Plugins), CachedCIInvalidated(false), CachedReuseCount(0) {} + RuntimeResourcePath(RuntimeResourcePath), Plugins(Plugins), + CachedCIInvalidated(false), CachedReuseCount(0) {} /// NOTE: \p Args is only used for checking the equaity of the invocation. /// Since this function assumes that it is already normalized, exact the same diff --git a/include/swift/IDETool/CompilerInvocation.h b/include/swift/IDETool/CompilerInvocation.h index 4b15f322f9933..c98f886d2e2d5 100644 --- a/include/swift/IDETool/CompilerInvocation.h +++ b/include/swift/IDETool/CompilerInvocation.h @@ -27,8 +27,7 @@ bool initCompilerInvocation( StringRef UnresolvedPrimaryFile, llvm::IntrusiveRefCntPtr FileSystem, const std::string &swiftExecutablePath, - const std::string &runtimeResourcePath, - const std::string &diagnosticDocumentationPath, time_t sessionTimestamp, + const std::string &runtimeResourcePath, time_t sessionTimestamp, std::string &Error); bool initInvocationByClangArguments(ArrayRef ArgList, diff --git a/include/swift/IDETool/IDEInspectionInstance.h b/include/swift/IDETool/IDEInspectionInstance.h index 0f3ab25f7f09b..95afeec267ec0 100644 --- a/include/swift/IDETool/IDEInspectionInstance.h +++ b/include/swift/IDETool/IDEInspectionInstance.h @@ -21,6 +21,7 @@ #include "swift/IDE/ConformingMethodList.h" #include "swift/IDE/CursorInfo.h" #include "swift/IDE/ImportDepth.h" +#include "swift/IDE/SignatureHelp.h" #include "swift/IDE/SwiftCompletionInfo.h" #include "swift/IDE/TypeContextInfo.h" #include "llvm/ADT/Hashing.h" @@ -80,6 +81,14 @@ struct ConformingMethodListResults { bool DidReuseAST; }; +/// The results returned from \c IDEInspectionInstance::signatures. +struct SignatureHelpResults { + /// The actual results. If \c nullptr, no results were found. + const SignatureHelpResult *Result; + /// Whether an AST was reused to produce the results. + bool DidReuseAST; +}; + /// The results returned from \c IDEInspectionInstance::cursorInfo. struct CursorInfoResults { /// The actual results. @@ -206,6 +215,15 @@ class IDEInspectionInstance { llvm::function_ref)> Callback); + void signatureHelp( + swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *ideInspectionTargetBuffer, unsigned int Offset, + DiagnosticConsumer *DiagC, + std::shared_ptr> CancellationFlag, + llvm::function_ref)> + Callback); + void cursorInfo( swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, diff --git a/include/swift/IRGen/GenericRequirement.h b/include/swift/IRGen/GenericRequirement.h index 3722cb2252ede..5d8714fe661be 100644 --- a/include/swift/IRGen/GenericRequirement.h +++ b/include/swift/IRGen/GenericRequirement.h @@ -109,6 +109,11 @@ class GenericRequirement { bool isAnyWitnessTable() const { return kind == Kind::WitnessTable || kind == Kind::WitnessTablePack; } + + bool isAnyPack() const { + return kind == Kind::MetadataPack || kind == Kind::WitnessTablePack; + } + bool isValue() const { return kind == Kind::Value; } diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index 78ac19c0c8817..7570d5ac7c3fd 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -57,15 +57,10 @@ class UniversalLinkageInfo { /// be promoted to public external. Used by the LLDB expression evaluator. bool ForcePublicDecls; - /// When true, allows duplicate external and hidden declarations by marking - /// them as linkonce / weak. - bool MergeableSymbols; - explicit UniversalLinkageInfo(IRGenModule &IGM); UniversalLinkageInfo(const llvm::Triple &triple, bool hasMultipleIGMs, - bool forcePublicDecls, bool isStaticLibrary, - bool mergeableSymbols); + bool forcePublicDecls, bool isStaticLibrary); /// In case of multiple llvm modules (in multi-threaded compilation) all /// private decls must be visible from other files. @@ -132,7 +127,7 @@ class LinkEntity { /// ValueDecl*, SILFunction*, or TypeBase*, depending on Kind. void *Pointer; - /// ProtocolConformance*, depending on Kind. + /// ProtocolConformance* or SILDifferentiabilityWitness*, depending on Kind. void *SecondaryPointer; /// A hand-rolled bitfield with the following layout: @@ -772,8 +767,8 @@ class LinkEntity { void setForDifferentiabilityWitness(Kind kind, const SILDifferentiabilityWitness *witness) { - Pointer = const_cast(static_cast(witness)); - SecondaryPointer = nullptr; + Pointer = nullptr; + SecondaryPointer = const_cast(static_cast(witness)); Data = LINKENTITY_SET_FIELD(Kind, unsigned(kind)); } @@ -1043,7 +1038,7 @@ class LinkEntity { } static LinkEntity forPropertyDescriptor(AbstractStorageDecl *decl) { - assert(decl->exportsPropertyDescriptor()); + assert((bool)decl->getPropertyDescriptorGenericSignature()); LinkEntity entity; entity.setForDecl(Kind::PropertyDescriptor, decl); return entity; @@ -1684,7 +1679,7 @@ class LinkEntity { SILDifferentiabilityWitness *getSILDifferentiabilityWitness() const { assert(getKind() == Kind::DifferentiabilityWitness); - return reinterpret_cast(Pointer); + return reinterpret_cast(SecondaryPointer); } const RootProtocolConformance *getRootProtocolConformance() const { @@ -1850,6 +1845,14 @@ class LinkEntity { bool isTypeKind() const { return isTypeKind(getKind()); } bool isAlwaysSharedLinkage() const; + + /// Whether the link entity's definitions must be considered non-unique. + /// + /// This applies only in the Embedded Swift linkage model, and is used for + /// any symbols that have not been explicitly requested to have unique + /// definitions (e.g., with @_used). + bool hasNonUniqueDefinition() const; + #undef LINKENTITY_GET_FIELD #undef LINKENTITY_SET_FIELD diff --git a/include/swift/Index/IndexRecord.h b/include/swift/Index/IndexRecord.h index c2d21994afe7c..db526df5fd75a 100644 --- a/include/swift/Index/IndexRecord.h +++ b/include/swift/Index/IndexRecord.h @@ -56,7 +56,7 @@ namespace index { bool indexAndRecord(SourceFile *primarySourceFile, StringRef indexUnitToken, StringRef indexStorePath, bool indexClangModules, bool indexSystemModules, bool skipStdlib, - bool includeLocals, bool isDebugCompilation, + bool includeLocals, bool compress, bool isDebugCompilation, bool isExplicitModuleBuild, StringRef targetTriple, const DependencyTracker &dependencyTracker, const PathRemapper &pathRemapper); @@ -98,7 +98,7 @@ bool indexAndRecord(SourceFile *primarySourceFile, StringRef indexUnitToken, bool indexAndRecord(ModuleDecl *module, ArrayRef indexUnitTokens, StringRef moduleUnitToken, StringRef indexStorePath, bool indexClangModules, bool indexSystemModules, - bool skipStdlib, bool includeLocals, + bool skipStdlib, bool includeLocals, bool compress, bool isDebugCompilation, bool isExplicitModuleBuild, StringRef targetTriple, const DependencyTracker &dependencyTracker, diff --git a/include/swift/Markup/AST.h b/include/swift/Markup/AST.h index c48e8fa624989..d1cfbb813053c 100644 --- a/include/swift/Markup/AST.h +++ b/include/swift/Markup/AST.h @@ -112,11 +112,11 @@ class Document final : public MarkupASTNode, ArrayRef Children); ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -136,11 +136,11 @@ class BlockQuote final : public MarkupASTNode, static BlockQuote *create(MarkupContext &MC, ArrayRef Children); ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -162,17 +162,16 @@ class List final : public MarkupASTNode, bool IsOrdered); ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } void setChildren(ArrayRef NewChildren) { assert(NewChildren.size() <= NumChildren); - std::copy(NewChildren.begin(), NewChildren.end(), - getTrailingObjects()); + std::copy(NewChildren.begin(), NewChildren.end(), getTrailingObjects()); NumChildren = NewChildren.size(); } @@ -197,11 +196,11 @@ class Item final : public MarkupASTNode, static Item *create(MarkupContext &MC, ArrayRef Children); ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -265,11 +264,11 @@ class Paragraph final : public MarkupASTNode, ArrayRef Children); ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -291,11 +290,11 @@ class Header final : public MarkupASTNode, ArrayRef Children); ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } unsigned getLevel() const { @@ -482,11 +481,11 @@ class Emphasis final : public InlineContent, ArrayRef Children); ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -506,11 +505,11 @@ class Strong final : public InlineContent, ArrayRef Children); ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -536,11 +535,11 @@ class Link final : public InlineContent, StringRef getDestination() const { return Destination; } ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -577,11 +576,11 @@ class Image final : public InlineContent, } ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -607,11 +606,11 @@ class InlineAttributes final : public InlineContent, private llvm::TrailingObjec StringRef getAttributes() const { return Attributes; } ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -678,11 +677,11 @@ class ParamField final : public PrivateExtension, } ArrayRef getChildren() { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } ArrayRef getChildren() const { - return {getTrailingObjects(), NumChildren}; + return getTrailingObjects(NumChildren); } static bool classof(const MarkupASTNode *N) { @@ -703,11 +702,11 @@ public: \ static Id *create(MarkupContext &MC, ArrayRef Children); \ \ ArrayRef getChildren() { \ - return {getTrailingObjects(), NumChildren}; \ + return getTrailingObjects(NumChildren); \ } \ \ ArrayRef getChildren() const { \ - return {getTrailingObjects(), NumChildren}; \ + return getTrailingObjects(NumChildren); \ } \ \ static bool classof(const MarkupASTNode *N) { \ diff --git a/include/swift/Migrator/FixitApplyDiagnosticConsumer.h b/include/swift/Migrator/FixitApplyDiagnosticConsumer.h index 4937a881b827d..ce5aebf58f5e9 100644 --- a/include/swift/Migrator/FixitApplyDiagnosticConsumer.h +++ b/include/swift/Migrator/FixitApplyDiagnosticConsumer.h @@ -21,7 +21,7 @@ #include "swift/Migrator/FixitFilter.h" #include "swift/Migrator/Migrator.h" #include "swift/Migrator/Replacement.h" -#include "clang/Rewrite/Core/RewriteBuffer.h" +#include "llvm/ADT/RewriteBuffer.h" #include "llvm/ADT/SmallSet.h" namespace swift { @@ -37,7 +37,7 @@ struct Replacement; class FixitApplyDiagnosticConsumer final : public DiagnosticConsumer, public FixitFilter { - clang::RewriteBuffer RewriteBuf; + llvm::RewriteBuffer RewriteBuf; /// The entire text of the input file. const StringRef Text; diff --git a/include/swift/Migrator/RewriteBufferEditsReceiver.h b/include/swift/Migrator/RewriteBufferEditsReceiver.h index 91c86d893bc6d..e9366ead711f5 100644 --- a/include/swift/Migrator/RewriteBufferEditsReceiver.h +++ b/include/swift/Migrator/RewriteBufferEditsReceiver.h @@ -16,7 +16,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceLocation.h" #include "clang/Edit/EditsReceiver.h" -#include "clang/Rewrite/Core/RewriteBuffer.h" +#include "llvm/ADT/RewriteBuffer.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" @@ -26,12 +26,13 @@ namespace swift { namespace migrator { /// An EditsReceiver that collects edits from an EditedSource and directly -/// applies it to a clang::RewriteBuffer. +/// applies it to a llvm::RewriteBuffer. class RewriteBufferEditsReceiver final : public clang::edit::EditsReceiver { const clang::SourceManager &ClangSourceManager; const clang::FileID InputFileID; const StringRef InputText; - clang::RewriteBuffer RewriteBuf; + llvm::RewriteBuffer RewriteBuf; + public: RewriteBufferEditsReceiver(const clang::SourceManager &ClangSourceManager, const clang::FileID InputFileID, diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 40e071a8facf5..b72721ee4e3ca 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -165,6 +165,8 @@ def verify_apply_fixes : Flag<["-"], "verify-apply-fixes">, HelpText<"Like -verify, but updates the original source file">; def verify_ignore_unknown: Flag<["-"], "verify-ignore-unknown">, HelpText<"Allow diagnostics for '' location in verify mode">; +def verify_ignore_unrelated: Flag<["-"], "verify-ignore-unrelated">, + HelpText<"Allow diagnostics in files outside those with expected diagnostics in verify mode">; def verify_generic_signatures : Separate<["-"], "verify-generic-signatures">, MetaVarName<"">, HelpText<"Verify the generic signatures in the given module">; @@ -244,6 +246,9 @@ def module_can_import_version: MultiArg<["-"], "module-can-import-version", 3>, MetaVarName<" ">, HelpText<"Specify canImport module and versions">; +def module_import_from_cas: Flag<["-"], "module-import-from-cas">, + HelpText<"Import modules from CAS instead of file system">; + def disable_cross_import_overlay_search: Flag<["-"], "disable-cross-import-overlay-search">, HelpText<"Disable searching for cross import overlay file">; @@ -255,10 +260,6 @@ def const_gather_protocols_file : Separate<["-"], "const-gather-protocols-file">, MetaVarName<"">, HelpText<"Specify a list of protocols for extraction of conformances' const values'">; -def placeholder_dependency_module_map - : Separate<["-"], "placeholder-dependency-module-map-file">, MetaVarName<"">, - HelpText<"Specify a JSON file containing information of external Swift module dependencies">; - def import_prescan : Flag<["-"], "import-prescan">, HelpText<"When performing a dependency scan, only identify all imports of the main Swift module sources">; @@ -283,12 +284,12 @@ def no_parallel_scan : Flag<["-"], "no-parallel-scan">, HelpText<"Perform dependency scanning in a single-threaded fashion.">; def enable_copy_propagation : Flag<["-"], "enable-copy-propagation">, - HelpText<"Run SIL copy propagation with lexical lifetimes to shorten object " + HelpText<"Always run SIL copy propagation with lexical lifetimes to shorten object " "lifetimes while preserving variable lifetimes.">; def copy_propagation_state_EQ : Joined<["-"], "enable-copy-propagation=">, HelpText<"Whether to enable copy propagation">, - MetaVarName<"true|requested-passes-only|false">; + MetaVarName<"always|optimizing|requested-passes-only|false">; def disable_infer_public_concurrent_value : Flag<["-"], "disable-infer-public-sendable">, HelpText<"Disable inference of Sendable conformances for public structs and enums">; @@ -319,6 +320,12 @@ let Flags = [FrontendOption, NoDriverOption, HelpHidden, ModuleInterfaceOptionIg Joined<["-"], "formal-cxx-interoperability-mode=">, HelpText<"What version of C++ interoperability a textual interface was originally generated with">, MetaVarName<"|off">; + def enable_assembly_vision_all + : Flag<["-"], "enable-assembly-vision-all">, + HelpText<"Enable assembly vision for all functions">; + def disable_assembly_vision_all + : Flag<["-"], "disable-assembly-vision-all">, + HelpText<"Disable assembly vision for all functions">; } // Flags that are saved into module interfaces @@ -388,10 +395,6 @@ def public_autolink_library : // HIDDEN FLAGS let Flags = [FrontendOption, NoDriverOption, HelpHidden] in { -def enable_experimental_swift_based_closure_specialization : - Flag<["-"], "experimental-swift-based-closure-specialization">, - HelpText<"Use the experimental Swift based closure-specialization optimization pass instead of the existing C++ one">; - def checked_async_objc_bridging : Joined<["-"], "checked-async-objc-bridging=">, HelpText<"Control whether checked continuations are used when bridging " "async calls from Swift to ObjC: 'on', 'off' ">; @@ -428,6 +431,9 @@ def dump_macro_expansions : Flag<["-"], "dump-macro-expansions">, def emit_macro_expansion_files : Separate<["-"], "emit-macro-expansion-files">, HelpText<"Specify when to emit macro expansion file: 'none', 'debug', or 'diagnostics'">; +def dump_source_file_imports : Flag<["-"], "dump-source-file-imports">, + HelpText<"Dumps the list of imports for each source file">; + def analyze_request_evaluator : Flag<["-"], "analyze-request-evaluator">, Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, HelpText<"Print out request evaluator cache statistics at the end of the compilation job">; @@ -448,6 +454,14 @@ def requirement_machine_max_concrete_nesting : Joined<["-"], "requirement-machin Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, HelpText<"Set the maximum concrete type nesting depth before giving up">; +def requirement_machine_max_concrete_size : Joined<["-"], "requirement-machine-max-concrete-size=">, + Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, + HelpText<"Set the maximum concrete type total size before giving up">; + +def requirement_machine_max_type_differences : Joined<["-"], "requirement-machine-max-type-differences=">, + Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, + HelpText<"Set the maximum number of type difference structures allocated before giving up">; + def requirement_machine_max_split_concrete_equiv_class_attempts : Joined<["-"], "requirement-machine-max-split-concrete-equiv-class-attempts=">, Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, HelpText<"Set the maximum concrete number of attempts at splitting " @@ -467,6 +481,14 @@ def disable_requirement_machine_reuse : Flag<["-"], "disable-requirement-machine def enable_requirement_machine_opaque_archetypes : Flag<["-"], "enable-requirement-machine-opaque-archetypes">, HelpText<"Enable more correct opaque archetype support, which is off by default because it might fail to produce a convergent rewrite system">; +def max_substitution_depth : Joined<["-"], "max-substitution-depth=">, + Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, + HelpText<"Set the maximum nesting depth for type substitution operations">; + +def max_substitution_count : Joined<["-"], "max-substitution-count=">, + Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, + HelpText<"Set the maximum step count for type substitution operations">; + def dump_type_witness_systems : Flag<["-"], "dump-type-witness-systems">, HelpText<"Enables dumping type witness systems from associated type inference">; @@ -519,6 +541,10 @@ def disable_clangimporter_source_import : Flag<["-"], "disable-clangimporter-source-import">, HelpText<"Disable ClangImporter and forward all requests straight the DWARF importer.">; +def disable_const_value_importing : Flag<["-"], + "disable-const-value-importing">, + HelpText<"Disable constant value importing in ClangImporter.">; + def disable_implicit_concurrency_module_import : Flag<["-"], "disable-implicit-concurrency-module-import">, HelpText<"Disable the implicit import of the _Concurrency module.">; @@ -654,6 +680,9 @@ def dump_clang_lookup_tables : Flag<["-"], "dump-clang-lookup-tables">, HelpText<"Dump the importer's Swift-name-to-Clang-name lookup tables to " "stderr">; +def dump_availability_scopes : Flag<["-"], "dump-availability-scopes">, + HelpText<"Dump availability scopes to stderr">; + def disable_modules_validate_system_headers : Flag<["-"], "disable-modules-validate-system-headers">, HelpText<"Disable validating system headers in the Clang importer">; @@ -666,6 +695,9 @@ def emit_pch : Flag<["-"], "emit-pch">, def pch_disable_validation : Flag<["-"], "pch-disable-validation">, HelpText<"Disable validating the persistent PCH">; +def version_independent_apinotes : Flag<["-"], "version-independent-apinotes">, + HelpText<"Input clang modules carry all versioned APINotes">; + def disable_sil_ownership_verifier : Flag<["-"], "disable-sil-ownership-verifier">, HelpText<"Do not verify ownership invariants during SIL Verification ">; @@ -840,6 +872,9 @@ def downgrade_typecheck_interface_error : Flag<["-"], "downgrade-typecheck-inter def enable_volatile_modules : Flag<["-"], "enable-volatile-modules">, HelpText<"Load Swift modules in memory">; +def solver_memory_threshold_EQ : Joined<["-"], "solver-memory-threshold=">, + HelpText<"Set the upper bound for memory consumption, in bytes, by the constraint solver">; + def solver_expression_time_threshold_EQ : Joined<["-"], "solver-expression-time-threshold=">, HelpText<"Expression type checking timeout, in seconds">; @@ -849,15 +884,17 @@ def solver_scope_threshold_EQ : Joined<["-"], "solver-scope-threshold=">, def solver_trail_threshold_EQ : Joined<["-"], "solver-trail-threshold=">, HelpText<"Expression type checking trail change limit">; -def solver_disable_shrink : - Flag<["-"], "solver-disable-shrink">, - HelpText<"Disable the shrink phase of expression type checking">; +def solver_disable_prepared_overloads : Flag<["-"], "solver-disable-prepared-overloads">, + HelpText<"Disable experimental prepared overloads optimization">; + +def solver_enable_prepared_overloads : Flag<["-"], "solver-enable-prepared-overloads">, + HelpText<"Enable experimental prepared overloads optimization">; def solver_disable_splitter : Flag<["-"], "solver-disable-splitter">, HelpText<"Disable the component splitter phase of expression type checking">; -def disable_constraint_solver_performance_hacks : Flag<["-"], "disable-constraint-solver-performance-hacks">, - HelpText<"Disable all the hacks in the constraint solver">; +def enable_constraint_solver_performance_hacks : Flag<["-"], "enable-constraint-solver-performance-hacks">, + HelpText<"Enable all the old hacks in the constraint solver">; def enable_operator_designated_types : Flag<["-"], "enable-operator-designated-types">, @@ -874,6 +911,12 @@ def disable_invalid_ephemeralness_as_error : def switch_checking_invocation_threshold_EQ : Joined<["-"], "switch-checking-invocation-threshold=">; +def dynamic_member_lookup_depth_limit_EQ + : Joined<["-"], "dynamic-member-lookup-depth-limit=">, + HelpText< + "The maximum number of dynamic member lookups that can be chained " + "to resolve a member reference">; + def enable_new_operator_lookup : Flag<["-"], "enable-new-operator-lookup">, HelpText<"Enable the new operator decl and precedencegroup lookup behavior">; @@ -1175,12 +1218,22 @@ def enable_arm64_corocc : Flag<["-"], "enable-arm64-corocc">, def disable_arm64_corocc : Flag<["-"], "disable-arm64-corocc">, HelpText<"Don't use swiftcorocc for yield_once_2 routines on arm64 variants.">; +def enable_callee_allocated_coro_abi : Flag<["-"], "enable-callee-allocated-coro-abi">, + HelpText<"Override per-platform settings and use yield_once_2.">; + +def disable_callee_allocated_coro_abi : Flag<["-"], "disable-callee-allocated-coro-abi">, + HelpText<"Override per-platform settings and don't use yield_once_2.">; + def enable_cond_fail_message_annotation : Flag<["-"], "enable-cond-fail-message-annotation">, HelpText<"Enable cond_fail message annotation. Will serialize a .o.yaml file per .o file.">; def disable_cond_fail_message_annotation : Flag<["-"], "dissable-cond-fail-message-annotation">, HelpText<"Disable cond_fail message annotation.">; +def min_valid_pointer_value : Joined<["-"], "min-valid-pointer-value=">, + MetaVarName<"">, + HelpText<"Overrides the target's least valid pointer value.'">; + let Flags = [FrontendOption, NoDriverOption, HelpHidden, ModuleInterfaceOptionIgnorable] in { def enable_pack_metadata_stack_promotion : Joined<["-"], "enable-pack-metadata-stack-promotion=">, @@ -1265,8 +1318,7 @@ def disable_ast_verifier : Flag<["-"], "disable-ast-verifier">, let Flags = [FrontendOption, NoDriverOption, HelpHidden, ModuleInterfaceOptionIgnorable] in { def enable_ossa_modules : Flag<["-"], "enable-ossa-modules">, - HelpText<"Always serialize SIL in ossa form. If this flag is not passed in, " - "when optimizing ownership will be lowered before serializing SIL">; + HelpText<"Obsolete. This option is ignored">; } def enable_recompilation_to_ossa_module : Flag<["-"], "enable-recompilation-to-ossa-module">, @@ -1448,9 +1500,6 @@ def bridging_header_pch_key : Separate<["-"], "bridging-header-pch-key">, def no_clang_include_tree: Flag<["-"], "no-clang-include-tree">, HelpText<"Do not use clang include tree, fallback to use CAS filesystem to build clang modules">; -def cas_fs: Separate<["-"], "cas-fs">, - HelpText<"Root CASID for CAS FileSystem">, MetaVarName<"">; - def clang_include_tree_root: Separate<["-"], "clang-include-tree-root">, HelpText<"Clang Include Tree CASID">, MetaVarName<"">; def clang_include_tree_filelist: Separate<["-"], "clang-include-tree-filelist">, @@ -1485,7 +1534,7 @@ def platform_c_calling_convention_EQ : def disable_sending_args_and_results_with_region_isolation : Flag<["-"], "disable-sending-args-and-results-with-region-based-isolation">, - HelpText<"Disable sending args and results when region based isolation is enabled. Only enabled with asserts">, + HelpText<"Disable sending args and results when region-based isolation is enabled. Only enabled with asserts">, Flags<[HelpHidden]>; def scanner_module_validation: Flag<["-"], "scanner-module-validation">, @@ -1512,6 +1561,12 @@ def enable_address_dependencies : Flag<["-"], "enable-address-dependencies">, def disable_address_dependencies : Flag<["-"], "disable-address-dependencies">, HelpText<"Disable enforcement of lifetime dependencies on addressable values.">; +def gen_reproducer : Flag<["-"], "gen-reproducer">, + HelpText<"Generate a reproducer for current compilation.">; +def gen_reproducer_dir + : Separate<["-"], "gen-reproducer-dir">, + HelpText<"Path to directory where reproducers write to.">; + } // end let Flags = [FrontendOption, NoDriverOption, HelpHidden] def disable_experimental_parser_round_trip : Flag<["-"], diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 664572632985d..99f714fd22e4d 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -33,7 +33,7 @@ def ModuleWrapOption : OptionFlag; def NoDriverOption : OptionFlag; // Some options should not be available depending on whether this is the -// interactive driver 'swift', or the batch compiler 'swiftc'. +// interactive driver 'swift', or the aot compiler 'swiftc'. def NoInteractiveOption : OptionFlag; def NoBatchOption : OptionFlag; @@ -298,6 +298,13 @@ def language_mode : Separate<["-"], "language-mode">, MetaVarName<"">, Alias; +def min_swift_runtime_version + : Separate<["-"], "min-swift-runtime-version">, + Flags<[FrontendOption, ModuleInterfaceOptionIgnorable]>, + HelpText<"The minimum Swift runtime version " + "that will be available at runtime">, + MetaVarName<"">; + def package_description_version: Separate<["-"], "package-description-version">, Flags<[FrontendOption, HelpHidden, ModuleInterfaceOption]>, HelpText<"The version number to be applied on the input for the PackageDescription availability kind">, @@ -328,7 +335,7 @@ def F_EQ : Joined<["-"], "F=">, Flags<[FrontendOption, ArgumentIsPath]>, def Fsystem : Separate<["-"], "Fsystem">, Flags<[FrontendOption, ArgumentIsPath, SwiftSymbolGraphExtractOption, - SwiftSynthesizeInterfaceOption]>, + SwiftAPIDigesterOption, SwiftSynthesizeInterfaceOption]>, HelpText<"Add directory to system framework search path">; def I : JoinedOrSeparate<["-"], "I">, @@ -340,23 +347,32 @@ def I_EQ : Joined<["-"], "I=">, Flags<[FrontendOption, ArgumentIsPath]>, def Isystem : Separate<["-"], "Isystem">, Flags<[FrontendOption, ArgumentIsPath, SwiftSymbolGraphExtractOption, - SwiftSynthesizeInterfaceOption]>, + SwiftAPIDigesterOption, SwiftSynthesizeInterfaceOption]>, HelpText<"Add directory to the system import search path">; def import_underlying_module : Flag<["-"], "import-underlying-module">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Implicitly imports the Objective-C half of a module">; -def import_objc_header : Separate<["-"], "import-objc-header">, - Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, - HelpText<"Implicitly imports an Objective-C header file">; def import_bridging_header : Separate<["-"], "import-bridging-header">, + Flags<[FrontendOption, ArgumentIsPath]>, + HelpText<"Implicitly imports a C header file">; +def import_objc_header : Separate<["-"], "import-objc-header">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, - Alias; + Alias; + +def internal_import_bridging_header : Separate<["-"], "internal-import-bridging-header">, + Flags<[FrontendOption, ArgumentIsPath]>, + HelpText<"Implicitly imports a C header file as an internal import">; + def import_pch : Separate<["-"], "import-pch">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, HelpText<"Import bridging header PCH file">; +def internal_import_pch : Separate<["-"], "internal-import-pch">, + Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, + HelpText<"Import bridging header PCH file as internal">; + def pch_output_dir: Separate<["-"], "pch-output-dir">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, HelpText<"Directory to persist automatically created precompiled bridging headers">; @@ -468,10 +484,6 @@ def remark_module_serialization : Flag<["-"], "Rmodule-serialization">, Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, HelpText<"Emit remarks about module serialization">; -def remark_abi_inference : Flag<["-"], "Rabi-inference">, - Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, - HelpText<"Emit a remark when an '@abi' attribute adds an attribute or modifier to the ABI declaration based on its presence in the API">; - def emit_tbd : Flag<["-"], "emit-tbd">, HelpText<"Emit a TBD file">, Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>; @@ -580,6 +592,14 @@ def define_enabled_availability_domain : Separate<["-"], "define-enabled-availab HelpText<"Defines a custom availability domain that is available at compile time">, MetaVarName<"">; +def define_always_enabled_availability_domain + : Separate<["-"], "define-always-enabled-availability-domain">, + Flags<[HelpHidden, FrontendOption, NoInteractiveOption, + ModuleInterfaceOptionIgnorable]>, + HelpText<"Defines a custom availability domain that is available for all " + "deployments">, + MetaVarName<"">; + def define_disabled_availability_domain : Separate<["-"], "define-disabled-availability-domain">, Flags<[HelpHidden, FrontendOption, NoInteractiveOption, ModuleInterfaceOptionIgnorable]>, HelpText<"Defines a custom availability domain that is unavailable at compile time">, @@ -803,10 +823,6 @@ def import_cf_types : Flag<["-"], "import-cf-types">, Flags<[FrontendOption, HelpHidden]>, HelpText<"Recognize and import CF types as class types">; -def solver_memory_threshold : Separate<["-"], "solver-memory-threshold">, - Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, - HelpText<"Set the upper bound for memory consumption, in bytes, by the constraint solver">; - def solver_shrink_unsolved_threshold : Separate<["-"], "solver-shrink-unsolved-threshold">, Flags<[FrontendOption, HelpHidden, DoesNotAffectIncrementalBuild]>, HelpText<"Set The upper bound to number of sub-expressions unsolved before termination of the shrink phrase">; @@ -875,7 +891,7 @@ def cxx_interoperability_mode : def experimental_c_foreign_reference_types : Flag<["-"], "experimental-c-foreign-reference-types">, Flags<[FrontendOption, HelpHidden, ModuleInterfaceOption]>, - HelpText<"Enable experimental C foreign references types (with reference counting).">; + HelpText<"Enable experimental C foreign references types. Deprecated, has no effect.">; def experimental_hermetic_seal_at_link: Flag<["-"], "experimental-hermetic-seal-at-link">, @@ -890,7 +906,7 @@ def experimental_package_interface_load: def experimental_serialize_debug_info: Flag<["-"], "experimental-serialize-debug-info">, Flags<[FrontendOption, HelpHidden]>, - HelpText<"Enables seriailzation/deserialization of debug scopes">; + HelpText<"Enables serialization/deserialization of debug scopes">; // Diagnostic control options def suppress_warnings : Flag<["-"], "suppress-warnings">, Flags<[FrontendOption]>, @@ -920,6 +936,10 @@ def Wwarning : Separate<["-"], "Wwarning">, MetaVarName<"">, HelpText<"Treat this warning group as warning">; +def suppress_notes : Flag<["-"], "suppress-notes">, + Flags<[FrontendOption]>, + HelpText<"Suppress all notes">; + def suppress_remarks : Flag<["-"], "suppress-remarks">, Flags<[FrontendOption]>, HelpText<"Suppress all remarks">; @@ -998,16 +1018,6 @@ def default_isolation_EQ : Joined<["-"], "default-isolation=">, Flags<[FrontendOption]>, Alias; -def executor_factory : JoinedOrSeparate<["-"], "executor-factory">, - Flags<[FrontendOption]>, - HelpText<"Specify the factory to use to create the default executors for " - "Swift Concurrency. This must be a type conforming to the " - "'ExecutorFactory' protocol.">, - MetaVarName<"">; -def executor_factory_EQ : Joined<["-"], "executor-factory=">, - Flags<[FrontendOption]>, - Alias; - def enable_experimental_feature : Separate<["-"], "enable-experimental-feature">, Flags<[FrontendOption, ModuleInterfaceOption]>, @@ -1032,6 +1042,10 @@ def strict_memory_safety : Flag<["-"], "strict-memory-safety">, Flags<[FrontendOption, ModuleInterfaceOptionIgnorable, SwiftAPIDigesterOption, SwiftSynthesizeInterfaceOption]>, HelpText<"Enable strict memory safety checking">; +def strict_memory_safety_migrate : Flag<["-"], "strict-memory-safety:migrate">, + Flags<[FrontendOption, ModuleInterfaceOptionIgnorable, + SwiftAPIDigesterOption, SwiftSynthesizeInterfaceOption]>, + HelpText<"Enable migration to strict memory safety checking">; def Rpass_EQ : Joined<["-"], "Rpass=">, Flags<[FrontendOption]>, @@ -1051,7 +1065,7 @@ def save_optimization_record_EQ : Joined<["-"], "save-optimization-record=">, "(default: YAML)">, MetaVarName<"">; def save_optimization_record_path : Separate<["-"], "save-optimization-record-path">, - Flags<[FrontendOption, ArgumentIsPath]>, + Flags<[FrontendOption, ArgumentIsPath, CacheInvariant]>, HelpText<"Specify the file name of any generated optimization record">; def save_optimization_record_passes : Separate<["-"], "save-optimization-record-passes">, @@ -1177,7 +1191,7 @@ def ExperimentalPackageCMO : Flag<["-"], "experimental-package-cmo">, def PackageCMO : Flag<["-"], "package-cmo">, Flags<[FrontendOption]>, - HelpText<"Enable optimization to perform defalut CMO within a package boundary">; + HelpText<"Enable optimization to perform default CMO within a package boundary">; def EnableDefaultCMO : Flag<["-"], "enable-default-cmo">, Flags<[HelpHidden, FrontendOption]>, @@ -1449,11 +1463,7 @@ def dump_scope_maps : Separate<["-"], "dump-scope-maps">, MetaVarName<"">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; -def dump_availability_scopes : - Flag<["-"], "dump-availability-scopes">, - HelpText<"Type-check input file(s) and dump availability scopes">, - ModeOpt, - Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; + def dump_type_info : Flag<["-"], "dump-type-info">, HelpText<"Output YAML dump of fixed-size types from all imported modules">, ModeOpt, @@ -1543,6 +1553,13 @@ def target_legacy_spelling : Joined<["--"], "target=">, def print_target_info : Flag<["-"], "print-target-info">, Flags<[FrontendOption]>, HelpText<"Print target information for the given target , such as x86_64-apple-macos10.9">, MetaVarName<"">; +def print_static_build_config : Flag<["-"], "print-static-build-config">, + Flags<[FrontendOption]>, + HelpText<"Print static build configuration that can be used to evaluate #ifs in Swift source code">; + +def print_supported_features : Flag<["-"], "print-supported-features">, + Flags<[FrontendOption]>, + HelpText<"Print information about features supported by the compiler">; def target_cpu : Separate<["-"], "target-cpu">, Flags<[FrontendOption, ModuleInterfaceOption]>, HelpText<"Generate code for a particular CPU variant">; @@ -1577,6 +1594,10 @@ def explain_module_dependency_detailed : Separate<["-"], "explain-module-depende def explicit_auto_linking : Flag<["-"], "explicit-auto-linking">, Flags<[NewDriverOnlyOption]>, HelpText<"Instead of linker-load directives, have the driver specify all link dependencies on the linker invocation. Requires '-explicit-module-build'.">; + +def experimental_emit_variant_module : Flag<["-"], "experimental-emit-variant-module">, + Flags<[NewDriverOnlyOption]>, + HelpText<"When a target variant triple is specified, the same driver invocation will emit two Swift modules, one for the primary target and one for the variant.">; def min_inlining_target_version : Separate<["-"], "target-min-inlining-version">, Flags<[FrontendOption, ModuleInterfaceOption]>, @@ -1660,9 +1681,13 @@ def scan_dependencies : Flag<["-"], "scan-dependencies">, HelpText<"Scan dependencies of the given Swift sources">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; -def emit_supported_features : Flag<["-"], "emit-supported-features">, - HelpText<"Emit a JSON file including all supported compiler features">, ModeOpt, +def emit_supported_arguments : Flag<["-"], "emit-supported-arguments">, + HelpText<"Emit a JSON file including all supported compiler arguments">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; +def emit_supported_features : Flag<["-"], "emit-supported-features">, + HelpText<"This is a compatibility alias for '-emit-supported-arguments'">, ModeOpt, + Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild, HelpHidden]>, + Alias; def enable_incremental_imports : Flag<["-"], "enable-incremental-imports">, @@ -1676,6 +1701,18 @@ def disable_incremental_imports : HelpText<"Disable cross-module incremental build metadata and " "driver scheduling for Swift modules">; +def enable_incremental_file_hashing : + Flag<["-"], "enable-incremental-file-hashing">, + Flags<[NewDriverOnlyOption]>, + HelpText<"Enable hashing of input and dependency file data " + "that can prevent unnecessary invalidation">; + +def disable_incremental_file_hashing : + Flag<["-"], "disable-incremental-file-hashing">, + Flags<[NewDriverOnlyOption]>, + HelpText<"Disable hashing of input and dependency file data " + "that can prevent unnecessary invalidation">; + def index_file : Flag<["-"], "index-file">, HelpText<"Produce index data for a source file">, ModeOpt, Flags<[NoInteractiveOption, DoesNotAffectIncrementalBuild]>; @@ -1687,6 +1724,10 @@ def index_file_path : Separate<["-"], "index-file-path">, def index_store_path : Separate<["-"], "index-store-path">, Flags<[FrontendOption, ArgumentIsPath, CacheInvariant]>, MetaVarName<"">, HelpText<"Store indexing data to ">; + +def index_store_compress : Flag<["-"], "index-store-compress">, + Flags<[FrontendOption]>, + HelpText<"Compress the unit and record files in the index store">; def index_unit_output_path : Separate<["-"], "index-unit-output-path">, Flags<[FrontendOption, ArgumentIsPath, CacheInvariant]>, MetaVarName<"">, @@ -1769,11 +1810,34 @@ def emit_symbol_graph_dir : Separate<["-"], "emit-symbol-graph-dir">, HelpText<"Emit a symbol graph to directory ">, MetaVarName<"">; +def symbol_graph_pretty_print: Flag<["-"], "symbol-graph-pretty-print">, + Flags<[FrontendOption, NoInteractiveOption, HelpHidden, SupplementaryOutput]>, + HelpText<"Pretty-print the output symbol graph JSON">; + +def symbol_graph_skip_inherited_docs: Flag<["-"], "symbol-graph-skip-inherited-docs">, + Flags<[FrontendOption, NoInteractiveOption, HelpHidden, SupplementaryOutput]>, + HelpText<"Skip emitting doc comments for members inherited through classes or " + "default implementations">; + +def symbol_graph_skip_synthesized_members: Flag<["-"], "symbol-graph-skip-synthesized-members">, + Flags<[FrontendOption, NoInteractiveOption, HelpHidden, SupplementaryOutput]>, + HelpText<"Skip members inherited through classes or default implementations">; + def symbol_graph_minimum_access_level: Separate<["-"], "symbol-graph-minimum-access-level">, Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput, HelpHidden]>, HelpText<"Include symbols with this access level or more when emitting a symbol graph">, MetaVarName<"">; +def symbol_graph_allow_availability_platforms: Separate<["-"], "symbol-graph-allow-availability-platforms">, + Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput, HelpHidden]>, + HelpText<"Restrict availability metadata to the given platforms, e.g. 'macOS,Swift'">, + MetaVarName<"">; + +def symbol_graph_block_availability_platforms: Separate<["-"], "symbol-graph-block-availability-platforms">, + Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput, HelpHidden]>, + HelpText<"Remove the given platforms from symbol graph availability metadata, e.g. 'macOS,Swift'">, + MetaVarName<"">; + def pretty_print: Flag<["-"], "pretty-print">, Flags<[SwiftSymbolGraphExtractOption]>, HelpText<"Pretty-print the output JSON">; @@ -1788,6 +1852,16 @@ def omit_extension_block_symbols: Flag<["-"], "omit-extension-block-symbols">, NoInteractiveOption, SupplementaryOutput, HelpHidden]>, HelpText<"Directly associate members and conformances with the extended nominal when generating symbol graphs instead of emitting 'swift.extension' symbols for extensions to external types">; +def allow_availability_platforms: Separate<["-"], "allow-availability-platforms">, + Flags<[SwiftSymbolGraphExtractOption]>, + HelpText<"Restrict availability metadata to the given platforms, e.g. 'macOS,Swift'">, + MetaVarName<"">; + +def block_availability_platforms: Separate<["-"], "block-availability-platforms">, + Flags<[SwiftSymbolGraphExtractOption]>, + HelpText<"Remove the given platforms from symbol graph availability metadata, e.g. 'macOS,Swift'">, + MetaVarName<"">; + // swift-synthesize-interface-only options def include_submodules : Flag<["-"], "include-submodules">, Flags<[NoDriverOption, SwiftSynthesizeInterfaceOption]>, @@ -2077,6 +2151,13 @@ def emit_module_serialize_diagnostics_path: "">, MetaVarName<"">; +def dependency_scan_serialize_diagnostics_path: + Separate<["-"], "dependency-scan-serialize-diagnostics-path">, + Flags<[ArgumentIsPath, SupplementaryOutput, NewDriverOnlyOption]>, + HelpText<"Emit a serialized diagnostics file for the dependency scanning task to " + "">, + MetaVarName<"">; + def emit_module_dependencies_path: Separate<["-"], "emit-module-dependencies-path">, Flags<[ArgumentIsPath, SupplementaryOutput, NewDriverOnlyOption]>, @@ -2194,8 +2275,12 @@ def sdk_module_cache_path : Separate<["-"], "sdk-module-cache-path">, Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>, HelpText<"Specifies the module cache path for explicitly-built SDK modules">; -def scanner_prefix_map : Separate<["-"], "scanner-prefix-map">, +def scanner_prefix_map_paths : MultiArg<["-"], "scanner-prefix-map-paths", 2>, Flags<[FrontendOption, NewDriverOnlyOption]>, + HelpText<"Remap paths reported by dependency scanner">, MetaVarName<" ">; + +def scanner_prefix_map : Separate<["-"], "scanner-prefix-map">, + Flags<[NewDriverOnlyOption]>, HelpText<"Remap paths reported by dependency scanner">, MetaVarName<"">; def scanner_prefix_map_sdk : Separate<["-"], "scanner-prefix-map-sdk">, @@ -2206,9 +2291,9 @@ def scanner_prefix_map_toolchain : Separate<["-"], "scanner-prefix-map-toolchain Flags<[NewDriverOnlyOption]>, HelpText<"Remap paths within toolchain directory reported by dependency scanner">, MetaVarName<"">; -def cache_replay_prefix_map: Separate<["-"], "cache-replay-prefix-map">, +def cache_replay_prefix_map: MultiArg<["-"], "cache-replay-prefix-map", 2>, Flags<[FrontendOption, NoDriverOption, CacheInvariant]>, - HelpText<"Remap paths when replaying outputs from cache">, MetaVarName<"">; + HelpText<"Remap paths when replaying outputs from cache">, MetaVarName<" ">; // END ONLY SUPPORTED IN NEW DRIVER @@ -2260,9 +2345,13 @@ def load_resolved_plugin: Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>, HelpText<"Path to resolved plugin configuration and a comma-separated list " "of module names where the macro types are declared. Library path " - "and exectuable path can be empty if not used">, + "and executable path can be empty if not used">, MetaVarName<"##">; +def resolved_plugin_verification : Flag<["-"], "resolved-plugin-verification">, + Flags<[FrontendOption, NoDriverOption]>, + HelpText<"verify resolved plugins">; + def in_process_plugin_server_path : Separate<["-"], "in-process-plugin-server-path">, Flags<[FrontendOption, ArgumentIsPath]>, HelpText<"Path to dynamic library plugin server">; diff --git a/include/swift/Option/SanitizerOptions.h b/include/swift/Option/SanitizerOptions.h index faa394f39b072..ab7f3bbdb8776 100644 --- a/include/swift/Option/SanitizerOptions.h +++ b/include/swift/Option/SanitizerOptions.h @@ -20,7 +20,7 @@ #include "llvm/Option/ArgList.h" // FIXME: This include is just for llvm::SanitizerCoverageOptions. We should // split the header upstream so we don't include so much. -#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Utils/Instrumentation.h" namespace swift { class DiagnosticEngine; diff --git a/include/swift/Parse/IDEInspectionCallbacks.h b/include/swift/Parse/IDEInspectionCallbacks.h index 938083795af51..7147a5f945c5d 100644 --- a/include/swift/Parse/IDEInspectionCallbacks.h +++ b/include/swift/Parse/IDEInspectionCallbacks.h @@ -39,7 +39,9 @@ enum class ParameterizedDeclAttributeKind { Available, FreestandingMacro, AttachedMacro, - StorageRestrictions + StorageRestrictions, + InheritActorContext, + Nonexhaustive, }; /// A bit of a hack. When completing inside the '@storageRestrictions' @@ -254,6 +256,10 @@ class CodeCompletionCallbacks { virtual void completeImportDecl(ImportPath::Builder &Path) {}; + /// Complete the 'using' decl with supported specifiers. + virtual void + completeUsingDecl() {}; + /// Complete unresolved members after dot. virtual void completeUnresolvedMember(CodeCompletionExpr *E, SourceLoc DotLoc) {}; diff --git a/include/swift/Parse/Lexer.h b/include/swift/Parse/Lexer.h index a33e4d3bb3dd3..5390a059892d7 100644 --- a/include/swift/Parse/Lexer.h +++ b/include/swift/Parse/Lexer.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 - 2025 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 @@ -414,7 +414,7 @@ class Lexer { static bool isOperator(StringRef string); SourceLoc getLocForStartOfBuffer() const { - return SourceLoc(llvm::SMLoc::getFromPointer(BufferStart)); + return SourceLoc::getFromPointer(BufferStart); } /// StringSegment - A segment of a (potentially interpolated) string. @@ -516,7 +516,7 @@ class Lexer { } static SourceLoc getSourceLoc(const char *Loc) { - return SourceLoc(llvm::SMLoc::getFromPointer(Loc)); + return SourceLoc::getFromPointer(Loc); } /// Get the token that starts at the given location. diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index f834cb84ce9b6..a930617945f5c 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -574,7 +574,11 @@ class Parser { /// the '(' by a space. /// /// If the next token is not '(' or it's on a new line, return false. - bool consumeIfAttributeLParen(); + bool consumeIfAttributeLParen(bool isCustomAttr = false); + + /// Check if the current token is '(' and it looks like a start of an + /// attribute argument list. + bool isAtAttributeLParen(bool isCustomAttr = false); bool consumeIfNotAtStartOfLine(tok K) { if (Tok.isAtStartOfLine()) return false; @@ -690,6 +694,14 @@ class Parser { return Context.LangOpts.EnableExperimentalConcurrency; } + /// Returns true if a Swift declaration starts after the current token, + /// otherwise returns false. + bool isNextStartOfSwiftDecl() { + BacktrackingScope backtrack(*this); + consumeToken(); + return isStartOfSwiftDecl(); + } + public: InFlightDiagnostic diagnose(SourceLoc Loc, DiagRef Diag) { if (Diags.isDiagnosticPointsToFirstBadToken(Diag.getID()) && @@ -1038,20 +1050,20 @@ class Parser { bool isAttrModifier, SourceRange &parensRange, llvm::function_ref parseAttr); - /// Parse the @_specialize attribute. + /// Parse the @_specialize/@specialized 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, AvailabilityRange *SILAvailability, + swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, bool isPublic, + AbstractSpecializeAttr *&Attr, AvailabilityRange *SILAvailability, llvm::function_ref parseSILTargetName = [](Parser &) { return false; }, llvm::function_ref parseSILSIPModule = [](Parser &) { return false; }); - /// Parse the arguments inside the @_specialize attribute + /// Parse the arguments inside the @_specialize/@specialized attribute bool parseSpecializeAttributeArguments( - swift::tok ClosingBrace, bool &DiscardAttribute, + swift::tok ClosingBrace, bool isPublic, bool &DiscardAttribute, std::optional &Exported, std::optional &Kind, TrailingWhereClause *&TrailingWhereClause, DeclNameRef &targetFunction, @@ -1127,8 +1139,8 @@ class Parser { ); /// Parse the @lifetime attribute. - ParserResult parseLifetimeAttribute(SourceLoc AtLoc, - SourceLoc Loc); + ParserResult + parseLifetimeAttribute(StringRef attrName, SourceLoc atLoc, SourceLoc loc); /// Common utility to parse swift @lifetime decl attribute and SIL @lifetime /// type modifier. @@ -1139,7 +1151,6 @@ class Parser { SourceLoc AtEndLoc, bool isFromClangAttribute = false); - bool isCustomAttributeArgument(); bool canParseCustomAttribute(); /// Parse a custom attribute after the initial '@'. @@ -1158,7 +1169,8 @@ class Parser { bool isParameterSpecifier() { if (Tok.is(tok::kw_inout)) return true; - if (Context.LangOpts.hasFeature(Feature::LifetimeDependence) && + if ((Context.LangOpts.hasFeature(Feature::LifetimeDependence) || + Context.LangOpts.hasFeature(Feature::Lifetimes)) && isSILLifetimeDependenceToken()) return true; if (!canHaveParameterSpecifierContextualKeyword()) return false; @@ -1169,8 +1181,9 @@ class Parser { Tok.isContextualKeyword("isolated") || Tok.isContextualKeyword("_const")) return true; - if (Context.LangOpts.hasFeature(Feature::SendingArgsAndResults) && - Tok.isContextualKeyword("sending")) + if (isCallerIsolatedSpecifier()) + return true; + if (Tok.isContextualKeyword("sending")) return true; return false; } @@ -1187,6 +1200,12 @@ class Parser { (peekToken().isContextualKeyword("lifetime")); } + bool isCallerIsolatedSpecifier() { + if (!Tok.isContextualKeyword("nonisolated")) + return false; + return peekToken().isFollowingLParen(); + } + bool canHaveParameterSpecifierContextualKeyword() { // The parameter specifiers like `isolated`, `consuming`, `borrowing` are // also valid identifiers and could be the name of a type. Check whether @@ -1215,6 +1234,9 @@ class Parser { ParserResult parseDeclImport(ParseDeclOptions Flags, DeclAttributes &Attributes); + ParserResult parseDeclUsing(ParseDeclOptions Flags, + DeclAttributes &Attributes); + /// Parse an inheritance clause into a vector of InheritedEntry's. /// /// \param allowClassRequirement whether to permit parsing of 'class' @@ -1421,6 +1443,7 @@ class Parser { SourceLoc IsolatedLoc; SourceLoc ConstLoc; SourceLoc SendingLoc; + SourceLoc CallerIsolatedLoc; SmallVector Attributes; LifetimeEntry *lifetimeEntry = nullptr; @@ -1723,6 +1746,10 @@ class Parser { /// Returns true if a qualified declaration name base type can be parsed. bool canParseBaseTypeForQualifiedDeclName(); + /// Returns true if `nonisolated` contextual keyword could be parsed + /// as part of the type a the current location. + bool canParseNonisolatedAsTypeModifier(); + /// Returns true if the current token is '->' or effects specifiers followed /// by '->'. /// diff --git a/include/swift/Parse/Token.h b/include/swift/Parse/Token.h index ba1a14a64ce30..39356dd252b06 100644 --- a/include/swift/Parse/Token.h +++ b/include/swift/Parse/Token.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 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -19,7 +19,6 @@ #include "swift/Basic/SourceLoc.h" #include "swift/Basic/LLVM.h" -#include "swift/Parse/Token.h" #include "swift/Config.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" @@ -196,8 +195,9 @@ class Token { #define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, ...) CONTEXTUAL_CASE(KW) #include "swift/AST/DeclAttr.def" #undef CONTEXTUAL_CASE - .Case("macro", true) - .Default(false); + .Case("macro", true) + .Case("using", true) + .Default(false); } bool isContextualPunctuator(StringRef ContextPunc) const { @@ -266,6 +266,9 @@ class Token { } } + /// True if the token is an editor placeholder. + bool isEditorPlaceholder() const; + /// True if the string literal token is multiline. bool isMultilineString() const { return MultilineString; @@ -284,9 +287,7 @@ class Token { /// getLoc - Return a source location identifier for the specified /// offset in the current file. - SourceLoc getLoc() const { - return SourceLoc(llvm::SMLoc::getFromPointer(Text.begin())); - } + SourceLoc getLoc() const { return SourceLoc::getFromPointer(Text.begin()); } unsigned getLength() const { return Text.size(); } @@ -300,17 +301,15 @@ class Token { CharSourceRange getCommentRange() const { if (CommentLength == 0) - return CharSourceRange(SourceLoc(llvm::SMLoc::getFromPointer(Text.begin())), - 0); + return CharSourceRange(SourceLoc::getFromPointer(Text.begin()), 0); auto TrimedComment = trimComment(); - return CharSourceRange( - SourceLoc(llvm::SMLoc::getFromPointer(TrimedComment.begin())), - TrimedComment.size()); + return CharSourceRange(SourceLoc::getFromPointer(TrimedComment.begin()), + TrimedComment.size()); } SourceLoc getCommentStart() const { if (CommentLength == 0) return SourceLoc(); - return SourceLoc(llvm::SMLoc::getFromPointer(trimComment().begin())); + return SourceLoc::getFromPointer(trimComment().begin()); } StringRef getRawText() const { diff --git a/include/swift/PrintAsClang/ClangMacros.def b/include/swift/PrintAsClang/ClangMacros.def index e62fbe039412c..48e614108c99b 100644 --- a/include/swift/PrintAsClang/ClangMacros.def +++ b/include/swift/PrintAsClang/ClangMacros.def @@ -176,24 +176,51 @@ CLANG_MACRO_CONDITIONAL("SWIFT_ENUM_ATTR", "(_extensibility)", \ "__attribute__((enum_extensibility(_extensibility)))") CLANG_MACRO_BODY("SWIFT_ENUM", \ - "# define SWIFT_ENUM(_type, _name, _extensibility) " \ + "# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \ + " (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \ + " __has_feature(objc_fixed_enum)\n" \ + "# define SWIFT_ENUM(_type, _name, _extensibility) " \ "enum _name : _type _name; " \ "enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type\n" \ - "# if __has_feature(generalized_swift_name)\n" \ - "# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \ - "enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); " \ - "enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) " \ - "SWIFT_ENUM_EXTRA _name : _type\n" \ + "# if __has_feature(generalized_swift_name)\n" \ + "# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \ + "enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); " \ + "enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) " \ + "SWIFT_ENUM_EXTRA _name : _type\n" \ + "# else\n" \ + "# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \ + "SWIFT_ENUM(_type, _name, _extensibility)\n" \ + "# endif\n" \ "# else\n" \ + "# define SWIFT_ENUM(_type, _name, _extensibility) _type _name; enum \n" \ "# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \ "SWIFT_ENUM(_type, _name, _extensibility)\n" \ "# endif") CLANG_MACRO_DEFINED("SWIFT_ENUM_NAMED") +CLANG_MACRO_BODY("SWIFT_ENUM_TAG", \ + "# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \ + " (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \ + " __has_feature(objc_fixed_enum)\n" \ + "# define SWIFT_ENUM_TAG enum\n" \ + "# else\n" \ + "# define SWIFT_ENUM_TAG\n" \ + "# endif") + +CLANG_MACRO_BODY("SWIFT_ENUM_FWD_DECL", \ + "# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \ + " (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \ + " __has_feature(objc_fixed_enum)\n" \ + "# define SWIFT_ENUM_FWD_DECL(_type, _name) enum _name : _type _name;\n" \ + "# else\n" \ + "# define SWIFT_ENUM_FWD_DECL(_type, _name) _type _name;\n" \ + "# endif") + CLANG_MACRO("SWIFT_UNAVAILABLE", , "__attribute__((unavailable))") CLANG_MACRO("SWIFT_UNAVAILABLE_MSG", "(msg)", "__attribute__((unavailable(msg)))") CLANG_MACRO("SWIFT_AVAILABILITY", "(plat, ...)", "__attribute__((availability(plat, __VA_ARGS__)))") +CLANG_MACRO("SWIFT_AVAILABILITY_DOMAIN", "(dom, ...)", "__attribute__((availability(domain: dom, __VA_ARGS__)))") CLANG_MACRO("SWIFT_WEAK_IMPORT", , "__attribute__((weak_import))") diff --git a/include/swift/Remote/CMemoryReader.h b/include/swift/Remote/CMemoryReader.h index 3805b76c7cdad..2a469bb80757b 100644 --- a/include/swift/Remote/CMemoryReader.h +++ b/include/swift/Remote/CMemoryReader.h @@ -42,39 +42,16 @@ namespace remote { class CMemoryReader final : public MemoryReader { MemoryReaderImpl Impl; - uint64_t ptrauthMask; - - uint64_t getPtrauthMask() { - if (ptrauthMask == 0) { - int success; - if (Impl.PointerSize == 4) { - uint32_t ptrauthMask32 = 0; - success = queryDataLayout(DataLayoutQueryType::DLQ_GetPtrAuthMask, - nullptr, &ptrauthMask32); - ptrauthMask = ptrauthMask32; - } else if (Impl.PointerSize == 8) { - success = queryDataLayout(DataLayoutQueryType::DLQ_GetPtrAuthMask, - nullptr, &ptrauthMask); - } else { - success = 0; - } - - if (!success) - ptrauthMask = ~0ull; - } - return ptrauthMask; - } - // Check to see if an address has bits outside the ptrauth mask. This suggests // that we're likely failing to strip a signed pointer when reading from it. bool hasSignatureBits(RemoteAddress address) { - return false; - uint64_t addressData = address.getAddressData(); - return addressData != (addressData & getPtrauthMask()); + uint64_t addressData = address.getRawAddress(); + uint64_t mask = getPtrAuthMask().value_or(~uint64_t(0)); + return addressData != (addressData & mask); } public: - CMemoryReader(MemoryReaderImpl Impl) : Impl(Impl), ptrauthMask(0) { + CMemoryReader(MemoryReaderImpl Impl) : Impl(Impl) { assert(this->Impl.queryDataLayout && "No queryDataLayout implementation"); assert(this->Impl.getStringLength && "No stringLength implementation"); assert(this->Impl.readBytes && "No readBytes implementation"); @@ -89,13 +66,12 @@ class CMemoryReader final : public MemoryReader { RemoteAddress getSymbolAddress(const std::string &name) override { auto addressData = Impl.getSymbolAddress(Impl.reader_context, name.c_str(), name.size()); - return RemoteAddress(addressData); + return RemoteAddress(addressData, RemoteAddress::DefaultAddressSpace); } uint64_t getStringLength(RemoteAddress address) { assert(!hasSignatureBits(address)); - return Impl.getStringLength(Impl.reader_context, - address.getAddressData()); + return Impl.getStringLength(Impl.reader_context, address.getRawAddress()); } bool readString(RemoteAddress address, std::string &dest) override { @@ -120,7 +96,7 @@ class CMemoryReader final : public MemoryReader { ReadBytesResult readBytes(RemoteAddress address, uint64_t size) override { assert(!hasSignatureBits(address)); void *FreeContext; - auto Ptr = Impl.readBytes(Impl.reader_context, address.getAddressData(), + auto Ptr = Impl.readBytes(Impl.reader_context, address.getRawAddress(), size, &FreeContext); auto Free = Impl.free; @@ -134,8 +110,7 @@ class CMemoryReader final : public MemoryReader { return ReadBytesResult(Ptr, freeLambda); } }; - -} -} +} // namespace remote +} // namespace swift #endif diff --git a/include/swift/Remote/Failure.h b/include/swift/Remote/Failure.h index 908e6357c0261..916c74892928d 100644 --- a/include/swift/Remote/Failure.h +++ b/include/swift/Remote/Failure.h @@ -288,7 +288,7 @@ class Failure { case ArgStorageKind::Address: { result += '0'; result += 'x'; - uint64_t address = Args[argIndex].Address.getAddressData(); + uint64_t address = Args[argIndex].Address.getRawAddress(); unsigned max = ((address >> 32) != 0 ? 16 : 8); for (unsigned i = 0; i != max; ++i) { result += "0123456789abcdef"[(address >> (max - 1 - i) * 4) & 0xF]; diff --git a/include/swift/Remote/InProcessMemoryReader.h b/include/swift/Remote/InProcessMemoryReader.h index 9fa78a28308d6..dbe7351317a62 100644 --- a/include/swift/Remote/InProcessMemoryReader.h +++ b/include/swift/Remote/InProcessMemoryReader.h @@ -105,8 +105,8 @@ class InProcessMemoryReader final : public MemoryReader { return ReadBytesResult(address.getLocalPointer(), [](const void *) {}); } }; - -} -} + +} // namespace remote +} // namespace swift #endif diff --git a/include/swift/Remote/MemoryReader.h b/include/swift/Remote/MemoryReader.h index 1008650b43990..835ea59b93b6c 100644 --- a/include/swift/Remote/MemoryReader.h +++ b/include/swift/Remote/MemoryReader.h @@ -22,6 +22,7 @@ #include "swift/SwiftRemoteMirror/MemoryReaderInterface.h" #include +#include #include #include #include @@ -37,7 +38,80 @@ namespace remote { /// This abstraction presents memory as if it were a read-only /// representation of the address space of a remote process. class MemoryReader { + uint8_t cachedPointerSize = 0; + uint8_t cachedSizeSize = 0; + uint64_t cachedPtrAuthMask = 0; + uint8_t cachedObjCReservedLowBits = 0xff; + uint64_t cachedLeastValidPointerValue = 0; + uint8_t cachedObjCInteropIsEnabled = 0xff; + +protected: + virtual bool queryDataLayout(DataLayoutQueryType type, void *inBuffer, + void *outBuffer) = 0; + public: + std::optional getPointerSize() { + if (cachedPointerSize == 0) { + if (!queryDataLayout(DLQ_GetPointerSize, nullptr, &cachedPointerSize)) + return std::nullopt; + } + return cachedPointerSize; + } + + std::optional getSizeSize() { + if (cachedSizeSize == 0) { + if (!queryDataLayout(DLQ_GetSizeSize, nullptr, &cachedSizeSize)) + return std::nullopt; + } + return cachedSizeSize; + } + + std::optional getPtrAuthMask() { + if (cachedPtrAuthMask == 0) { + auto ptrSize = getPointerSize(); + if (!ptrSize) + return std::nullopt; + + if (ptrSize.value() == sizeof(uint64_t)) { + if (!queryDataLayout(DLQ_GetPtrAuthMask, nullptr, &cachedPtrAuthMask)) + return std::nullopt; + } else if (ptrSize.value() == sizeof(uint32_t)) { + uint32_t mask; + if (!queryDataLayout(DLQ_GetPtrAuthMask, nullptr, &mask)) + return std::nullopt; + cachedPtrAuthMask = mask; + } + } + return cachedPtrAuthMask; + } + + std::optional getObjCReservedLowBits() { + if (cachedObjCReservedLowBits == 0xff) { + if (!queryDataLayout(DLQ_GetObjCReservedLowBits, nullptr, + &cachedObjCReservedLowBits)) + return std::nullopt; + } + return cachedObjCReservedLowBits; + } + + std::optional getLeastValidPointerValue() { + if (cachedLeastValidPointerValue == 0) { + if (!queryDataLayout(DLQ_GetLeastValidPointerValue, nullptr, + &cachedLeastValidPointerValue)) + return std::nullopt; + } + return cachedLeastValidPointerValue; + } + + std::optional getObjCInteropIsEnabled() { + if (cachedObjCInteropIsEnabled == 0xff) { + if (!queryDataLayout(DLQ_GetObjCInteropIsEnabled, nullptr, + &cachedObjCInteropIsEnabled)) + return std::nullopt; + } + return cachedObjCInteropIsEnabled; + } + /// A convenient name for the return type from readBytes. using ReadBytesResult = std::unique_ptr>; @@ -46,9 +120,6 @@ class MemoryReader { using ReadObjResult = std::unique_ptr>; - virtual bool queryDataLayout(DataLayoutQueryType type, void *inBuffer, - void *outBuffer) = 0; - /// Look up the given public symbol name in the remote process. virtual RemoteAddress getSymbolAddress(const std::string &name) = 0; @@ -57,13 +128,29 @@ class MemoryReader { /// /// Returns false if the operation failed. virtual bool readString(RemoteAddress address, std::string &dest) = 0; - + + /// Attempts to read a remote address from the given address in the remote + /// process. + /// + /// Returns false if the operator failed. + template + bool readRemoteAddress(RemoteAddress address, RemoteAddress &out) { + constexpr std::size_t integerSize = sizeof(IntegerType); + static_assert((integerSize == 4 || integerSize == 8) && + "Only 32 or 64 bit architectures are supported!"); + return readRemoteAddressImpl(address, out, integerSize); + } + /// Attempts to read an integer from the given address in the remote /// process. /// /// Returns false if the operation failed. template bool readInteger(RemoteAddress address, IntegerType *dest) { + static_assert(!std::is_same(), + "RemoteAddress cannot be read in directly, use " + "readRemoteAddress instead."); + return readBytes(address, reinterpret_cast(dest), sizeof(IntegerType)); } @@ -147,7 +234,15 @@ class MemoryReader { virtual RemoteAbsolutePointer resolvePointer(RemoteAddress address, uint64_t readValue) { // Default implementation returns the read value as is. - return RemoteAbsolutePointer("", readValue); + return RemoteAbsolutePointer( + RemoteAddress(readValue, address.getAddressSpace())); + } + + /// Performs the inverse operation of \ref resolvePointer. + /// A use-case for this is to turn file addresses into in-process addresses. + virtual std::optional + resolveRemoteAddress(RemoteAddress address) const { + return std::nullopt; } virtual std::optional @@ -159,7 +254,7 @@ class MemoryReader { virtual RemoteAbsolutePointer getSymbol(RemoteAddress address) { if (auto symbol = resolvePointerAsSymbol(address)) return *symbol; - return RemoteAbsolutePointer("", address.getAddressData()); + return RemoteAbsolutePointer(address); } /// Lookup a dynamic symbol name (ie dynamic loader binding) for the given @@ -202,50 +297,37 @@ class MemoryReader { // index (counting from 0). bool readHeapObjectExtraInhabitantIndex(RemoteAddress address, int *extraInhabitantIndex) { - uint8_t PointerSize; - if (!queryDataLayout(DataLayoutQueryType::DLQ_GetPointerSize, - nullptr, &PointerSize)) { - return false; - } - uint64_t LeastValidPointerValue; - if (!queryDataLayout(DataLayoutQueryType::DLQ_GetLeastValidPointerValue, - nullptr, &LeastValidPointerValue)) { - return false; - } - uint8_t ObjCReservedLowBits; - if (!queryDataLayout(DataLayoutQueryType::DLQ_GetObjCReservedLowBits, - nullptr, &ObjCReservedLowBits)) { + auto PointerSize = getPointerSize(); + auto LeastValidPointerValue = getLeastValidPointerValue(); + auto ObjCReservedLowBits = getObjCReservedLowBits(); + + if (!PointerSize || !LeastValidPointerValue || !ObjCReservedLowBits) return false; - } + uint64_t RawPointerValue; - if (!readInteger(address, PointerSize, &RawPointerValue)) { + if (!readInteger(address, PointerSize.value(), &RawPointerValue)) { return false; } - if (RawPointerValue >= LeastValidPointerValue) { + if (RawPointerValue >= LeastValidPointerValue.value()) { *extraInhabitantIndex = -1; // Valid value, not an XI } else { - *extraInhabitantIndex = (RawPointerValue >> ObjCReservedLowBits); + *extraInhabitantIndex = (RawPointerValue >> ObjCReservedLowBits.value()); } return true; } bool readFunctionPointerExtraInhabitantIndex(RemoteAddress address, int *extraInhabitantIndex) { - uint8_t PointerSize; - if (!queryDataLayout(DataLayoutQueryType::DLQ_GetPointerSize, - nullptr, &PointerSize)) { + auto PointerSize = getPointerSize(); + auto LeastValidPointerValue = getLeastValidPointerValue(); + if (!PointerSize || !LeastValidPointerValue) return false; - } - uint64_t LeastValidPointerValue; - if (!queryDataLayout(DataLayoutQueryType::DLQ_GetLeastValidPointerValue, - nullptr, &LeastValidPointerValue)) { - return false; - } + uint64_t RawPointerValue; - if (!readInteger(address, PointerSize, &RawPointerValue)) { + if (!readInteger(address, PointerSize.value(), &RawPointerValue)) { return false; } - if (RawPointerValue >= LeastValidPointerValue) { + if (RawPointerValue >= LeastValidPointerValue.value()) { *extraInhabitantIndex = -1; // Valid value, not an XI } else { *extraInhabitantIndex = RawPointerValue; @@ -254,9 +336,38 @@ class MemoryReader { } virtual ~MemoryReader() = default; + +protected: + /// Implementation detail of remoteRemoteAddress. This exists because + /// templated functions cannot be virtual. + /// + /// Attempts to read a remote address of a given size from the given address + /// in the remote process. + /// + /// Returns false if the operator failed. + virtual bool readRemoteAddressImpl(RemoteAddress address, RemoteAddress &out, + std::size_t integerSize) { + assert((integerSize == 4 || integerSize == 8) && + "Only 32 or 64 bit architectures are supported!"); + if (integerSize == 4) { + uint32_t buf; + if (!readBytes(address, reinterpret_cast(&buf), integerSize)) + return false; + out = RemoteAddress(buf, address.getAddressSpace()); + return true; + } + if (integerSize == 8) { + uint64_t buf; + if (!readBytes(address, reinterpret_cast(&buf), integerSize)) + return false; + out = RemoteAddress(buf, address.getAddressSpace()); + return true; + } + return false; + } }; -} // end namespace reflection +} // end namespace remote } // end namespace swift #endif // SWIFT_REFLECTION_READER_H diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 262368e9f4a2b..9187685f49b3b 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -58,19 +58,17 @@ enum class MangledNameKind { template class RemoteRef { private: - uint64_t Address; + RemoteAddress Address; const T *LocalBuffer; public: - RemoteRef() - : Address(0), LocalBuffer(nullptr) {} + RemoteRef() : Address(), LocalBuffer(nullptr) {} /*implicit*/ RemoteRef(std::nullptr_t _) : RemoteRef() {} - template - explicit RemoteRef(StoredPointer address, const T *localBuffer) - : Address((uint64_t)address), LocalBuffer(localBuffer) {} + explicit RemoteRef(RemoteAddress address, const T *localBuffer) + : Address(address), LocalBuffer(localBuffer) {} // Some versions of clang++ sometimes fail to generate the // copy constructor for this type correctly - add a workaround @@ -83,9 +81,7 @@ class RemoteRef { return *this; } - uint64_t getAddressData() const { - return Address; - } + RemoteAddress getRemoteAddress() const { return Address; } const T *getLocalBuffer() const { return LocalBuffer; @@ -113,23 +109,23 @@ class RemoteRef { template RemoteRef getField(U &field) const { auto offset = (intptr_t)&field - (intptr_t)LocalBuffer; - return RemoteRef((uint64_t)(Address + (int64_t)offset), &field); + return RemoteRef((Address + (int64_t)offset), &field); } /// Resolve the remote address of a relative offset stored at the remote address. - uint64_t resolveRelativeAddressData() const { + RemoteAddress resolveRelativeAddressData() const { int32_t offset; memcpy(&offset, LocalBuffer, sizeof(int32_t)); if (offset == 0) - return 0; + return RemoteAddress(); return Address + (int64_t)offset; } - - template - uint64_t resolveRelativeFieldData(U &field) const { + + template + RemoteAddress resolveRelativeFieldData(U &field) const { return getField(field).resolveRelativeAddressData(); } - + RemoteRef atByteOffset(int64_t Offset) const { return RemoteRef(Address + Offset, (const T *)((intptr_t)LocalBuffer + Offset)); @@ -195,10 +191,10 @@ class MetadataReader { /// amounts of data when we encounter corrupt values for sizes/counts. static const uint64_t MaxMetadataSize = 1048576; // 1MB - /// The dense map info for a std::pair. + /// The dense map info for a std::pair. struct DenseMapInfoTypeCacheKey { - using Pair = std::pair; - using StoredPointerInfo = llvm::DenseMapInfo; + using Pair = std::pair; + using StoredPointerInfo = llvm::DenseMapInfo; static inline Pair getEmptyKey() { // Since bool doesn't have an empty key implementation, we only use the @@ -223,7 +219,7 @@ class MetadataReader { /// A cache of built types, keyed by the address of the type and whether the /// request ignored articial superclasses or not. - llvm::DenseMap, BuiltType, + llvm::DenseMap, BuiltType, DenseMapInfoTypeCacheKey> TypeCache; @@ -231,7 +227,7 @@ class MetadataReader { using OwnedMetadataRef = MemoryReader::ReadBytesResult; /// A cache of read type metadata, keyed by the address of the metadata. - llvm::DenseMap MetadataCache; + llvm::DenseMap MetadataCache; using ContextDescriptorRef = RemoteRef>; @@ -313,14 +309,14 @@ class MetadataReader { /// A cache of read nominal type descriptors, keyed by the address of the /// nominal type descriptor. - llvm::DenseMap + llvm::DenseMap ContextDescriptorCache; using OwnedProtocolDescriptorRef = std::unique_ptr, delete_with_free>; /// A cache of read extended existential shape metadata, keyed by the /// address of the shape metadata. - llvm::DenseMap ShapeCache; + llvm::DenseMap ShapeCache; enum class IsaEncodingKind { /// We haven't checked yet. @@ -359,8 +355,8 @@ class MetadataReader { StoredPointer IsaIndexShift; StoredPointer IsaMagicMask; StoredPointer IsaMagicValue; - StoredPointer IndexedClassesPointer; - StoredPointer IndexedClassesCountPointer; + RemoteAddress IndexedClassesPointer; + RemoteAddress IndexedClassesCountPointer; StoredPointer LastIndexedClassesCount = 0; enum class TaggedPointerEncodingKind { @@ -389,11 +385,11 @@ class MetadataReader { StoredPointer TaggedPointerMask; StoredPointer TaggedPointerSlotShift; StoredPointer TaggedPointerSlotMask; - StoredPointer TaggedPointerClasses; + RemoteAddress TaggedPointerClasses; StoredPointer TaggedPointerExtendedMask; StoredPointer TaggedPointerExtendedSlotShift; StoredPointer TaggedPointerExtendedSlotMask; - StoredPointer TaggedPointerExtendedClasses; + RemoteAddress TaggedPointerExtendedClasses; StoredPointer TaggedPointerObfuscator; Demangle::NodeFactory Factory; @@ -411,33 +407,32 @@ class MetadataReader { StoredPointer PtrAuthMask; - StoredPointer stripSignedPointer(StoredSignedPointer P) { - return P.SignedValue & PtrAuthMask; + RemoteAddress stripSignedPointer(RemoteAddress P) { + // Only pointers in the default address space are signed. + if (P.getAddressSpace() == RemoteAddress::DefaultAddressSpace) + return P & PtrAuthMask; + return P; + } + + RemoteAddress stripSignedPointer(StoredSignedPointer P) { + return RemoteAddress(P.SignedValue & PtrAuthMask, + RemoteAddress::DefaultAddressSpace); } RemoteAbsolutePointer stripSignedPointer(const RemoteAbsolutePointer &P) { - if (P.isResolved()) { - return RemoteAbsolutePointer("", - P.getResolvedAddress().getAddressData() & PtrAuthMask); - } - return P; + auto Stripped = stripSignedPointer(P.getResolvedAddress()); + return RemoteAbsolutePointer(P.getSymbol(), P.getOffset(), Stripped); } StoredPointer queryPtrAuthMask() { - StoredPointer QueryResult; - if (Reader->queryDataLayout(DataLayoutQueryType::DLQ_GetPtrAuthMask, - nullptr, &QueryResult)) { - return QueryResult; - } - return ~StoredPointer(0); + auto QueryResult = Reader->getPtrAuthMask(); + return QueryResult.value_or(~StoredPointer(0)); } template - MetadataReader(std::shared_ptr reader, T &&... args) - : Builder(std::forward(args)...), - Reader(std::move(reader)), - PtrAuthMask(queryPtrAuthMask()) { - } + MetadataReader(std::shared_ptr reader, T &&...args) + : Builder(std::forward(args)...), Reader(std::move(reader)), + PtrAuthMask(queryPtrAuthMask()) {} MetadataReader(const MetadataReader &other) = delete; MetadataReader &operator=(const MetadataReader &other) = delete; @@ -464,7 +459,7 @@ class MetadataReader { auto offsetInMangledName = (const char *)base - mangledName.getLocalBuffer(); auto remoteAddress = - mangledName.getAddressData() + offsetInMangledName + offset; + mangledName.getRemoteAddress() + offsetInMangledName + offset; RemoteAbsolutePointer resolved; if (directness == Directness::Indirect) { @@ -474,7 +469,7 @@ class MetadataReader { return nullptr; } } else { - resolved = Reader->getSymbol(RemoteAddress(remoteAddress)); + resolved = Reader->getSymbol(remoteAddress); } switch (kind) { @@ -485,12 +480,13 @@ class MetadataReader { // Try to preserve a reference to an OpaqueTypeDescriptor // symbolically, since we'd like to read out and resolve the type ref // to the underlying type if available. - if (useOpaqueTypeSymbolicReferences - && context.isResolved() - && context.getResolved()->getKind() == ContextDescriptorKind::OpaqueType){ + if (useOpaqueTypeSymbolicReferences && context.isResolved() && + context.getResolved()->getKind() == + ContextDescriptorKind::OpaqueType) { return dem.createNode( - Node::Kind::OpaqueTypeDescriptorSymbolicReference, - context.getResolved().getAddressData()); + Node::Kind::OpaqueTypeDescriptorSymbolicReference, + context.getResolved().getRemoteAddress().getRawAddress(), + context.getResolved().getRemoteAddress().getAddressSpace()); } return buildContextMangling(context, dem); @@ -504,43 +500,28 @@ class MetadataReader { // The symbolic reference points at a unique extended // existential type shape. return dem.createNode( - Node::Kind::UniqueExtendedExistentialTypeShapeSymbolicReference, - resolved.getResolvedAddress().getAddressData()); + Node::Kind::UniqueExtendedExistentialTypeShapeSymbolicReference, + resolved.getResolvedAddress().getRawAddress(), + resolved.getResolvedAddress().getAddressSpace()); } case Demangle::SymbolicReferenceKind::NonUniqueExtendedExistentialTypeShape: { // The symbolic reference points at a non-unique extended // existential type shape. return dem.createNode( - Node::Kind::NonUniqueExtendedExistentialTypeShapeSymbolicReference, - resolved.getResolvedAddress().getAddressData()); + Node::Kind::NonUniqueExtendedExistentialTypeShapeSymbolicReference, + resolved.getResolvedAddress().getRawAddress(), + resolved.getResolvedAddress().getAddressSpace()); } case Demangle::SymbolicReferenceKind::ObjectiveCProtocol: { // 'resolved' points to a struct of two relative addresses. // The second entry is a relative address to the mangled protocol // without symbolic references. - // lldb might return an unresolved remote absolute pointer from its - // resolvePointerAsSymbol implementation -- workaround this. - if (!resolved.isResolved()) { - auto remoteAddr = RemoteAddress(remoteAddress); - resolved = - RemoteAbsolutePointer("", remoteAddr.getAddressData()); - } - - auto addr = - resolved.getResolvedAddress().getAddressData() + sizeof(int32_t); + auto addr = resolved.getResolvedAddress() + sizeof(int32_t); int32_t offset; - Reader->readInteger(RemoteAddress(addr), &offset); + Reader->readInteger(addr, &offset); auto addrOfTypeRef = addr + offset; - resolved = Reader->getSymbol(RemoteAddress(addrOfTypeRef)); - - // lldb might return an unresolved remote absolute pointer from its - // resolvePointerAsSymbol implementation -- workaround this. - if (!resolved.isResolved()) { - auto remoteAddr = RemoteAddress(addrOfTypeRef); - resolved = - RemoteAbsolutePointer("", remoteAddr.getAddressData()); - } + resolved = Reader->getSymbol(addrOfTypeRef); // Dig out the protocol from the protocol list. auto protocolList = readMangledName(resolved.getResolvedAddress(), @@ -594,10 +575,9 @@ class MetadataReader { /// Demangle a mangled name from a potentially temporary std::string. The /// demangler may produce pointers into the string data, so this copies the /// string into the demangler's allocation first. - Demangle::NodePointer demangle(uint64_t remoteAddress, + Demangle::NodePointer demangle(RemoteAddress remoteAddress, const std::string &mangledName, - MangledNameKind kind, - Demangler &dem) { + MangledNameKind kind, Demangler &dem) { StringRef mangledNameCopy = dem.copyString(mangledName); return demangle(RemoteRef(remoteAddress, mangledNameCopy.data()), kind, dem); @@ -625,7 +605,7 @@ class MetadataReader { /// Given a remote pointer to metadata, attempt to discover its MetadataKind. std::optional - readKindFromMetadata(StoredPointer MetadataAddress) { + readKindFromMetadata(RemoteAddress MetadataAddress) { auto meta = readMetadata(MetadataAddress); if (!meta) return std::nullopt; @@ -634,11 +614,10 @@ class MetadataReader { } /// Given a remote pointer to class metadata, attempt to read its superclass. - StoredPointer - readSuperClassFromClassMetadata(StoredPointer MetadataAddress) { + RemoteAddress readSuperClassFromClassMetadata(RemoteAddress MetadataAddress) { auto meta = readMetadata(MetadataAddress); if (!meta || meta->getKind() != MetadataKind::Class) - return StoredPointer(); + return RemoteAddress(); auto classMeta = cast(meta); return stripSignedPointer(classMeta->Superclass); @@ -647,7 +626,7 @@ class MetadataReader { /// Given a remote pointer to class metadata, attempt to discover its class /// instance size and whether fields should use the resilient layout strategy. std::optional - readInstanceStartFromClassMetadata(StoredPointer MetadataAddress) { + readInstanceStartFromClassMetadata(RemoteAddress MetadataAddress) { auto meta = readMetadata(MetadataAddress); if (!meta || meta->getKind() != MetadataKind::Class) return std::nullopt; @@ -656,7 +635,7 @@ class MetadataReader { // The following algorithm only works on the non-fragile Apple runtime. // Grab the RO-data pointer. This part is not ABI. - StoredPointer roDataPtr = readObjCRODataPtr(MetadataAddress); + RemoteAddress roDataPtr = readObjCRODataPtr(MetadataAddress); if (!roDataPtr) return std::nullopt; @@ -664,7 +643,7 @@ class MetadataReader { auto address = roDataPtr + sizeof(uint32_t) * 1; unsigned start; - if (!Reader->readInteger(RemoteAddress(address), &start)) + if (!Reader->readInteger(address, &start)) return std::nullopt; return start; @@ -694,19 +673,19 @@ class MetadataReader { /// witness table. Note that it's not safe to access any non-mandatory /// members of the value witness table, like extra inhabitants or enum members. std::optional> - readValueWitnessTable(StoredPointer MetadataAddress) { + readValueWitnessTable(RemoteAddress MetadataAddress) { // The value witness table pointer is at offset -1 from the metadata // pointer, that is, the pointer-sized word immediately before the // pointer's referenced address. TargetValueWitnessTable VWT; auto ValueWitnessTableAddrAddr = MetadataAddress - sizeof(StoredPointer); StoredSignedPointer SignedValueWitnessTableAddr; - if (!Reader->readInteger(RemoteAddress(ValueWitnessTableAddrAddr), + if (!Reader->readInteger(ValueWitnessTableAddrAddr, &SignedValueWitnessTableAddr)) return std::nullopt; - auto ValueWitnessTableAddr = stripSignedPointer(SignedValueWitnessTableAddr); - if (!Reader->readBytes(RemoteAddress(ValueWitnessTableAddr), - (uint8_t *)&VWT, sizeof(VWT))) + auto ValueWitnessTableAddr = + stripSignedPointer(SignedValueWitnessTableAddr); + if (!Reader->readBytes(ValueWitnessTableAddr, (uint8_t *)&VWT, sizeof(VWT))) return std::nullopt; return VWT; } @@ -718,8 +697,7 @@ class MetadataReader { std::optional readMetadataAndValueErrorExistential(RemoteAddress ExistentialAddress) { // An pointer to an error existential is always an heap object. - auto MetadataAddress = - readMetadataFromInstance(ExistentialAddress.getAddressData()); + auto MetadataAddress = readMetadataFromInstance(ExistentialAddress); if (!MetadataAddress) return std::nullopt; @@ -748,18 +726,15 @@ class MetadataReader { if (isBridged) { // NSError instances don't need to be unwrapped. - return RemoteExistential(RemoteAddress(*MetadataAddress), - ExistentialAddress, - isBridged); + return RemoteExistential(*MetadataAddress, ExistentialAddress, isBridged); } // In addition to the isa pointer and two 32-bit reference counts, if the // error existential is layout-compatible with NSError, we also need to // skip over its three word-sized fields: the error code, the domain, // and userInfo. - StoredPointer InstanceMetadataAddressAddress = - ExistentialAddress.getAddressData() + - (isObjC ? 5 : 2) * sizeof(StoredPointer); + RemoteAddress InstanceMetadataAddressAddress = + ExistentialAddress + (isObjC ? 5 : 2) * sizeof(StoredPointer); // We need to get the instance's alignment info so we can get the exact // offset of the start of its data in the class. @@ -775,7 +750,7 @@ class MetadataReader { // Now we need to skip over the instance metadata pointer and instance's // conformance pointer for Swift.Error. - StoredPointer InstanceAddress = + RemoteAddress InstanceAddress = InstanceMetadataAddressAddress + 2 * sizeof(StoredPointer); // When built with Objective-C interop, the runtime also stores a conformance @@ -788,10 +763,8 @@ class MetadataReader { auto AlignmentMask = VWT->getAlignmentMask(); InstanceAddress = (InstanceAddress + AlignmentMask) & ~AlignmentMask; - return RemoteExistential( - RemoteAddress(*InstanceMetadataAddress), - RemoteAddress(InstanceAddress), - isBridged); + return RemoteExistential(*InstanceMetadataAddress, InstanceAddress, + isBridged); } /// Given a known-opaque existential, attempt to discover the pointer to its @@ -801,10 +774,11 @@ class MetadataReader { // OpaqueExistentialContainer is the layout of an opaque existential. // `Type` is the pointer to the metadata. TargetOpaqueExistentialContainer Container; - if (!Reader->readBytes(RemoteAddress(ExistentialAddress), - (uint8_t *)&Container, sizeof(Container))) + if (!Reader->readBytes(ExistentialAddress, (uint8_t *)&Container, + sizeof(Container))) return std::nullopt; - auto MetadataAddress = static_cast(Container.Type); + auto MetadataAddress = + RemoteAddress(Container.Type, ExistentialAddress.getAddressSpace()); auto Metadata = readMetadata(MetadataAddress); if (!Metadata) return std::nullopt; @@ -816,20 +790,19 @@ class MetadataReader { // Inline representation (the value fits in the existential container). // So, the value starts at the first word of the container. if (VWT->isValueInline()) - return RemoteExistential(RemoteAddress(MetadataAddress), - ExistentialAddress); + return RemoteExistential(MetadataAddress, ExistentialAddress); // Non-inline (box'ed) representation. // The first word of the container stores the address to the box. - StoredPointer BoxAddress; - if (!Reader->readInteger(ExistentialAddress, &BoxAddress)) + RemoteAddress BoxAddress; + if (!Reader->template readRemoteAddress(ExistentialAddress, + BoxAddress)) return std::nullopt; auto AlignmentMask = VWT->getAlignmentMask(); auto Offset = (sizeof(HeapObject) + AlignmentMask) & ~AlignmentMask; auto StartOfValue = BoxAddress + Offset; - return RemoteExistential(RemoteAddress(MetadataAddress), - RemoteAddress(StartOfValue)); + return RemoteExistential(MetadataAddress, StartOfValue); } /// Given a known-opaque existential, discover if its value is inlined in @@ -839,10 +812,12 @@ class MetadataReader { // OpaqueExistentialContainer is the layout of an opaque existential. // `Type` is the pointer to the metadata. TargetOpaqueExistentialContainer Container; - if (!Reader->readBytes(RemoteAddress(ExistentialAddress), - (uint8_t *)&Container, sizeof(Container))) + if (!Reader->readBytes(ExistentialAddress, (uint8_t *)&Container, + sizeof(Container))) return std::nullopt; - auto MetadataAddress = static_cast(Container.Type); + auto MetadataAddress = + RemoteAddress(Container.Type, ExistentialAddress.getAddressSpace()); + auto Metadata = readMetadata(MetadataAddress); if (!Metadata) return std::nullopt; @@ -855,11 +830,10 @@ class MetadataReader { } /// Read a protocol from a reference to said protocol. - template + template typename Resolver::Result readProtocol( - const TargetProtocolDescriptorRef &ProtocolAddress, - Demangler &dem, - Resolver resolver) { + const RemoteTargetProtocolDescriptorRef &ProtocolAddress, + Demangler &dem, Resolver resolver) { #if SWIFT_OBJC_INTEROP if (Runtime::ObjCInterop) { // Check whether we have an Objective-C protocol. @@ -895,8 +869,7 @@ class MetadataReader { #endif // Swift-native protocol. - auto Demangled = - readDemanglingForContextDescriptor( + auto Demangled = readDemanglingForContextDescriptor( stripSignedPointer({ProtocolAddress.getSwiftProtocol()}), dem); if (!Demangled) return resolver.failure(); @@ -904,12 +877,79 @@ class MetadataReader { return resolver.swiftProtocol(Demangled); } + BuiltType readTypeFromShape( + RemoteAbsolutePointer shapeAddress, + std::function>(unsigned)> getArgs) { + RemoteAddress addr = shapeAddress.getResolvedAddress(); + ShapeRef Shape = readShape(addr); + if (!Shape) + return BuiltType(); + + assert(Shape->hasGeneralizationSignature()); + + // Pull out the existential type from the mangled type name. + Demangler dem; + auto mangledExistentialAddr = + resolveRelativeField(Shape, Shape->ExistentialType); + auto node = + readMangledName(mangledExistentialAddr, MangledNameKind::Type, dem); + if (!node) + return BuiltType(); + + BuiltType builtProto = decodeMangledType(node).getType(); + if (!builtProto) + return BuiltType(); + + if (auto builtArgs = getArgs(Shape->getGenSigArgumentLayoutSizeInWords())) { + // Build up a substitution map for the generalized signature. + BuiltGenericSignature sig = + decodeRuntimeGenericSignature(Shape, + Shape->getGeneralizationSignature()) + .getType(); + if (!sig) + return BuiltType(); + + BuiltSubstitutionMap subst = + Builder.createSubstitutionMap(sig, *builtArgs); + if (subst.empty()) + return BuiltType(); + + builtProto = Builder.subst(builtProto, subst); + if (!builtProto) + return BuiltType(); + } + + // Read the type expression to build up any remaining layers of + // existential metatype. + if (Shape->Flags.hasTypeExpression()) { + Demangler dem; + + // Read the mangled name. + auto mangledContextName = Shape->getTypeExpression(); + auto mangledNameAddress = + resolveRelativeField(Shape, mangledContextName->name); + auto node = + readMangledName(mangledNameAddress, MangledNameKind::Type, dem); + if (!node) + return BuiltType(); + + while (node->getKind() == Demangle::Node::Kind::Type && + node->hasChildren() && + node->getChild(0)->getKind() == Demangle::Node::Kind::Metatype && + node->getChild(0)->getNumChildren()) { + builtProto = Builder.createExistentialMetatypeType(builtProto); + node = node->getChild(0)->getChild(0); + } + } + return builtProto; + } + /// Given a remote pointer to metadata, attempt to turn it into a type. BuiltType - readTypeFromMetadata(StoredPointer MetadataAddress, + readTypeFromMetadata(RemoteAddress MetadataAddress, bool skipArtificialSubclasses = false, int recursion_limit = defaultTypeRecursionLimit) { - std::pair TypeCacheKey(MetadataAddress, + std::pair TypeCacheKey(MetadataAddress, skipArtificialSubclasses); auto Cached = TypeCache.find(TypeCacheKey); if (Cached != TypeCache.end()) @@ -952,17 +992,20 @@ class MetadataReader { for (unsigned i = 0, n = tupleMeta->NumElements; i != n; ++i) { auto &element = tupleMeta->getElement(i); - if (auto elementType = - readTypeFromMetadata(element.Type, false, recursion_limit)) + auto elementTypeAddress = + RemoteAddress(element.Type, MetadataAddress.getAddressSpace()); + if (auto elementType = readTypeFromMetadata(elementTypeAddress, false, + recursion_limit)) elementTypes.push_back(elementType); else return BuiltType(); } // Read the labels string. + auto labelAddress = + RemoteAddress(tupleMeta->Labels, MetadataAddress.getAddressSpace()); std::string labelStr; - if (tupleMeta->Labels && - !Reader->readString(RemoteAddress(tupleMeta->Labels), labelStr)) + if (labelAddress && !Reader->readString(labelAddress, labelStr)) return BuiltType(); std::vector labels; @@ -988,8 +1031,10 @@ class MetadataReader { std::vector> Parameters; for (unsigned i = 0, n = Function->getNumParameters(); i != n; ++i) { - auto ParamTypeRef = readTypeFromMetadata(Function->getParameter(i), - false, recursion_limit); + auto paramAddress = RemoteAddress(Function->getParameter(i), + MetadataAddress.getAddressSpace()); + auto ParamTypeRef = + readTypeFromMetadata(paramAddress, false, recursion_limit); if (!ParamTypeRef) return BuiltType(); @@ -999,8 +1044,10 @@ class MetadataReader { Parameters.push_back(std::move(Param)); } + auto resultTypeAddress = RemoteAddress(Function->ResultType, + MetadataAddress.getAddressSpace()); auto Result = - readTypeFromMetadata(Function->ResultType, false, recursion_limit); + readTypeFromMetadata(resultTypeAddress, false, recursion_limit); if (!Result) return BuiltType(); @@ -1012,8 +1059,10 @@ class MetadataReader { BuiltType globalActor = BuiltType(); if (Function->hasGlobalActor()) { - globalActor = readTypeFromMetadata(Function->getGlobalActor(), false, - recursion_limit); + auto globalActorAddress = RemoteAddress( + Function->getGlobalActor(), MetadataAddress.getAddressSpace()); + globalActor = + readTypeFromMetadata(globalActorAddress, false, recursion_limit); if (!globalActor) return BuiltType(); } @@ -1035,8 +1084,10 @@ class MetadataReader { BuiltType thrownError = BuiltType(); if (Function->hasThrownError()) { - thrownError = readTypeFromMetadata(Function->getThrownError(), false, - recursion_limit); + auto thrownErrorAddress = RemoteAddress( + Function->getThrownError(), MetadataAddress.getAddressSpace()); + thrownError = + readTypeFromMetadata(thrownErrorAddress, false, recursion_limit); if (!thrownError) return BuiltType(); } @@ -1055,9 +1106,12 @@ class MetadataReader { BuiltType SuperclassType = BuiltType(); if (Exist->Flags.hasSuperclassConstraint()) { + auto superclassContraintAddress = + RemoteAddress(Exist->getSuperclassConstraint(), + MetadataAddress.getAddressSpace()); // The superclass is stored after the list of protocols. - SuperclassType = readTypeFromMetadata(Exist->getSuperclassConstraint(), - false, recursion_limit); + SuperclassType = readTypeFromMetadata(superclassContraintAddress, false, + recursion_limit); if (!SuperclassType) return BuiltType(); HasExplicitAnyObject = true; @@ -1087,7 +1141,10 @@ class MetadataReader { Demangler dem; std::vector Protocols; for (auto ProtocolAddress : Exist->getProtocols()) { - if (auto Protocol = readProtocol(ProtocolAddress, dem, resolver)) + auto ProtocolRef = RemoteTargetProtocolDescriptorRef( + RemoteAddress(ProtocolAddress.getRawData(), + MetadataAddress.getAddressSpace())); + if (auto Protocol = readProtocol(ProtocolRef, dem, resolver)) Protocols.push_back(Protocol); else return BuiltType(); @@ -1099,87 +1156,37 @@ class MetadataReader { } case MetadataKind::ExtendedExistential: { auto Exist = cast>(Meta); - // Read the shape for this existential. - StoredPointer shapeAddress = stripSignedPointer(Exist->Shape); - ShapeRef Shape = readShape(shapeAddress); - if (!Shape) - return BuiltType(); - - const unsigned shapeArgumentCount - = Shape->getGenSigArgumentLayoutSizeInWords(); - // Pull out the arguments to the generalization signature. - assert(Shape->hasGeneralizationSignature()); - std::vector builtArgs; - for (unsigned i = 0; i < shapeArgumentCount; ++i) { - auto remoteArg = Exist->getGeneralizationArguments()[i]; - auto builtArg = readTypeFromMetadata(remoteArg, false, recursion_limit); - if (!builtArg) - return BuiltType(); - builtArgs.push_back(builtArg); - } - - // Pull out the existential type from the mangled type name. - Demangler dem; - auto mangledExistentialAddr = - resolveRelativeField(Shape, Shape->ExistentialType); - auto node = readMangledName(RemoteAddress(mangledExistentialAddr), - MangledNameKind::Type, dem); - if (!node) - return BuiltType(); - - BuiltType builtProto = decodeMangledType(node).getType(); - if (!builtProto) - return BuiltType(); - - // Build up a substitution map for the generalized signature. - BuiltGenericSignature sig = - decodeRuntimeGenericSignature(Shape, - Shape->getGeneralizationSignature()) - .getType(); - if (!sig) - return BuiltType(); - - BuiltSubstitutionMap subst = - Builder.createSubstitutionMap(sig, builtArgs); - if (subst.empty()) - return BuiltType(); - - builtProto = Builder.subst(builtProto, subst); - if (!builtProto) - return BuiltType(); - - // Read the type expression to build up any remaining layers of - // existential metatype. - if (Shape->Flags.hasTypeExpression()) { - Demangler dem; - - // Read the mangled name. - auto mangledContextName = Shape->getTypeExpression(); - auto mangledNameAddress = - resolveRelativeField(Shape, mangledContextName->name); - auto node = readMangledName(RemoteAddress(mangledNameAddress), - MangledNameKind::Type, dem); - if (!node) - return BuiltType(); - - while (node->getKind() == Demangle::Node::Kind::Type && - node->getNumChildren() && - node->getChild(0)->getKind() == Demangle::Node::Kind::Metatype && - node->getChild(0)->getNumChildren()) { - builtProto = Builder.createExistentialMetatypeType(builtProto); - node = node->getChild(0)->getChild(0); - } - } + RemoteAddress shapeAddress = stripSignedPointer(Exist->Shape); + auto builtProto = readTypeFromShape( + RemoteAddress(shapeAddress), + [&](unsigned shapeArgumentCount) + -> std::optional> { + // Pull out the arguments to the generalization signature. + std::vector builtArgs; + for (unsigned i = 0; i < shapeArgumentCount; ++i) { + auto remoteArg = Exist->getGeneralizationArguments()[i]; + auto builtArg = readTypeFromMetadata( + remote::RemoteAddress(remoteArg, + shapeAddress.getAddressSpace()), + false, recursion_limit); + if (!builtArg) + return std::nullopt; + builtArgs.push_back(builtArg); + } + return builtArgs; + }); TypeCache[TypeCacheKey] = builtProto; return builtProto; } case MetadataKind::Metatype: { auto Metatype = cast>(Meta); + auto InstanceTypeAddress = RemoteAddress( + Metatype->InstanceType, MetadataAddress.getAddressSpace()); auto Instance = - readTypeFromMetadata(Metatype->InstanceType, false, recursion_limit); + readTypeFromMetadata(InstanceTypeAddress, false, recursion_limit); if (!Instance) return BuiltType(); auto BuiltMetatype = Builder.createMetatypeType(Instance); TypeCache[TypeCacheKey] = BuiltMetatype; @@ -1187,7 +1194,8 @@ class MetadataReader { } case MetadataKind::ObjCClassWrapper: { auto objcWrapper = cast>(Meta); - auto classAddress = objcWrapper->Class; + auto classAddress = + RemoteAddress(objcWrapper->Class, MetadataAddress.getAddressSpace()); std::string className; if (!readObjCClassName(classAddress, className)) @@ -1199,8 +1207,10 @@ class MetadataReader { } case MetadataKind::ExistentialMetatype: { auto Exist = cast>(Meta); + auto classAddress = + RemoteAddress(Exist->InstanceType, MetadataAddress.getAddressSpace()); auto Instance = - readTypeFromMetadata(Exist->InstanceType, false, recursion_limit); + readTypeFromMetadata(classAddress, false, recursion_limit); if (!Instance) return BuiltType(); auto BuiltExist = Builder.createExistentialMetatypeType(Instance); TypeCache[TypeCacheKey] = BuiltExist; @@ -1281,8 +1291,13 @@ class MetadataReader { switch (req.Flags.getKind()) { case GenericRequirementKind::SameType: { Demangler rdem; + // FIXME: This should not work since the mangled name pointer is on the + // local process. + auto mangledNameAddress = + RemoteAddress((uint64_t)req.getMangledTypeName().data(), + RemoteAddress::DefaultAddressSpace); auto demangledConstraint = - demangle(RemoteRef(req.getMangledTypeName().data(), + demangle(RemoteRef(mangledNameAddress, req.getMangledTypeName().data()), MangledNameKind::Type, rdem); auto constraintType = decodeMangledType(demangledConstraint); @@ -1323,7 +1338,9 @@ class MetadataReader { Demangler dem; auto protocolAddress = resolveRelativeIndirectProtocol(contextRef, req.Protocol); - auto protocol = readProtocol(protocolAddress, dem, resolver); + auto protocolRef = + RemoteTargetProtocolDescriptorRef(protocolAddress); + auto protocol = readProtocol(protocolRef, dem, resolver); if (!protocol) { return TypeLookupError("Failed to read protocol type in conformance " "requirement of runtime generic signature."); @@ -1335,8 +1352,13 @@ class MetadataReader { } case GenericRequirementKind::BaseClass: { Demangler rdem; + // FIXME: This should not work since the mangled name pointer is on the + // local process. + auto mangledNameAddress = + RemoteAddress((uint64_t)req.getMangledTypeName().data(), + RemoteAddress::DefaultAddressSpace); auto demangledConstraint = - demangle(RemoteRef(req.getMangledTypeName().data(), + demangle(RemoteRef(mangledNameAddress, req.getMangledTypeName().data()), MangledNameKind::Type, rdem); auto constraintType = decodeMangledType(demangledConstraint); @@ -1379,90 +1401,84 @@ class MetadataReader { ParentContextDescriptorRef readContextDescriptor(const RemoteAbsolutePointer &address) { // Map an unresolved pointer to an unresolved context ref. - if (!address.isResolved()) { + if (!address.getSymbol().empty()) { // We can only handle references to a symbol without an offset currently. - if (address.getOffset() != 0) { - return ParentContextDescriptorRef(); - } - return ParentContextDescriptorRef(address.getSymbol()); + if (address.getOffset() == 0) + return ParentContextDescriptorRef(address.getSymbol()); } - + return ParentContextDescriptorRef( - readContextDescriptor(address.getResolvedAddress().getAddressData())); + readContextDescriptor(address.getResolvedAddress())); } - ShapeRef - readShape(StoredPointer address) { - if (address == 0) + ShapeRef readShape(RemoteAddress address) { + if (!address) return nullptr; + using ShapeHeader = TargetExtendedExistentialTypeShape; auto cached = ShapeCache.find(address); if (cached != ShapeCache.end()) - return ShapeRef(address, - reinterpret_cast *>( - cached->second.get())); + return ShapeRef( + address, reinterpret_cast(cached->second.get())); ExtendedExistentialTypeShapeFlags flags; - if (!Reader->readBytes(RemoteAddress(address), (uint8_t*)&flags, - sizeof(flags))) + if (!Reader->readBytes(address, (uint8_t *)&flags, sizeof(flags))) return nullptr; // Read the size of the requirement signature. - uint64_t reqSigGenericSize = 0; - uint64_t genericHeaderSize = sizeof(GenericContextDescriptorHeader); + uint64_t descriptorSize; { - GenericContextDescriptorHeader header; - auto headerAddr = address + sizeof(flags); - - if (!Reader->readBytes(RemoteAddress(headerAddr), - (uint8_t*)&header, sizeof(header))) + auto readResult = + Reader->readBytes(RemoteAddress(address), sizeof(ShapeHeader)); + if (!readResult) return nullptr; - - reqSigGenericSize = reqSigGenericSize - + (header.NumParams + 3u & ~3u) - + header.NumRequirements - * sizeof(TargetGenericRequirementDescriptor); - } - uint64_t typeExprSize = flags.hasTypeExpression() ? sizeof(StoredPointer) : 0; - uint64_t suggestedVWSize = flags.hasSuggestedValueWitnesses() ? sizeof(StoredPointer) : 0; - - uint64_t size = sizeof(ExtendedExistentialTypeShapeFlags) + - sizeof(TargetRelativeDirectPointer) + - genericHeaderSize + typeExprSize + suggestedVWSize + - reqSigGenericSize; - if (size > MaxMetadataSize) + auto shapeHeader = + reinterpret_cast(readResult.get()); + + // Read the size of the requirement signature. + uint64_t reqSigGenericSize = 0; + auto flags = shapeHeader->Flags; + auto &reqSigHeader = shapeHeader->ReqSigHeader; + reqSigGenericSize = + reqSigGenericSize + (reqSigHeader.NumParams + 3u & ~3u) + + reqSigHeader.NumRequirements * + sizeof(TargetGenericRequirementDescriptor); + uint64_t typeExprSize = + flags.hasTypeExpression() ? sizeof(StoredPointer) : 0; + uint64_t suggestedVWSize = + flags.hasSuggestedValueWitnesses() ? sizeof(StoredPointer) : 0; + + descriptorSize = sizeof(shapeHeader) + typeExprSize + suggestedVWSize + + reqSigGenericSize; + } + if (descriptorSize > MaxMetadataSize) return nullptr; - auto readResult = Reader->readBytes(RemoteAddress(address), size); + auto readResult = Reader->readBytes(RemoteAddress(address), descriptorSize); if (!readResult) return nullptr; - auto descriptor = - reinterpret_cast *>( - readResult.get()); + auto descriptor = reinterpret_cast(readResult.get()); - ShapeCache.insert( - std::make_pair(address, std::move(readResult))); + ShapeCache.insert(std::make_pair(address, std::move(readResult))); return ShapeRef(address, descriptor); } /// Given the address of a context descriptor, attempt to read it. - ContextDescriptorRef - readContextDescriptor(StoredPointer address) { - if (address == 0) + ContextDescriptorRef readContextDescriptor(RemoteAddress remoteAddress) { + if (!remoteAddress) return nullptr; - auto remoteAddress = RemoteAddress(address); auto ptr = Reader->readBytes(remoteAddress, sizeof(TargetContextDescriptor)); if (!ptr) return nullptr; - auto cached = ContextDescriptorCache.find(address); + auto cached = ContextDescriptorCache.find(remoteAddress); if (cached != ContextDescriptorCache.end()) return ContextDescriptorRef( - address, reinterpret_cast *>( - cached->second.get())); + remoteAddress, + reinterpret_cast *>( + cached->second.get())); bool success = false; switch ( @@ -1512,8 +1528,9 @@ class MetadataReader { auto *descriptor = reinterpret_cast *>(ptr.get()); - ContextDescriptorCache.insert(std::make_pair(address, std::move(ptr))); - return ContextDescriptorRef(address, descriptor); + ContextDescriptorCache.insert( + std::make_pair(remoteAddress, std::move(ptr))); + return ContextDescriptorRef(remoteAddress, descriptor); } template @@ -1626,19 +1643,17 @@ class MetadataReader { /// Read a context descriptor from the given address and build a mangling /// tree representing it. Demangle::NodePointer - readDemanglingForContextDescriptor(StoredPointer contextAddress, + readDemanglingForContextDescriptor(RemoteAddress contextAddress, Demangler &Dem) { auto context = readContextDescriptor(contextAddress); if (!context) return nullptr; return buildContextMangling(context, Dem); } - + /// Read the mangled underlying type from an opaque type descriptor. - Demangle::NodePointer - readUnderlyingTypeManglingForOpaqueTypeDescriptor(StoredPointer contextAddr, - unsigned ordinal, - Demangler &Dem) { + Demangle::NodePointer readUnderlyingTypeManglingForOpaqueTypeDescriptor( + RemoteAddress contextAddr, unsigned ordinal, Demangler &Dem) { auto context = readContextDescriptor(contextAddr); if (!context) return nullptr; @@ -1654,13 +1669,12 @@ class MetadataReader { auto nameAddr = resolveRelativeField(context, opaqueType->getUnderlyingTypeArgumentMangledName(ordinal)); - - return readMangledName(RemoteAddress(nameAddr), - MangledNameKind::Type, Dem); + + return readMangledName(nameAddr, MangledNameKind::Type, Dem); } TypeLookupErrorOr - readUnderlyingTypeForOpaqueTypeDescriptor(StoredPointer contextAddr, + readUnderlyingTypeForOpaqueTypeDescriptor(RemoteAddress contextAddr, unsigned ordinal) { Demangle::Demangler Dem; auto node = readUnderlyingTypeManglingForOpaqueTypeDescriptor(contextAddr, @@ -1670,31 +1684,33 @@ class MetadataReader { return decodeMangledType(node); } - bool isTaggedPointer(StoredPointer objectAddress) { + bool isTaggedPointer(RemoteAddress objectAddress) { if (getTaggedPointerEncoding() != TaggedPointerEncodingKind::Extended) return false; - - return (objectAddress ^ TaggedPointerObfuscator) & TaggedPointerMask; + + return (bool)((objectAddress ^ TaggedPointerObfuscator) & + TaggedPointerMask); } /// Read the isa pointer of an Object-C tagged pointer value. - std::optional - readMetadataFromTaggedPointer(StoredPointer objectAddress) { + std::optional + readMetadataFromTaggedPointer(RemoteAddress objectAddress) { auto readArrayElement = - [&](StoredPointer base, - StoredPointer tag) -> std::optional { - StoredPointer addr = base + tag * sizeof(StoredPointer); - StoredPointer isa; - if (!Reader->readInteger(RemoteAddress(addr), &isa)) + [&](RemoteAddress base, + StoredPointer tag) -> std::optional { + RemoteAddress addr = base + tag * sizeof(StoredPointer); + RemoteAddress isa; + if (!Reader->template readRemoteAddress(addr, isa)) return std::nullopt; return isa; }; // Extended pointers have a tag of 0b111, using 8 additional bits // to specify the class. - if (TaggedPointerExtendedMask != 0 && - (((objectAddress ^ TaggedPointerObfuscator) & TaggedPointerExtendedMask) - == TaggedPointerExtendedMask)) { + if (TaggedPointerExtendedMask && + ((((StoredPointer)objectAddress.getRawAddress() ^ + TaggedPointerObfuscator) & + TaggedPointerExtendedMask) == TaggedPointerExtendedMask)) { auto tag = ((objectAddress >> TaggedPointerExtendedSlotShift) & TaggedPointerExtendedSlotMask); return readArrayElement(TaggedPointerExtendedClasses, tag); @@ -1708,13 +1724,13 @@ class MetadataReader { /// Read the isa pointer of a class or closure context instance and apply /// the isa mask. - std::optional - readMetadataFromInstance(StoredPointer objectAddress) { + std::optional + readMetadataFromInstance(RemoteAddress objectAddress) { if (isTaggedPointer(objectAddress)) return readMetadataFromTaggedPointer(objectAddress); - StoredPointer isa; - if (!Reader->readInteger(RemoteAddress(objectAddress), &isa)) + RemoteAddress isa; + if (!Reader->template readRemoteAddress(objectAddress, isa)) return std::nullopt; switch (getIsaEncoding()) { @@ -1731,11 +1747,12 @@ class MetadataReader { case IsaEncodingKind::Indexed: { // If applying the magic mask doesn't give us the magic value, // it's not an indexed isa. - if ((isa & IsaMagicMask) != IsaMagicValue) + if (((StoredPointer)(isa & IsaMagicMask).getRawAddress()) != + IsaMagicValue) return isa; // Extract the index. - auto classIndex = (isa & IsaIndexMask) >> IsaIndexShift; + StoredPointer classIndex = (isa & IsaIndexMask) >> IsaIndexShift; // 0 is never a valid index. if (classIndex == 0) { @@ -1761,10 +1778,10 @@ class MetadataReader { RemoteAddress eltPointer = RemoteAddress(IndexedClassesPointer + classIndex * sizeof(StoredPointer)); - StoredPointer metadataPointer; - if (!Reader->readInteger(eltPointer, &metadataPointer)) { + RemoteAddress metadataPointer; + if (!Reader->template readRemoteAddress(eltPointer, + metadataPointer)) return std::nullopt; - } return metadataPointer; } @@ -1865,7 +1882,7 @@ class MetadataReader { return cls->getClassBoundsAsSwiftSuperclass(); }, - [](StoredPointer objcClassName) + [](RemoteAddress objcClassName) -> std::optional { // We have no ability to look up an ObjC class by name. // FIXME: add a query for this; clients may have a way to do it. @@ -1883,14 +1900,14 @@ class MetadataReader { template std::optional forTypeReference(TypeReferenceKind refKind, - StoredPointer ref, + RemoteAddress ref, const DescriptorFn &descriptorFn, const MetadataFn &metadataFn, const ClassNameFn &classNameFn) { switch (refKind) { case TypeReferenceKind::IndirectTypeDescriptor: { StoredSignedPointer descriptorAddress; - if (!Reader->readInteger(RemoteAddress(ref), &descriptorAddress)) { + if (!Reader->readInteger(ref, &descriptorAddress)) { return std::nullopt; } @@ -1910,8 +1927,8 @@ class MetadataReader { return classNameFn(ref); case TypeReferenceKind::IndirectObjCClass: { - StoredPointer classRef = 0; - if (!Reader->readInteger(RemoteAddress(ref), &classRef)) + RemoteAddress classRef; + if (!Reader->template readRemoteAddress(ref, classRef)) return std::nullopt; auto metadata = readMetadata(classRef); @@ -1927,8 +1944,8 @@ class MetadataReader { /// Read a single generic type argument from a bound generic type /// metadata. - std::optional - readGenericArgFromMetadata(StoredPointer metadata, unsigned index) { + std::optional + readGenericArgFromMetadata(RemoteAddress metadata, unsigned index) { auto Meta = readMetadata(metadata); if (!Meta) return std::nullopt; @@ -1958,9 +1975,9 @@ class MetadataReader { if (index >= generics->getGenericContextHeader().getNumArguments()) return std::nullopt; - StoredPointer genericArgAddress; - if (!Reader->readInteger(RemoteAddress(addressOfGenericArgAddress), - &genericArgAddress)) + RemoteAddress genericArgAddress; + if (!Reader->template readRemoteAddress( + addressOfGenericArgAddress, genericArgAddress)) return std::nullopt; return genericArgAddress; @@ -1968,7 +1985,7 @@ class MetadataReader { /// Given the address of a nominal type descriptor, attempt to resolve /// its nominal type declaration. - BuiltTypeDecl readNominalTypeFromDescriptor(StoredPointer address) { + BuiltTypeDecl readNominalTypeFromDescriptor(RemoteAddress address) { auto descriptor = readContextDescriptor(address); if (!descriptor) return BuiltTypeDecl(); @@ -1977,7 +1994,7 @@ class MetadataReader { } /// Try to read the offset of a tuple element from a tuple metadata. - bool readTupleElementOffset(StoredPointer metadataAddress, unsigned eltIndex, + bool readTupleElementOffset(RemoteAddress metadataAddress, unsigned eltIndex, StoredSize *offset) { // Read the metadata. auto metadata = readMetadata(metadataAddress); @@ -2001,7 +2018,7 @@ class MetadataReader { /// Given a remote pointer to class metadata, attempt to read its superclass. std::optional - readOffsetToFirstCaptureFromMetadata(StoredPointer MetadataAddress) { + readOffsetToFirstCaptureFromMetadata(RemoteAddress MetadataAddress) { auto meta = readMetadata(MetadataAddress); if (!meta || meta->getKind() != MetadataKind::HeapLocalVariable) return std::nullopt; @@ -2010,15 +2027,15 @@ class MetadataReader { return heapMeta->OffsetToFirstCapture; } - std::optional readPointer(StoredPointer address) { - return Reader->readPointer(RemoteAddress(address), sizeof(StoredPointer)); + std::optional readPointer(RemoteAddress address) { + return Reader->readPointer(address, sizeof(StoredPointer)); } - std::optional readResolvedPointerValue(StoredPointer address) { + std::optional readResolvedPointerValue(RemoteAddress address) { if (auto pointer = readPointer(address)) { - if (!pointer->isResolved()) + if (!pointer->getResolvedAddress()) return std::nullopt; - return (StoredPointer)pointer->getResolvedAddress().getAddressData(); + return pointer->getResolvedAddress(); } return std::nullopt; } @@ -2027,13 +2044,13 @@ class MetadataReader { RemoteAbsolutePointer resolvePointerField(RemoteRef base, const U &field) { auto pointerRef = base.getField(field); - return Reader->resolvePointer(RemoteAddress(getAddress(pointerRef)), + return Reader->resolvePointer(getAddress(pointerRef), *pointerRef.getLocalBuffer()); } /// Given a remote pointer to class metadata, attempt to read its superclass. std::optional - readCaptureDescriptorFromMetadata(StoredPointer MetadataAddress) { + readCaptureDescriptorFromMetadata(RemoteAddress MetadataAddress) { auto meta = readMetadata(MetadataAddress); if (!meta || meta->getKind() != MetadataKind::HeapLocalVariable) return std::nullopt; @@ -2043,15 +2060,14 @@ class MetadataReader { } protected: - template - StoredPointer getAddress(RemoteRef base) { - return (StoredPointer)base.getAddressData(); + template + RemoteAddress getAddress(RemoteRef base) { + return base.getRemoteAddress(); } - - template - StoredPointer resolveRelativeField( - RemoteRef base, const Field &field) { - return (StoredPointer)base.resolveRelativeFieldData(field); + + template + RemoteAddress resolveRelativeField(RemoteRef base, const Field &field) { + return base.resolveRelativeFieldData(field); } template @@ -2067,9 +2083,9 @@ class MetadataReader { offset &= ~1u; using SignedPointer = typename std::make_signed::type; - - StoredPointer resultAddress = getAddress(fieldRef) + (SignedPointer)offset; - + + RemoteAddress resultAddress = getAddress(fieldRef) + (SignedPointer)offset; + // Low bit set in the offset indicates that the offset leads to the absolute // address in memory. if (indirect) { @@ -2078,16 +2094,16 @@ class MetadataReader { } return std::nullopt; } - - return RemoteAbsolutePointer("", resultAddress); + + return RemoteAbsolutePointer(resultAddress); } /// Given a pointer to an Objective-C class, try to read its class name. - bool readObjCClassName(StoredPointer classAddress, std::string &className) { + bool readObjCClassName(RemoteAddress classAddress, std::string &className) { // The following algorithm only works on the non-fragile Apple runtime. // Grab the RO-data pointer. This part is not ABI. - StoredPointer roDataPtr = readObjCRODataPtr(classAddress); + RemoteAddress roDataPtr = readObjCRODataPtr(classAddress); if (!roDataPtr) return false; // This is ABI. @@ -2096,18 +2112,19 @@ class MetadataReader { + sizeof(StoredPointer); // Read the name pointer. - StoredPointer namePtr; - if (!Reader->readInteger(RemoteAddress(roDataPtr + OffsetToName), &namePtr)) + RemoteAddress namePtr; + if (!Reader->template readRemoteAddress( + roDataPtr + OffsetToName, namePtr)) return false; // If the name pointer is null, treat that as an error. if (!namePtr) return false; - return Reader->readString(RemoteAddress(namePtr), className); + return Reader->readString(namePtr, className); } - MetadataRef readMetadata(StoredPointer address) { + MetadataRef readMetadata(RemoteAddress address) { auto cached = MetadataCache.find(address); if (cached != MetadataCache.end()) return MetadataRef(address, @@ -2115,7 +2132,7 @@ class MetadataReader { cached->second.get())); StoredPointer KindValue = 0; - if (!Reader->readInteger(RemoteAddress(address), &KindValue)) + if (!Reader->readInteger(address, &KindValue)) return nullptr; switch (getEnumeratedMetadataKind(KindValue)) { @@ -2128,20 +2145,17 @@ class MetadataReader { case MetadataKind::ErrorObject: return _readMetadata(address); case MetadataKind::Existential: { - StoredPointer flagsAddress = address + - sizeof(StoredPointer); + RemoteAddress flagsAddress = address + sizeof(StoredPointer); ExistentialTypeFlags::int_type flagsData; - if (!Reader->readInteger(RemoteAddress(flagsAddress), - &flagsData)) + if (!Reader->readInteger(flagsAddress, &flagsData)) return nullptr; ExistentialTypeFlags flags(flagsData); - StoredPointer numProtocolsAddress = flagsAddress + sizeof(flagsData); + RemoteAddress numProtocolsAddress = flagsAddress + sizeof(flagsData); uint32_t numProtocols; - if (!Reader->readInteger(RemoteAddress(numProtocolsAddress), - &numProtocols)) + if (!Reader->readInteger(numProtocolsAddress, &numProtocols)) return nullptr; // Make sure the number of protocols is reasonable @@ -2162,9 +2176,10 @@ class MetadataReader { case MetadataKind::ExtendedExistential: { // We need to read the shape in order to figure out how large // the generalization arguments are. - StoredPointer shapeAddress = address + sizeof(StoredPointer); - StoredSignedPointer signedShapePtr; - if (!Reader->readInteger(RemoteAddress(shapeAddress), &signedShapePtr)) + RemoteAddress shapeAddress = address + sizeof(StoredPointer); + RemoteAddress signedShapePtr; + if (!Reader->template readRemoteAddress(shapeAddress, + signedShapePtr)) return nullptr; auto shapePtr = stripSignedPointer(signedShapePtr); @@ -2186,7 +2201,7 @@ class MetadataReader { StoredSize flagsValue; auto flagsAddr = address + TargetFunctionTypeMetadata::OffsetToFlags; - if (!Reader->readInteger(RemoteAddress(flagsAddr), &flagsValue)) + if (!Reader->readInteger(flagsAddr, &flagsValue)) return nullptr; auto flags = @@ -2223,8 +2238,7 @@ class MetadataReader { auto numElementsAddress = address + TargetTupleTypeMetadata::getOffsetToNumElements(); StoredSize numElements; - if (!Reader->readInteger(RemoteAddress(numElementsAddress), - &numElements)) + if (!Reader->readInteger(numElementsAddress, &numElements)) return nullptr; auto totalSize = sizeof(TargetTupleTypeMetadata) + numElements * sizeof(TupleTypeMetadata::Element); @@ -2245,7 +2259,7 @@ class MetadataReader { return nullptr; } - StoredPointer + RemoteAddress readAddressOfNominalTypeDescriptor(MetadataRef &metadata, bool skipArtificialSubclasses = false) { switch (metadata->getKind()) { @@ -2253,28 +2267,29 @@ class MetadataReader { auto classMeta = cast(metadata); while (true) { if (!classMeta->isTypeMetadata()) - return 0; + return RemoteAddress(); StoredSignedPointer descriptorAddressSigned = classMeta->getDescriptionAsSignedPointer(); - StoredPointer descriptorAddress = stripSignedPointer(descriptorAddressSigned); + RemoteAddress descriptorAddress = + stripSignedPointer(descriptorAddressSigned); // If this class has a null descriptor, it's artificial, // and we need to skip it upon request. Otherwise, we're done. if (descriptorAddress || !skipArtificialSubclasses) - return static_cast(descriptorAddress); + return descriptorAddress; auto superclassMetadataAddress = stripSignedPointer(classMeta->Superclass); if (!superclassMetadataAddress) - return 0; + return RemoteAddress(); auto superMeta = readMetadata(superclassMetadataAddress); if (!superMeta) - return 0; + return RemoteAddress(); auto superclassMeta = dyn_cast(superMeta); if (!superclassMeta) - return 0; + return RemoteAddress(); classMeta = superclassMeta; metadata = superMeta; @@ -2286,39 +2301,42 @@ class MetadataReader { case MetadataKind::Enum: { auto valueMeta = cast>(metadata); StoredSignedPointer descriptorAddressSigned = valueMeta->getDescriptionAsSignedPointer(); - StoredPointer descriptorAddress = stripSignedPointer(descriptorAddressSigned); + RemoteAddress descriptorAddress = + stripSignedPointer(descriptorAddressSigned); return descriptorAddress; } case MetadataKind::ForeignClass: { auto foreignMeta = cast>(metadata); StoredSignedPointer descriptorAddressSigned = foreignMeta->getDescriptionAsSignedPointer(); - StoredPointer descriptorAddress = stripSignedPointer(descriptorAddressSigned); + RemoteAddress descriptorAddress = + stripSignedPointer(descriptorAddressSigned); return descriptorAddress; } case MetadataKind::ForeignReferenceType: { auto foreignMeta = cast>(metadata); StoredSignedPointer descriptorAddressSigned = foreignMeta->getDescriptionAsSignedPointer(); - StoredPointer descriptorAddress = stripSignedPointer(descriptorAddressSigned); + RemoteAddress descriptorAddress = + stripSignedPointer(descriptorAddressSigned); return descriptorAddress; } default: - return 0; + return RemoteAddress(); } } private: template