diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 20773c55c28..51116f4f6da 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -285,17 +285,20 @@ # utils /utils/*windows* @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/swift-xcodegen/ @hamishknight +/utils/swift_build_support/ @etcwilde @justice-adams-apple @shahmishal /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/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 diff --git a/CHANGELOG.md b/CHANGELOG.md index d2bb92dd347..05c0b540902 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,61 @@ ## Swift 6.2 +* 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 @@ -10732,6 +10787,7 @@ 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-0470]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0470-isolated-conformances.md [#64927]: [#42697]: [#42728]: diff --git a/Runtimes/Core/Concurrency/CMakeLists.txt b/Runtimes/Core/Concurrency/CMakeLists.txt index 8dd89783d7a..4af3a52b0f8 100644 --- a/Runtimes/Core/Concurrency/CMakeLists.txt +++ b/Runtimes/Core/Concurrency/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(swift_Concurrency EmbeddedSupport.cpp Error.cpp ExecutorBridge.cpp + ExecutorImpl.cpp ExecutorChecks.cpp GlobalExecutor.cpp Setup.cpp diff --git a/Runtimes/Core/cmake/modules/AvailabilityMacros.cmake b/Runtimes/Core/cmake/modules/AvailabilityMacros.cmake index b1dd4920c6b..1e82bac48a3 100644 --- a/Runtimes/Core/cmake/modules/AvailabilityMacros.cmake +++ b/Runtimes/Core/cmake/modules/AvailabilityMacros.cmake @@ -1,4 +1,7 @@ -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}\">") diff --git a/Runtimes/Core/core/CMakeLists.txt b/Runtimes/Core/core/CMakeLists.txt index 20df62386e9..64f360ced42 100644 --- a/Runtimes/Core/core/CMakeLists.txt +++ b/Runtimes/Core/core/CMakeLists.txt @@ -208,6 +208,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 diff --git a/Runtimes/Overlay/Android/Android/CMakeLists.txt b/Runtimes/Overlay/Android/Android/CMakeLists.txt new file mode 100644 index 00000000000..414836134e3 --- /dev/null +++ b/Runtimes/Overlay/Android/Android/CMakeLists.txt @@ -0,0 +1,25 @@ + +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 PRIVATE + SwiftAndroid + swiftCore) + +install(TARGETS swiftAndroid + 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 00000000000..8642382b6ef --- /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 00000000000..abe85dee695 --- /dev/null +++ b/Runtimes/Overlay/Android/Math/CMakeLists.txt @@ -0,0 +1,17 @@ + +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 + 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 00000000000..517c7eddd2e --- /dev/null +++ b/Runtimes/Overlay/Android/clang/CMakeLists.txt @@ -0,0 +1,40 @@ + +# 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(FILES + android.modulemap + SwiftAndroidNDK.h + SwiftBionic.h + DESTINATION ${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>/${SwiftOverlay_PLATFORM_SUBDIR}/${SwiftOverlay_ARCH_SUBDIR}) + +install(FILES + posix_filesystem.apinotes + spawn.apinotes + DESTINATION ${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>/apinotes) diff --git a/Runtimes/Overlay/CMakeLists.txt b/Runtimes/Overlay/CMakeLists.txt index 030a1c32658..eef4042942b 100644 --- a/Runtimes/Overlay/CMakeLists.txt +++ b/Runtimes/Overlay/CMakeLists.txt @@ -50,6 +50,9 @@ add_compile_options( "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>") add_subdirectory(clang) +if(ANDROID) + add_subdirectory(Android) +endif() if(WIN32) add_subdirectory(Windows) endif() diff --git a/Runtimes/Resync.cmake b/Runtimes/Resync.cmake index 59d95df91d1..1af913cba58 100644 --- a/Runtimes/Resync.cmake +++ b/Runtimes/Resync.cmake @@ -104,11 +104,35 @@ copy_library_sources("linker-support" "" "Overlay") message(STATUS "Clang[${StdlibSources}/public/ClangOverlays] -> ${CMAKE_CURRENT_LIST_DIR}/Overlay/clang") copy_files(public/ClangOverlays Overlay/clang FILES float.swift.gyb) +# 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 diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift index ae4a34a8524..74b18fc5e8b 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift @@ -105,9 +105,9 @@ import SILBridging 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()) } } @@ -128,47 +128,48 @@ let autodiffClosureSpecialization = FunctionPass(name: "autodiff-closure-special } var remainingSpecializationRounds = 5 - var callerModified = false repeat { + // TODO: Names here are pretty misleading. We are looking for a place where + // the pullback closure is created (so for `partial_apply` instruction). var callSites = gatherCallSites(in: function, context) + guard !callSites.isEmpty else { + return + } - if !callSites.isEmpty { - for callSite in callSites { - var (specializedFunction, alreadyExists) = getOrCreateSpecializedFunction(basedOn: callSite, context) + 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) + if !alreadyExists { + context.notifyNewFunction(function: specializedFunction, derivedFrom: callSite.applyCallee) } - var deadClosures: InstructionWorklist = callSites.reduce(into: InstructionWorklist(context)) { deadClosures, callSite in - callSite.closureArgDescriptors - .map { $0.closure } - .forEach { deadClosures.pushIfNotVisited($0) } - } + rewriteApplyInstruction(using: specializedFunction, callSite: callSite, context) + } - defer { - deadClosures.deinitialize() - } + var deadClosures: InstructionWorklist = callSites.reduce(into: InstructionWorklist(context)) { deadClosures, callSite in + callSite.closureArgDescriptors + .map { $0.closure } + .forEach { deadClosures.pushIfNotVisited($0) } + } - while let deadClosure = deadClosures.pop() { - let isDeleted = context.tryDeleteDeadClosure(closure: deadClosure as! SingleValueInstruction) - if isDeleted { - context.notifyInvalidatedStackNesting() - } - } + defer { + deadClosures.deinitialize() + } - if context.needFixStackNesting { - function.fixStackNesting(context) + while let deadClosure = deadClosures.pop() { + let isDeleted = context.tryDeleteDeadClosure(closure: deadClosure as! SingleValueInstruction) + if isDeleted { + context.notifyInvalidatedStackNesting() } } - callerModified = callSites.count > 0 + if context.needFixStackNesting { + function.fixStackNesting(context) + } + remainingSpecializationRounds -= 1 - } while callerModified && remainingSpecializationRounds > 0 + } while remainingSpecializationRounds > 0 } // =========== Top-level functions ========== // @@ -503,12 +504,6 @@ private func handleApplies(for rootClosure: SingleValueInstruction, callSiteMap: continue } - // 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 - } - if callee.isDefinedExternally { continue } @@ -779,13 +774,13 @@ private extension SpecializationCloner { let clonedRootClosure = builder.cloneRootClosure(representedBy: closureArgDesc, capturedArguments: clonedClosureArgs) - let (finalClonedReabstractedClosure, releasableClonedReabstractedClosures) = + let finalClonedReabstractedClosure = builder.cloneRootClosureReabstractions(rootClosure: closureArgDesc.closure, clonedRootClosure: clonedRootClosure, reabstractedClosure: callSite.appliedArgForClosure(at: closureArgDesc.closureArgIndex)!, origToClonedValueMap: origToClonedValueMap, self.context) - let allClonedReleasableClosures = [clonedRootClosure] + releasableClonedReabstractedClosures + let allClonedReleasableClosures = [ finalClonedReabstractedClosure ]; return (finalClonedReabstractedClosure, allClonedReleasableClosures) } @@ -935,10 +930,9 @@ private extension Builder { func cloneRootClosureReabstractions(rootClosure: Value, clonedRootClosure: Value, reabstractedClosure: Value, origToClonedValueMap: [HashableValue: Value], _ context: FunctionPassContext) - -> (finalClonedReabstractedClosure: SingleValueInstruction, releasableClonedReabstractedClosures: [PartialApplyInst]) + -> SingleValueInstruction { func inner(_ rootClosure: Value, _ clonedRootClosure: Value, _ reabstractedClosure: Value, - _ releasableClonedReabstractedClosures: inout [PartialApplyInst], _ origToClonedValueMap: inout [HashableValue: Value]) -> Value { switch reabstractedClosure { case let reabstractedClosure where reabstractedClosure == rootClosure: @@ -947,7 +941,7 @@ private extension Builder { case let cvt as ConvertFunctionInst: let toBeReabstracted = inner(rootClosure, clonedRootClosure, cvt.fromFunction, - &releasableClonedReabstractedClosures, &origToClonedValueMap) + &origToClonedValueMap) let reabstracted = self.createConvertFunction(originalFunction: toBeReabstracted, resultType: cvt.type, withoutActuallyEscaping: cvt.withoutActuallyEscaping) origToClonedValueMap[cvt] = reabstracted @@ -955,7 +949,7 @@ private extension Builder { case let cvt as ConvertEscapeToNoEscapeInst: let toBeReabstracted = inner(rootClosure, clonedRootClosure, cvt.fromFunction, - &releasableClonedReabstractedClosures, &origToClonedValueMap) + &origToClonedValueMap) let reabstracted = self.createConvertEscapeToNoEscape(originalFunction: toBeReabstracted, resultType: cvt.type, isLifetimeGuaranteed: true) origToClonedValueMap[cvt] = reabstracted @@ -963,7 +957,7 @@ private extension Builder { case let pai as PartialApplyInst: let toBeReabstracted = inner(rootClosure, clonedRootClosure, pai.arguments[0], - &releasableClonedReabstractedClosures, &origToClonedValueMap) + &origToClonedValueMap) guard let function = pai.referencedFunction else { log("Parent function of callSite: \(rootClosure.parentFunction)") @@ -978,13 +972,11 @@ private extension Builder { 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 toBeReabstracted = inner(rootClosure, clonedRootClosure, mdi.value, &origToClonedValueMap) let base = origToClonedValueMap[mdi.base]! let reabstracted = self.createMarkDependence(value: toBeReabstracted, base: base, kind: .Escaping) origToClonedValueMap[mdi] = reabstracted @@ -998,11 +990,10 @@ private extension Builder { } } - var releasableClonedReabstractedClosures: [PartialApplyInst] = [] var origToClonedValueMap = origToClonedValueMap let finalClonedReabstractedClosure = inner(rootClosure, clonedRootClosure, reabstractedClosure, - &releasableClonedReabstractedClosures, &origToClonedValueMap) - return (finalClonedReabstractedClosure as! SingleValueInstruction, releasableClonedReabstractedClosures) + &origToClonedValueMap) + return (finalClonedReabstractedClosure as! SingleValueInstruction) } func destroyPartialApply(pai: PartialApplyInst, _ context: FunctionPassContext){ diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift index b9caf36d8b8..d9abef94fe4 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift @@ -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/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index 1cb0130076f..043a62589f3 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -19,6 +19,7 @@ swift_compiler_sources(Optimizer SimplifyCondBranch.swift SimplifyCondFail.swift SimplifyConvertEscapeToNoEscape.swift + SimplifyCopyBlock.swift SimplifyCopyValue.swift SimplifyDebugStep.swift SimplifyDestroyValue.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCopyBlock.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCopyBlock.swift new file mode 100644 index 00000000000..86463763324 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCopyBlock.swift @@ -0,0 +1,70 @@ +//===--- 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 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 is EndBorrowInst, is DestroyValueInst: + context.erase(instruction: use.instruction) + default: + fatalError("unhandled use") + } + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift index 48c7482a59a..2f0967860bd 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift @@ -114,6 +114,7 @@ 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) }) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift index 40bd631f5d0..0461b09d249 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift @@ -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) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift index d992481da69..761738d761c 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift @@ -930,8 +930,8 @@ extension LifetimeDependenceDefUseWalker { // 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") + // 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: @@ -946,7 +946,6 @@ extension LifetimeDependenceDefUseWalker { case .incomingArgument: fatalError("Incoming arguments are never reachable") } - return .continueWalk } private mutating func visitAppliedUse(of operand: Operand, by apply: FullApplySite) -> WalkResult { diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift index 2736f60ca05..a4972f34f27 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift @@ -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()) } } @@ -226,7 +226,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 +329,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") } } @@ -630,13 +630,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 +651,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 +699,10 @@ extension LocalVariableReachableAccess { continue case .assign: accessStack.push(accessInfo.access) - break case .escape: break } + break } return currentEffect } @@ -708,25 +712,31 @@ 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. /// + /// 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` are /// reachable from `modifyInst`. /// @@ -748,37 +758,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, includeing 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 +807,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 } @@ -835,9 +848,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 +860,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 +889,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 +902,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 +933,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/Test.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift index 101beb34b03..51863d39598 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift @@ -164,6 +164,8 @@ public func registerOptimizerTests() { lifetimeDependenceScopeTest, lifetimeDependenceUseTest, linearLivenessTest, + localVariableReachableUsesTest, + localVariableReachingAssignmentsTest, parseTestSpecificationTest, variableIntroducerTest, gatherCallSitesTest, diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index c0234cc6285..50f6c4de45e 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -1071,7 +1071,7 @@ class ThinToThickFunctionInst : SingleValueInstruction, UnaryInstruction { final public class ThickToObjCMetatypeInst : SingleValueInstruction {} final public class ObjCToThickMetatypeInst : SingleValueInstruction {} -final public class CopyBlockInst : SingleValueInstruction {} +final public class CopyBlockInst : SingleValueInstruction, UnaryInstruction {} final public class CopyBlockWithoutEscapingInst : SingleValueInstruction {} final public diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 9dc517f62b6..f219fde451b 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -965,6 +965,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/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 1dba89461de..ea37b36afc9 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -761,7 +761,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 diff --git a/docs/HowToGuides/GettingStarted.md b/docs/HowToGuides/GettingStarted.md index 79cf5f20ab0..dd5da288211 100644 --- a/docs/HowToGuides/GettingStarted.md +++ b/docs/HowToGuides/GettingStarted.md @@ -164,6 +164,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) diff --git a/include/swift/ABI/Task.h b/include/swift/ABI/Task.h index cdc61926763..1961ec25149 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; @@ -305,9 +316,10 @@ public: #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); char Storage[PrivateStorageSize]; diff --git a/include/swift/ABI/TaskGroup.h b/include/swift/ABI/TaskGroup.h index b1c821d8e1c..03f7e101cce 100644 --- a/include/swift/ABI/TaskGroup.h +++ b/include/swift/ABI/TaskGroup.h @@ -44,6 +44,12 @@ public: /// 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/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index f3b7220761f..4273dcae8e4 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -910,16 +910,6 @@ 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, @@ -1235,11 +1225,18 @@ BridgedNonSendableAttr BridgedNonSendableAttr_createParsed( BridgedASTContext cContext, BridgedSourceLoc cAtLoc, BridgedSourceRange cRange, BridgedNonSendableKind cKind); -SWIFT_NAME("BridgedNonisolatedAttr.createParsed(_:atLoc:range:isUnsafe:)") +enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedNonIsolatedModifier { + BridgedNonIsolatedModifierNone, + BridgedNonIsolatedModifierUnsafe, + BridgedNonIsolatedModifierNonSending +}; + +SWIFT_NAME("BridgedNonisolatedAttr.createParsed(_:atLoc:range:modifier:)") BridgedNonisolatedAttr BridgedNonisolatedAttr_createParsed(BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, bool isUnsafe); + BridgedSourceRange cRange, + BridgedNonIsolatedModifier modifier); SWIFT_NAME("BridgedObjCAttr.createParsedUnnamed(_:atLoc:attrNameLoc:)") BridgedObjCAttr @@ -2590,11 +2587,6 @@ BridgedConventionTypeAttr BridgedConventionTypeAttr_createParsed( BridgedSourceLoc cNameLoc, BridgedDeclNameRef cWitnessMethodProtocol, BridgedStringRef cClangType, BridgedSourceLoc cClangTypeLoc); -enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedExecutionTypeAttrExecutionKind { - BridgedExecutionTypeAttrExecutionKind_Concurrent, - BridgedExecutionTypeAttrExecutionKind_Caller -}; - SWIFT_NAME("BridgedDifferentiableTypeAttr.createParsed(_:atLoc:nameLoc:" "parensRange:kind:kindLoc:)") BridgedDifferentiableTypeAttr BridgedDifferentiableTypeAttr_createParsed( @@ -2602,14 +2594,6 @@ BridgedDifferentiableTypeAttr BridgedDifferentiableTypeAttr_createParsed( 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); - SWIFT_NAME("BridgedIsolatedTypeAttr.createParsed(_:atLoc:nameLoc:parensRange:" "isolationKind:isolationKindLoc:)") BridgedIsolatedTypeAttr BridgedIsolatedTypeAttr_createParsed( @@ -2777,6 +2761,12 @@ BridgedSendingTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr base, BridgedSourceLoc cSpecifierLoc); +SWIFT_NAME("BridgedCallerIsolatedTypeRepr.createParsed(_:base:specifierLoc:)") +BridgedCallerIsolatedTypeRepr +BridgedCallerIsolatedTypeRepr_createParsed(BridgedASTContext cContext, + BridgedTypeRepr base, + BridgedSourceLoc cSpecifierLoc); + SWIFT_NAME( "BridgedTupleTypeRepr.createParsed(_:elements:leftParenLoc:rightParenLoc:)") BridgedTupleTypeRepr BridgedTupleTypeRepr_createParsed( diff --git a/include/swift/AST/ASTContextGlobalCache.h b/include/swift/AST/ASTContextGlobalCache.h index 1925fe7885f..a57e5bc249f 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; diff --git a/include/swift/AST/ASTPrinter.h b/include/swift/AST/ASTPrinter.h index 9a8accb07bf..00fee2981d8 100644 --- a/include/swift/AST/ASTPrinter.h +++ b/include/swift/AST/ASTPrinter.h @@ -413,7 +413,8 @@ public: 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); diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index e2b37f7f4b2..c92c236a063 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -226,8 +226,8 @@ protected: isEarlyAdopter : 1 ); - SWIFT_INLINE_BITFIELD(NonisolatedAttr, DeclAttribute, 1, - isUnsafe : 1 + SWIFT_INLINE_BITFIELD(NonisolatedAttr, DeclAttribute, NumNonIsolatedModifierBits, + Modifier : NumNonIsolatedModifierBits ); SWIFT_INLINE_BITFIELD_FULL(AllowFeatureSuppressionAttr, DeclAttribute, 1+31, @@ -236,10 +236,6 @@ protected: NumFeatures : 31 ); - - SWIFT_INLINE_BITFIELD(ExecutionAttr, DeclAttribute, NumExecutionKindBits, - Behavior : NumExecutionKindBits - ); } Bits; // clang-format on @@ -2985,17 +2981,28 @@ public: /// 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"); } - NonisolatedAttr(bool unsafe, bool implicit) - : NonisolatedAttr({}, {}, unsafe, implicit) {} + NonIsolatedModifier getModifier() const { + return static_cast(Bits.NonisolatedAttr.Modifier); + } - bool isUnsafe() const { return Bits.NonisolatedAttr.isUnsafe; } + bool isUnsafe() const { return getModifier() == NonIsolatedModifier::Unsafe; } + bool isNonSending() const { + return getModifier() == NonIsolatedModifier::NonSending; + } + + 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; @@ -3003,11 +3010,11 @@ public: /// 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(); } }; @@ -3282,34 +3289,6 @@ public: } }; -class ExecutionAttr : public DeclAttribute { -public: - ExecutionAttr(SourceLoc AtLoc, SourceRange Range, - ExecutionKind behavior, - bool Implicit) - : DeclAttribute(DeclAttrKind::Execution, AtLoc, Range, Implicit) { - Bits.ExecutionAttr.Behavior = static_cast(behavior); - } - - ExecutionAttr(ExecutionKind behavior, bool Implicit) - : ExecutionAttr(/*AtLoc=*/SourceLoc(), /*Range=*/SourceRange(), behavior, - Implicit) {} - - ExecutionKind getBehavior() const { - return static_cast(Bits.ExecutionAttr.Behavior); - } - - static bool classof(const DeclAttribute *DA) { - return DA->getKind() == DeclAttrKind::Execution; - } - - UNIMPLEMENTED_CLONE(ExecutionAttr) - - bool isEquivalent(const ExecutionAttr *other, Decl *attachedTo) const { - return getBehavior() == other->getBehavior(); - } -}; - /// Attributes that may be applied to declarations. class DeclAttributes { /// Linked list of declaration attributes. @@ -3769,10 +3748,6 @@ protected: SWIFT_INLINE_BITFIELD_FULL(IsolatedTypeAttr, TypeAttribute, 8, Kind : 8 ); - - SWIFT_INLINE_BITFIELD_FULL(ExecutionTypeAttr, TypeAttribute, 8, - Behavior : 8 - ); } Bits; // clang-format on @@ -4044,28 +4019,6 @@ public: 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 d56edbc4b6e..7ac94fb07c0 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.h @@ -130,14 +130,17 @@ enum class ExternKind: uint8_t { enum : unsigned { NumExternKindBits = countBitsUsed(static_cast(ExternKind::Last_ExternKind)) }; -enum class ExecutionKind : uint8_t { - Concurrent = 0, - Caller, - Last_ExecutionKind = Caller +enum class NonIsolatedModifier : uint8_t { + None = 0, + Unsafe, + NonSending, + Last_NonIsolatedModifier = NonSending }; -enum : unsigned { NumExecutionKindBits = - countBitsUsed(static_cast(ExecutionKind::Last_ExecutionKind)) }; +enum : unsigned { + NumNonIsolatedModifierBits = countBitsUsed( + static_cast(NonIsolatedModifier::Last_NonIsolatedModifier)) +}; enum class DeclAttrKind : unsigned { #define DECL_ATTR(_, CLASS, ...) CLASS, diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 2172b2f29ac..a9c63f66bc9 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6902,6 +6902,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. @@ -7190,6 +7193,18 @@ public: 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); @@ -8151,8 +8166,6 @@ public: 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 diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index d85281b5b3b..52f1051dd02 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -863,11 +863,7 @@ DECL_ATTR(abi, ABI, 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, @@ -885,7 +881,12 @@ SIMPLE_DECL_ATTR(extensible, Extensible, ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | ForbiddenInABIAttr, 169) -LAST_DECL_ATTR(Extensible) +SIMPLE_DECL_ATTR(concurrent, Concurrent, + OnFunc | OnConstructor | OnSubscript | OnVar, + ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, + 170) + +LAST_DECL_ATTR(Concurrent) #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index e7d6ba6adcc..06c64b5177e 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1700,14 +1700,6 @@ 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", ()) ERROR(attr_private_import_expected_sourcefile, none, @@ -2172,5 +2164,18 @@ 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", ()) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index f2e9c2450b4..694a6c2d389 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -1137,12 +1137,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 diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 62b8218cc55..30591ed5957 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1265,6 +1265,10 @@ REMARK(macro_loaded,none, "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'", @@ -1980,10 +1984,19 @@ 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 %kindonly1 declared in header", @@ -8524,10 +8537,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)) @@ -8548,43 +8557,79 @@ GROUPED_ERROR(isolated_conformance_wrong_domain,IsolatedConformances,none, (ActorIsolation, Type, DeclName, ActorIsolation)) //===----------------------------------------------------------------------===// -// MARK: @execution Attribute +// MARK: @concurrent and nonisolated(nonsending) attributes //===----------------------------------------------------------------------===// -ERROR(attr_execution_only_on_async,none, - "cannot use '@execution' on non-async %kind0", - (ValueDecl *)) +ERROR(execution_behavior_only_on_async,none, + "cannot use %0 on non-async %kind1", + (DeclAttribute, ValueDecl *)) -ERROR(attr_execution_only_on_async_closure,none, - "cannot use '@execution' on non-async closure", - ()) +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_only_on_async,none, - "cannot use '@execution' on non-async function type", - ()) +ERROR(execution_behavior_only_on_async_closure,none, + "cannot use %0 on non-async closure", + (DeclAttribute)) -ERROR(attr_execution_incompatible_isolated_parameter,none, - "cannot use '@execution' on %kind0 because it has " - "an isolated parameter: %1", - (ValueDecl *, ValueDecl *)) +ERROR(execution_behavior_type_attr_only_on_async,none, + "cannot use '@%0' on non-async function type", + (StringRef)) -ERROR(attr_execution_incompatible_dynamically_isolated_parameter,none, - "cannot use '@execution' on %kind0 because it has " - "a dynamically isolated parameter: %1", - (ValueDecl *, ValueDecl *)) +ERROR(nonisolated_nonsending_only_on_function_types, none, + "%0 may only be used on function types", + (TypeRepr *)) -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(nonisolated_nonsending_only_on_async,none, + "cannot use %0 on non-async function type", + (TypeRepr *)) -ERROR(attr_execution_type_attr_incompatible_with_isolated_param,none, - "cannot use '@execution' together with an isolated parameter", - ()) +ERROR(cannot_use_nonisolated_nonsending_together_with_concurrent,none, + "cannot use %0 together with '@concurrent'", + (TypeRepr *)) -ERROR(attr_execution_type_attr_incompatible_with_isolated_any,none, - "cannot use '@execution' together with @isolated(any)", - ()) +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_incompatible_dynamically_isolated_parameter,none, + "cannot use %0 on %kind1 because it has " + "a dynamically 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 " diff --git a/include/swift/AST/ExistentialLayout.h b/include/swift/AST/ExistentialLayout.h index 706e6341379..7e4fa5e7518 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; } diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 7515022f3dc..5579cf818cd 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -1449,23 +1449,25 @@ public: }; 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 { - return paramDecl; + GenericTypeParamDecl *getParamDecl() const; + + TypeRepr *getRepr() const { + return repr; } /// Retrieves the corresponding parameter type of the value referenced by this @@ -1480,9 +1482,7 @@ public: this->paramType = paramType; } - SourceRange getSourceRange() const { - return loc.getSourceRange(); - } + SourceRange getSourceRange() const; static bool classof(const Expr *E) { return E->getKind() == ExprKind::TypeValue; diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index cca69049659..32640376288 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -376,6 +376,19 @@ public: /// 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; diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 73e3b444855..c4651036541 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -127,6 +127,7 @@ IDENTIFIER(main) IDENTIFIER_WITH_NAME(MainEntryPoint, "$main") IDENTIFIER(message) IDENTIFIER(next) +IDENTIFIER(nonsending) IDENTIFIER_(nsErrorDomain) IDENTIFIER(objectAtIndexedSubscript) IDENTIFIER(objectForKeyedSubscript) diff --git a/include/swift/AST/PluginLoader.h b/include/swift/AST/PluginLoader.h index fdcd73f13ba..576d88e5b36 100644 --- a/include/swift/AST/PluginLoader.h +++ b/include/swift/AST/PluginLoader.h @@ -52,10 +52,17 @@ private: /// 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/PrintOptions.h b/include/swift/AST/PrintOptions.h index 8c2773f282e..37a643e0231 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -143,6 +143,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 +175,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; @@ -397,9 +404,6 @@ struct PrintOptions { /// 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, diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index 2c18a09d218..4b75884d901 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -338,6 +338,10 @@ public: // 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 ff697f5ef49..1d32b1625bd 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -510,6 +510,9 @@ public: /// Scanner Prefix Mapper. std::vector ScannerPrefixMapper; + /// Verify resolved plugin is not changed. + bool ResolvedPluginVerification = false; + /// When set, don't validate module system dependencies. /// /// If a system header is modified and this is not set, the compiler will diff --git a/include/swift/AST/TypeAttr.def b/include/swift/AST/TypeAttr.def index 811e85a08d8..4defd43cf1c 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) diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index 749cf721902..e4cde7d2d0b 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -1249,6 +1249,35 @@ public: 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) const; + friend class TypeRepr; +}; + /// A TypeRepr for a known, fixed type. /// /// Fixed type representations should be used sparingly, in places @@ -1680,6 +1709,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 a1f7e51ded3..6a98b8f4dd3 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/Types.h b/include/swift/AST/Types.h index 7dc36c98bcb..2406fbf5e8b 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -5452,6 +5452,10 @@ public: 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 { @@ -5656,6 +5660,17 @@ public: /// 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). diff --git a/include/swift/Basic/Casting.h b/include/swift/Basic/Casting.h new file mode 100644 index 00000000000..925afdbb45f --- /dev/null +++ b/include/swift/Basic/Casting.h @@ -0,0 +1,51 @@ +//===--- 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"); + + Destination destination; + memcpy(&destination, &source, sizeof(source)); + return destination; +} + +} + +#endif diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 40728fabd6e..6f11e0b7023 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -254,6 +254,9 @@ SUPPRESSIBLE_LANGUAGE_FEATURE(MemorySafetyAttributes, 458, "@unsafe attribute") 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") +LANGUAGE_FEATURE(ValueGenericsNameLookup, 452, "Value generics appearing as static members for namelookup") // Swift 6 UPCOMING_FEATURE(ConciseMagicFile, 274, 6) @@ -276,6 +279,7 @@ UPCOMING_FEATURE(GlobalActorIsolatedTypesUsability, 0434, 6) ADOPTABLE_UPCOMING_FEATURE(ExistentialAny, 335, 7) UPCOMING_FEATURE(InternalImportsByDefault, 409, 7) UPCOMING_FEATURE(MemberImportVisibility, 444, 7) +UPCOMING_FEATURE(InferIsolatedConformances, 470, 7) // Optional language features / modes @@ -457,7 +461,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) @@ -467,7 +471,7 @@ EXPERIMENTAL_FEATURE(ImportNonPublicCxxMembers, true) /// Synthesize static factory methods for C++ foreign reference types and import /// them as Swift initializers. -EXPERIMENTAL_FEATURE(CXXForeignReferenceTypeInitializers, true) +EXPERIMENTAL_FEATURE(SuppressCXXForeignReferenceTypeInitializers, true) // Isolated deinit SUPPRESSIBLE_LANGUAGE_FEATURE(IsolatedDeinit, 371, "isolated deinit") @@ -484,10 +488,6 @@ 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) @@ -495,12 +495,6 @@ ADOPTABLE_EXPERIMENTAL_FEATURE(AsyncCallerExecution, false) /// Allow custom availability domains to be defined and referenced. EXPERIMENTAL_FEATURE(CustomAvailability, 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) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 01e0162a9a5..8a73b092900 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -409,10 +409,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; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 40e071a8fac..c73c48622d2 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -1175,6 +1175,12 @@ 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.">; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 66457263298..0f5b00ad5c0 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -998,16 +998,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]>, @@ -1774,6 +1764,16 @@ def symbol_graph_minimum_access_level: Separate<["-"], "symbol-graph-minimum-acc 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 +1788,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]>, @@ -2263,6 +2273,10 @@ def load_resolved_plugin: "and exectuable 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/Parse/Parser.h b/include/swift/Parse/Parser.h index f834cb84ce9..6da0b8a8788 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1169,6 +1169,8 @@ public: Tok.isContextualKeyword("isolated") || Tok.isContextualKeyword("_const")) return true; + if (isCallerIsolatedSpecifier()) + return true; if (Context.LangOpts.hasFeature(Feature::SendingArgsAndResults) && Tok.isContextualKeyword("sending")) return true; @@ -1187,6 +1189,12 @@ public: (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 @@ -1421,6 +1429,7 @@ public: SourceLoc IsolatedLoc; SourceLoc ConstLoc; SourceLoc SendingLoc; + SourceLoc CallerIsolatedLoc; SmallVector Attributes; LifetimeEntry *lifetimeEntry = nullptr; @@ -1723,6 +1732,10 @@ public: /// 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/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index 57cddec85d9..e87ff57ca4e 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -45,17 +45,6 @@ #define SWIFT_CONCURRENCY_ENABLE_DISPATCH 0 #endif -// 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 DefaultActor; class TaskOptionRecord; @@ -1080,7 +1069,7 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) void swift_task_startOnMainActor(AsyncTask* job); SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -void swift_task_startSynchronously(AsyncTask* job); +void swift_task_startSynchronously(AsyncTask* job, SerialExecutorRef targetExecutor); /// Donate this thread to the global executor until either the /// given condition returns true or we've run out of cooperative diff --git a/include/swift/Runtime/STLCompatibility.h b/include/swift/Runtime/STLCompatibility.h deleted file mode 100644 index e2c5aa27cff..00000000000 --- a/include/swift/Runtime/STLCompatibility.h +++ /dev/null @@ -1,45 +0,0 @@ -//===---- STLCompatibility.h - Runtime C++ Compatibiltiy Stubs --*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_RUNTIME_STL_COMPATIBILITY_H -#define SWIFT_RUNTIME_STL_COMPATIBILITY_H - -#if __cplusplus >= 202002l || defined(__cpp_lib_bit_cast) -#include -#else -#include -#include -#include -#include - -namespace std { -inline namespace __swift { -template -std::enable_if_t && - std::is_trivially_copyable_v, Destination> -bit_cast(const Source &src) noexcept { - static_assert(std::is_trivially_constructible_v, - "The destination type must be trivially constructible"); - Destination dst; - if constexpr (std::is_pointer_v || std::is_pointer_v) - std::memcpy(reinterpret_cast(&dst), - reinterpret_cast(&src), sizeof(Destination)); - else - std::memcpy(&dst, &src, sizeof(Destination)); - return dst; -} -} -} -#endif - -#endif diff --git a/include/swift/SIL/OSSALifetimeCompletion.h b/include/swift/SIL/OSSALifetimeCompletion.h index 89a4fa025ee..73d23804c97 100644 --- a/include/swift/SIL/OSSALifetimeCompletion.h +++ b/include/swift/SIL/OSSALifetimeCompletion.h @@ -43,34 +43,42 @@ public: enum HandleTrivialVariable_t { IgnoreTrivialVariable, ExtendTrivialVariable }; private: - // If domInfo is nullptr, then InteriorLiveness never assumes dominance. As a - // result it may report extra unenclosedPhis. In that case, any attempt to - // create a new phi would result in an immediately redundant phi. + /// If domInfo is nullptr, then InteriorLiveness never assumes dominance. As a + /// result it may report extra unenclosedPhis. In that case, any attempt to + /// create a new phi would result in an immediately redundant phi. const DominanceInfo *domInfo = nullptr; DeadEndBlocks &deadEndBlocks; - // Cache intructions already handled by the recursive algorithm to avoid - // recomputing their lifetimes. + /// Cache intructions already handled by the recursive algorithm to avoid + /// recomputing their lifetimes. ValueSet completedValues; - // Extend trivial variables for lifetime diagnostics (only in SILGenCleanup). + /// Extend trivial variables for lifetime diagnostics (only in SILGenCleanup). HandleTrivialVariable_t handleTrivialVariable; -public: - OSSALifetimeCompletion(SILFunction *function, const DominanceInfo *domInfo, - DeadEndBlocks &deadEndBlocks, - HandleTrivialVariable_t handleTrivialVariable = IgnoreTrivialVariable) - : domInfo(domInfo), deadEndBlocks(deadEndBlocks), - completedValues(function), handleTrivialVariable(handleTrivialVariable) {} + /// Whether verification of the computed liveness should be run even when the + /// global setting is off. + /// TODO: Remove this option. + bool ForceLivenessVerification; - // The kind of boundary at which to complete the lifetime. - // - // Liveness: "As early as possible." Consume the value after the last - // non-consuming uses. - // Availability: "As late as possible." Consume the value in the last blocks - // beyond the non-consuming uses in which the value has been - // consumed on no incoming paths. +public: + OSSALifetimeCompletion( + SILFunction *function, const DominanceInfo *domInfo, + DeadEndBlocks &deadEndBlocks, + HandleTrivialVariable_t handleTrivialVariable = IgnoreTrivialVariable, + bool forceLivenessVerification = false) + : domInfo(domInfo), deadEndBlocks(deadEndBlocks), + completedValues(function), handleTrivialVariable(handleTrivialVariable), + ForceLivenessVerification(forceLivenessVerification) {} + + /// The kind of boundary at which to complete the lifetime. + /// + /// Liveness: "As early as possible." Consume the value after the last + /// non-consuming uses. + /// Availability: "As late as possible." Consume the value in the last blocks + /// beyond the non-consuming uses in which the value has been + /// consumed on no incoming paths. struct Boundary { enum Value : uint8_t { Liveness, diff --git a/include/swift/SILOptimizer/Analysis/RegionAnalysis.h b/include/swift/SILOptimizer/Analysis/RegionAnalysis.h index f21ccd2b313..7b81e1a3356 100644 --- a/include/swift/SILOptimizer/Analysis/RegionAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/RegionAnalysis.h @@ -108,6 +108,7 @@ private: class TrackableValue; class TrackableValueState; +struct TrackableValueLookupResult; enum class TrackableValueFlag { /// Base value that says a value is uniquely represented and is @@ -272,6 +273,25 @@ public: } }; +/// A class that contains both a lookup value as well as extra metadata about +/// properties of the original value that we looked up up from that we +/// discovered as we searched for the lookup value. +struct regionanalysisimpl::TrackableValueLookupResult { + /// The actual value that we are tracking. + /// + /// If we are tracking a Sendable address that has a non-Sendable base, this + /// will be an empty TrackableValue. + TrackableValue value; + + /// If we are tracking an address, this is the base trackable value that is + /// being tracked. If the base is a Sendable value, then this will be an empty + /// TrackableValue. + std::optional base; + + void print(llvm::raw_ostream &os) const; + SWIFT_DEBUG_DUMP { print(llvm::dbgs()); } +}; + class RegionAnalysis; class RegionAnalysisValueMap { @@ -282,6 +302,8 @@ public: using Region = PartitionPrimitives::Region; using TrackableValue = regionanalysisimpl::TrackableValue; using TrackableValueState = regionanalysisimpl::TrackableValueState; + using TrackableValueLookupResult = + regionanalysisimpl::TrackableValueLookupResult; private: /// A map from the representative of an equivalence class of values to their @@ -301,30 +323,57 @@ private: /// State that the value -> representative computation yields to us. struct UnderlyingTrackedValueInfo { + /// The equivalence class value that we found that should be merged into + /// regions. + /// + /// Always set to a real value. SILValue value; - /// Only used for addresses. - std::optional actorIsolation; + /// The actual base value that we found if we were looking for an address + /// equivilance class and had a non-Sendable base. If we have an object or + /// we do not have a separate base, this is SILValue(). + SILValue base; - explicit UnderlyingTrackedValueInfo(SILValue value) : value(value) {} + /// Constructor for use if we only have either an object or an address + /// equivalence class that involves a complete non-Sendable path. + explicit UnderlyingTrackedValueInfo(SILValue value) : value(value), base() { + assert(value); + } - UnderlyingTrackedValueInfo() : value(), actorIsolation() {} + /// Constructor for use with addresses only where we have either: + /// + /// 1. A sendable address that is used but that has a non-Sendable base that + /// we have to insert requires for. + /// + /// 2. A non-Sendable address that is used but that has a separate + /// non-Sendable base due to an access path chain that has a split in + /// between the two due to the non-Sendable address being projected out of + /// an intervening sendable struct. The struct can be Sendable due to things + /// like being global actor isolated or by being marked @unchecked Sendable. + explicit UnderlyingTrackedValueInfo(SILValue value, SILValue base) + : value(value), base(base) { + assert(value); + assert(base); + } + UnderlyingTrackedValueInfo() : value(), base() {} UnderlyingTrackedValueInfo(const UnderlyingTrackedValueInfo &newVal) - : value(newVal.value), actorIsolation(newVal.actorIsolation) {} + : value(newVal.value), base(newVal.base) {} UnderlyingTrackedValueInfo & operator=(const UnderlyingTrackedValueInfo &newVal) { value = newVal.value; - actorIsolation = newVal.actorIsolation; + base = newVal.base; return *this; } - UnderlyingTrackedValueInfo(SILValue value, - std::optional actorIsolation) - : value(value), actorIsolation(actorIsolation) {} - operator bool() const { return value; } + + void print(llvm::raw_ostream &os) const; + SWIFT_DEBUG_DUMP { + print(llvm::dbgs()); + llvm::dbgs() << '\n'; + } }; /// A map from a SILValue to its equivalence class representative. @@ -363,10 +412,16 @@ public: void print(llvm::raw_ostream &os) const; SWIFT_DEBUG_DUMP { print(llvm::dbgs()); } - TrackableValue + TrackableValueLookupResult getTrackableValue(SILValue value, bool isAddressCapturedByPartialApply = false) const; +private: + TrackableValue + getTrackableValueHelper(SILValue value, + bool isAddressCapturedByPartialApply = false) const; + +public: /// An actor introducing inst is an instruction that doesn't have any /// non-Sendable parameters and produces a new value that has to be actor /// isolated. @@ -378,7 +433,8 @@ public: private: std::optional getValueForId(Element id) const; - std::optional tryToTrackValue(SILValue value) const; + std::optional + tryToTrackValue(SILValue value) const; TrackableValue getActorIntroducingRepresentative(SILInstruction *introducingInst, SILIsolationInfo isolation) const; @@ -400,6 +456,15 @@ private: UnderlyingTrackedValueInfo getUnderlyingTrackedValueHelper(SILValue value) const; + /// A helper function that performs the actual getUnderlyingTrackedValue + /// computation that is cached in getUnderlyingTrackedValue(). Please never + /// call this directly! Only call it from getUnderlyingTrackedValue. + UnderlyingTrackedValueInfo + getUnderlyingTrackedValueHelperObject(SILValue value) const; + + UnderlyingTrackedValueInfo + getUnderlyingTrackedValueHelperAddress(SILValue value) const; + UnderlyingTrackedValueInfo getUnderlyingTrackedValue(SILValue value) const { // Use try_emplace so we only construct underlying tracked value info on // success and only lookup once in the hash table. diff --git a/include/swift/SILOptimizer/Utils/PartitionUtils.h b/include/swift/SILOptimizer/Utils/PartitionUtils.h index 201441aaefc..d0f99cd2d5c 100644 --- a/include/swift/SILOptimizer/Utils/PartitionUtils.h +++ b/include/swift/SILOptimizer/Utils/PartitionUtils.h @@ -434,6 +434,16 @@ enum class PartitionOpKind : uint8_t { /// tryToTrackValue and SILIsolationInfo::get(). AssignFresh, + /// Assign one value to a fresh region and then assign it to another value + /// that was also just assign fresh. Takes two parameters. The first is the + /// element to assign fresh and the second is the element to assign to. + /// + /// Used in combination with AssignFresh to initialize a chain of + /// values. Different from Assign since Assign allows for errors to + /// occur. This does not allow for any errors to be emitted since we are just + /// initializing a chain of values. + AssignFreshAssign, + /// Merge the regions of two values, takes two args, both must be from /// non-sent regions. Merge, @@ -483,39 +493,43 @@ enum class PartitionOpKind : uint8_t { class PartitionOp { using Element = PartitionPrimitives::Element; -private: - PartitionOpKind opKind; - llvm::SmallVector opArgs; +public: + enum class Flag : uint8_t { + None, + /// This is a require of a non-Sendable base that we have a Sendable use + /// from. If the region was sent but at the sent point did not have any + /// element of the region that was captured by reference in a closure, we + /// can ignore the use. + RequireOfMutableBaseOfSendableValue, + }; + using Options = OptionSet; + +private: /// Record the SILInstruction that this PartitionOp was generated from, if /// generated during compilation from a SILBasicBlock PointerUnion source; + std::optional opArg1; + std::optional opArg2; + + PartitionOpKind opKind; + + Options options; + // TODO: can the following declarations be merged? PartitionOp(PartitionOpKind opKind, Element arg1, - SILInstruction *sourceInst = nullptr) - : opKind(opKind), opArgs({arg1}), source(sourceInst) { + SILInstruction *sourceInst = nullptr, Options options = {}) + : source(sourceInst), opArg1(arg1), opKind(opKind), options(options) { assert(((opKind != PartitionOpKind::Send && opKind != PartitionOpKind::UndoSend) || sourceInst) && "Send needs a sourceInst"); } - template - PartitionOp(PartitionOpKind opKind, T collectionOfIndices, - SILInstruction *sourceInst = nullptr) - : opKind(opKind), opArgs(), source(sourceInst) { - assert(((opKind != PartitionOpKind::Send && - opKind != PartitionOpKind::UndoSend) || - sourceInst) && - "Send needs a sourceInst"); - for (Element elt : collectionOfIndices) { - opArgs.push_back(elt); - } - } - - PartitionOp(PartitionOpKind opKind, Element arg1, Operand *sourceOperand) - : opKind(opKind), opArgs({arg1}), source(sourceOperand) { + PartitionOp(PartitionOpKind opKind, Element arg1, Operand *sourceOperand, + Options options = {}) + : source(sourceOperand), opArg1(arg1), opKind(opKind), options(options) { assert(((opKind != PartitionOpKind::Send && opKind != PartitionOpKind::UndoSend) || bool(sourceOperand)) && @@ -523,8 +537,9 @@ private: } PartitionOp(PartitionOpKind opKind, Element arg1, Element arg2, - SILInstruction *sourceInst = nullptr) - : opKind(opKind), opArgs({arg1, arg2}), source(sourceInst) { + SILInstruction *sourceInst = nullptr, Options options = {}) + : source(sourceInst), opArg1(arg1), opArg2(arg2), opKind(opKind), + options(options) { assert(((opKind != PartitionOpKind::Send && opKind != PartitionOpKind::UndoSend) || sourceInst) && @@ -532,15 +547,17 @@ private: } PartitionOp(PartitionOpKind opKind, Element arg1, Element arg2, - Operand *sourceOp = nullptr) - : opKind(opKind), opArgs({arg1, arg2}), source(sourceOp) { + Operand *sourceOp = nullptr, Options options = {}) + : source(sourceOp), opArg1(arg1), opArg2(arg2), opKind(opKind), + options(options) { assert((opKind == PartitionOpKind::Assign || opKind == PartitionOpKind::Merge) && "Only supported for assign and merge"); } - PartitionOp(PartitionOpKind opKind, SILInstruction *sourceInst) - : opKind(opKind), opArgs(), source(sourceInst) {} + PartitionOp(PartitionOpKind opKind, SILInstruction *sourceInst, + Options options = {}) + : source(sourceInst), opKind(opKind), options(options) {} friend class Partition; @@ -550,10 +567,19 @@ public: return PartitionOp(PartitionOpKind::Assign, destElt, srcElt, srcOperand); } - template - static PartitionOp AssignFresh(T collection, + static PartitionOp AssignFresh(Element elt, SILInstruction *sourceInst = nullptr) { - return PartitionOp(PartitionOpKind::AssignFresh, collection, sourceInst); + return PartitionOp(PartitionOpKind::AssignFresh, elt, sourceInst); + } + + /// Assign fresh \p elt and then assign it to \p srcElt. + /// + /// Used as part of emitting a sequence of AssignFresh that join the same + /// element. + static PartitionOp AssignFreshAssign(Element elt, Element srcElt, + SILInstruction *sourceInst = nullptr) { + return PartitionOp(PartitionOpKind::AssignFreshAssign, elt, srcElt, + sourceInst); } static PartitionOp Send(Element tgt, Operand *sendingOp) { @@ -570,9 +596,9 @@ public: sourceOperand); } - static PartitionOp Require(Element tgt, - SILInstruction *sourceInst = nullptr) { - return PartitionOp(PartitionOpKind::Require, tgt, sourceInst); + static PartitionOp Require(Element tgt, SILInstruction *sourceInst = nullptr, + Options options = {}) { + return PartitionOp(PartitionOpKind::Require, tgt, sourceInst, options); } static PartitionOp UnknownPatternError(Element elt, @@ -593,21 +619,50 @@ public: } bool operator==(const PartitionOp &other) const { - return opKind == other.opKind && opArgs == other.opArgs && - source == other.source; + return opKind == other.opKind && opArg1 == other.opArg1 && + opArg2 == other.opArg2 && source == other.source; }; bool operator<(const PartitionOp &other) const { if (opKind != other.opKind) return opKind < other.opKind; - if (opArgs != other.opArgs) - return opArgs < other.opArgs; + + if (opArg1 != other.opArg1) { + // null < non-null always. + if (!opArg1.has_value() && other.opArg1.has_value()) + return true; + // non-null >= null always. + if (opArg1.has_value() && !other.opArg1.has_value()) + return false; + return *opArg1 < other.opArg1.has_value(); + } + + if (opArg2 != other.opArg2) { + // null < non-null always. + if (!opArg2.has_value() && other.opArg2.has_value()) + return true; + // non-null >= null always. + if (opArg2.has_value() && !other.opArg2.has_value()) + return false; + return *opArg2 < other.opArg2.has_value(); + } + return source < other.source; } PartitionOpKind getKind() const { return opKind; } - ArrayRef getOpArgs() const { return opArgs; } + Element getOpArg1() const { return opArg1.value(); } + Element getOpArg2() const { return opArg2.value(); } + + Options getOptions() const { return options; } + + void getOpArgs(SmallVectorImpl &args) const { + if (opArg1.has_value()) + args.push_back(*opArg1); + if (opArg2.has_value()) + args.push_back(*opArg2); + } SILInstruction *getSourceInst() const { if (source.is()) @@ -1226,8 +1281,9 @@ public: isolationRegionInfo->merge(getIsolationRegionInfo(pair.first)); if (!isolationRegionInfo) return {}; - if (sourceOp) + if (sourceOp) { isClosureCapturedElt |= isClosureCaptured(pair.first, sourceOp); + } } } @@ -1311,30 +1367,28 @@ public: switch (op.getKind()) { case PartitionOpKind::Assign: { - assert(op.getOpArgs().size() == 2 && - "Assign PartitionOp should be passed 2 arguments"); - assert(p.isTrackingElement(op.getOpArgs()[1]) && + assert(p.isTrackingElement(op.getOpArg2()) && "Assign PartitionOp's source argument should be already tracked"); // See if we are assigning an a non-disconnected value into a 'out // sending' parameter. In such a case, we emit a diagnostic. if (doesParentFunctionHaveSendingResult(op)) { - if (auto instance = getRepresentativeValue(op.getOpArgs()[0])) { + if (auto instance = getRepresentativeValue(op.getOpArg1())) { if (auto value = instance.maybeGetValue()) { if (auto *fArg = dyn_cast(value)) { if (fArg->getArgumentConvention().isIndirectOutParameter()) { auto staticRegionIsolation = - getIsolationRegionInfo(op.getOpArgs()[1]); - Region srcRegion = p.getRegion(op.getOpArgs()[1]); + getIsolationRegionInfo(op.getOpArg2()); + Region srcRegion = p.getRegion(op.getOpArg2()); auto dynamicRegionIsolation = getIsolationRegionInfo(srcRegion); // We can unconditionally getValue here since we can never // assign an actor introducing inst. - auto rep = getRepresentativeValue(op.getOpArgs()[1]).getValue(); + auto rep = getRepresentativeValue(op.getOpArg2()).getValue(); if (!dynamicRegionIsolation.isDisconnected() && !staticRegionIsolation.isUnsafeNonIsolated()) { handleError(AssignNeverSendableIntoSendingResultError( - op, op.getOpArgs()[0], fArg, op.getOpArgs()[1], rep, + op, op.getOpArg1(), fArg, op.getOpArg2(), rep, dynamicRegionIsolation)); } } @@ -1343,19 +1397,11 @@ public: } } - p.assignElement(op.getOpArgs()[0], op.getOpArgs()[1]); + p.assignElement(op.getOpArg1(), op.getOpArg2()); return; } case PartitionOpKind::AssignFresh: { - auto arrayRef = op.getOpArgs(); - - Element front = arrayRef.front(); - p.trackNewElement(front); - arrayRef = arrayRef.drop_front(); - for (auto x : arrayRef) { - p.trackNewElement(x); - p.assignElement(x, front); - } + p.trackNewElement(op.getOpArg1()); return; } case PartitionOpKind::Send: { @@ -1364,14 +1410,12 @@ public: // ensures that if we pass the same argument multiple times to the same // sending function as weakly sent arguments, we do not get an // error. - assert(op.getOpArgs().size() == 1 && - "Send PartitionOp should be passed 1 argument"); - assert(p.isTrackingElement(op.getOpArgs()[0]) && + assert(p.isTrackingElement(op.getOpArg1()) && "Send PartitionOp's argument should already be tracked"); // Before we do any further work, see if we have a nonisolated(unsafe) // element. In such a case, this is also not a real send point. - Element sentElement = op.getOpArgs()[0]; + Element sentElement = op.getOpArg1(); if (getIsolationRegionInfo(sentElement).isUnsafeNonIsolated()) { return; } @@ -1380,7 +1424,7 @@ public: // isolation region info of everything else in our region. This is the // dynamic isolation region info found by the dataflow. Region sentRegion = p.getRegion(sentElement); - bool isClosureCapturedElt = false; + bool regionHasClosureCapturedElt = false; SILDynamicMergedIsolationInfo sentRegionIsolation; // TODO: Today we only return the first element in our region that has @@ -1392,7 +1436,7 @@ public: if (!pairOpt) { return handleError(UnknownCodePatternError(op)); } - std::tie(sentRegionIsolation, isClosureCapturedElt) = *pairOpt; + std::tie(sentRegionIsolation, regionHasClosureCapturedElt) = *pairOpt; // If we merged anything, we need to handle an attempt to send a // never-sent value unless our value has the same isolation info as our @@ -1401,7 +1445,7 @@ public: if (!(calleeIsolationInfo && sentRegionIsolation.hasSameIsolation(calleeIsolationInfo)) && !sentRegionIsolation.isDisconnected()) { - return handleSendNeverSentHelper(op, op.getOpArgs()[0], + return handleSendNeverSentHelper(op, op.getOpArg1(), sentRegionIsolation); } @@ -1417,9 +1461,9 @@ public: return; } - // Mark op.getOpArgs()[0] as sent. + // Mark op.getOpArg1() as sent. SendingOperandState &state = operandToStateMap.get(op.getSourceOp()); - state.isClosureCaptured |= isClosureCapturedElt; + state.isClosureCaptured |= regionHasClosureCapturedElt; if (auto newInfo = state.isolationInfo.merge(sentRegionIsolation)) { state.isolationInfo = *newInfo; } else { @@ -1428,44 +1472,40 @@ public: assert(state.isolationInfo && "Cannot have unknown"); state.isolationHistory.pushCFGHistoryJoin(p.getIsolationHistory()); auto *ptrSet = ptrSetFactory.get(op.getSourceOp()); - p.markSent(op.getOpArgs()[0], ptrSet); + p.markSent(op.getOpArg1(), ptrSet); return; } case PartitionOpKind::UndoSend: { - assert(op.getOpArgs().size() == 1 && - "UndoSend PartitionOp should be passed 1 argument"); - assert(p.isTrackingElement(op.getOpArgs()[0]) && + assert(p.isTrackingElement(op.getOpArg1()) && "UndoSend PartitionOp's argument should already be tracked"); - // Mark op.getOpArgs()[0] as not sent. - p.undoSend(op.getOpArgs()[0]); + // Mark op.getOpArg1() as not sent. + p.undoSend(op.getOpArg1()); return; } case PartitionOpKind::Merge: { - assert(op.getOpArgs().size() == 2 && - "Merge PartitionOp should be passed 2 arguments"); - assert(p.isTrackingElement(op.getOpArgs()[0]) && - p.isTrackingElement(op.getOpArgs()[1]) && + assert(p.isTrackingElement(op.getOpArg1()) && + p.isTrackingElement(op.getOpArg2()) && "Merge PartitionOp's arguments should already be tracked"); // See if we are assigning an a non-disconnected value into a 'out // sending' parameter. In such a case, we emit a diagnostic. if (doesParentFunctionHaveSendingResult(op)) { - if (auto instance = getRepresentativeValue(op.getOpArgs()[0])) { + if (auto instance = getRepresentativeValue(op.getOpArg1())) { if (auto value = instance.maybeGetValue()) { if (auto *fArg = dyn_cast(value)) { if (fArg->getArgumentConvention().isIndirectOutParameter()) { auto staticRegionIsolation = - getIsolationRegionInfo(op.getOpArgs()[1]); - Region srcRegion = p.getRegion(op.getOpArgs()[1]); + getIsolationRegionInfo(op.getOpArg2()); + Region srcRegion = p.getRegion(op.getOpArg2()); auto dynamicRegionIsolation = getIsolationRegionInfo(srcRegion); // We can unconditionally getValue here since we can never // assign an actor introducing inst. - auto rep = getRepresentativeValue(op.getOpArgs()[1]).getValue(); + auto rep = getRepresentativeValue(op.getOpArg2()).getValue(); if (!dynamicRegionIsolation.isDisconnected() && !staticRegionIsolation.isUnsafeNonIsolated()) { handleError(AssignNeverSendableIntoSendingResultError( - op, op.getOpArgs()[0], fArg, op.getOpArgs()[1], rep, + op, op.getOpArg1(), fArg, op.getOpArg2(), rep, dynamicRegionIsolation)); } } @@ -1474,39 +1514,35 @@ public: } } - p.merge(op.getOpArgs()[0], op.getOpArgs()[1]); + p.merge(op.getOpArg1(), op.getOpArg2()); return; } case PartitionOpKind::Require: - assert(op.getOpArgs().size() == 1 && - "Require PartitionOp should be passed 1 argument"); - assert(p.isTrackingElement(op.getOpArgs()[0]) && + assert(p.isTrackingElement(op.getOpArg1()) && "Require PartitionOp's argument should already be tracked"); - if (auto *sentOperandSet = p.getSentOperandSet(op.getOpArgs()[0])) { + if (auto *sentOperandSet = p.getSentOperandSet(op.getOpArg1())) { for (auto sentOperand : sentOperandSet->data()) { - handleLocalUseAfterSendHelper(op, op.getOpArgs()[0], sentOperand); + handleLocalUseAfterSendHelper(op, op.getOpArg1(), sentOperand); } } return; case PartitionOpKind::InOutSendingAtFunctionExit: { - assert(op.getOpArgs().size() == 1 && - "Require PartitionOp should be passed 1 argument"); - assert(p.isTrackingElement(op.getOpArgs()[0]) && + assert(p.isTrackingElement(op.getOpArg1()) && "Require PartitionOp's argument should already be tracked"); // First check if the region of our 'inout sending' element has been // sent. In that case, we emit a special use after free error. - if (auto *sentOperandSet = p.getSentOperandSet(op.getOpArgs()[0])) { + if (auto *sentOperandSet = p.getSentOperandSet(op.getOpArg1())) { for (auto sentOperand : sentOperandSet->data()) { - handleError(InOutSendingNotInitializedAtExitError( - op, op.getOpArgs()[0], sentOperand)); + handleError(InOutSendingNotInitializedAtExitError(op, op.getOpArg1(), + sentOperand)); } return; } // If we were not sent, check if our region is actor isolated. If so, // error since we need a disconnected value in the inout parameter. - Region inoutSendingRegion = p.getRegion(op.getOpArgs()[0]); + Region inoutSendingRegion = p.getRegion(op.getOpArg1()); auto dynamicRegionIsolation = getIsolationRegionInfo(inoutSendingRegion); // If we failed to merge emit an unknown pattern error so we fail. @@ -1519,24 +1555,31 @@ public: // disconnected. if (!dynamicRegionIsolation.isDisconnected()) { handleError(InOutSendingNotDisconnectedAtExitError( - op, op.getOpArgs()[0], dynamicRegionIsolation)); + op, op.getOpArg1(), dynamicRegionIsolation)); } return; } case PartitionOpKind::UnknownPatternError: // Begin tracking the specified element in case we have a later use. - p.trackNewElement(op.getOpArgs()[0]); + p.trackNewElement(op.getOpArg1()); // Then emit an unknown code pattern error. return handleError(UnknownCodePatternError(op)); - case PartitionOpKind::NonSendableIsolationCrossingResult: + case PartitionOpKind::NonSendableIsolationCrossingResult: { // Grab the dynamic dataflow isolation information for our element's // region. - Region region = p.getRegion(op.getOpArgs()[0]); + Region region = p.getRegion(op.getOpArg1()); // Then emit the error. return handleError( - NonSendableIsolationCrossingResultError(op, op.getOpArgs()[0])); + NonSendableIsolationCrossingResultError(op, op.getOpArg1())); + } + case PartitionOpKind::AssignFreshAssign: + assert(p.isTrackingElement(op.getOpArg2()) && + "Source argument should be already tracked"); + p.trackNewElement(op.getOpArg1()); + p.assignElement(op.getOpArg1(), op.getOpArg2()); + return; } llvm_unreachable("Covered switch isn't covered?!"); } diff --git a/include/swift/SILOptimizer/Utils/SILIsolationInfo.h b/include/swift/SILOptimizer/Utils/SILIsolationInfo.h index 114759dcc7b..dbd3b3946bb 100644 --- a/include/swift/SILOptimizer/Utils/SILIsolationInfo.h +++ b/include/swift/SILOptimizer/Utils/SILIsolationInfo.h @@ -442,10 +442,18 @@ public: /// TODO: Fix the type checker. static bool isNonSendableType(SILType type, SILFunction *fn); + static bool isSendableType(SILType type, SILFunction *fn) { + return !isNonSendableType(type, fn); + } + static bool isNonSendableType(SILValue value) { return isNonSendableType(value->getType(), value->getFunction()); } + static bool isSendableType(SILValue value) { + return !isNonSendableType(value); + } + bool hasSameIsolation(ActorIsolation actorIsolation) const; /// Returns true if \p this and \p other have the same isolation. It allows diff --git a/include/swift/SymbolGraphGen/SymbolGraphOptions.h b/include/swift/SymbolGraphGen/SymbolGraphOptions.h index 168e345d4c4..179c2c142de 100644 --- a/include/swift/SymbolGraphGen/SymbolGraphOptions.h +++ b/include/swift/SymbolGraphGen/SymbolGraphOptions.h @@ -10,8 +10,9 @@ // //===----------------------------------------------------------------------===// -#include "llvm/TargetParser/Triple.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/TargetParser/Triple.h" #include "swift/AST/AttrKind.h" @@ -69,6 +70,13 @@ struct SymbolGraphOptions { /// If this has a value specifies an explicit allow list of reexported module /// names that should be included symbol graph. std::optional> AllowedReexportedModules = {}; + + /// If set, a list of availability platforms to restrict (or block) when + /// rendering symbol graphs. + std::optional> AvailabilityPlatforms = {}; + + /// Whether `AvailabilityPlatforms` is an allow list or a block list. + bool AvailabilityIsBlockList = false; }; } // end namespace symbolgraphgen diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 17ec755b219..060a8364a63 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -618,14 +618,6 @@ static StringRef getDumpString(FunctionRefInfo::ApplyLevel applyLevel) { return "double_apply"; } } -static StringRef getDumpString(ExecutionKind kind) { - switch (kind) { - case ExecutionKind::Concurrent: - return "concurrent"; - case ExecutionKind::Caller: - return "caller"; - } -} static StringRef getDumpString(ExplicitSafety safety) { switch (safety) { case ExplicitSafety::Unspecified: @@ -2391,6 +2383,8 @@ namespace { VD->getAttrs().getAttribute()) { if (nonisolatedAttr->isUnsafe()) { printFlag(true, "nonisolated(unsafe)", DeclModifierColor); + } else if (nonisolatedAttr->isNonSending()) { + printFlag(true, "nonisolated(nonsending)", DeclModifierColor); } else { printFlag(true, "nonisolated", DeclModifierColor); } @@ -4646,6 +4640,12 @@ public: printFoot(); } + void visitCallerIsolatedTypeRepr(CallerIsolatedTypeRepr *T, Label label) { + printCommon("caller_isolated", label); + printRec(T->getBase(), Label::optional("base")); + printFoot(); + } + void visitCompileTimeLiteralTypeRepr(CompileTimeLiteralTypeRepr *T, Label label) { printCommon("_const", label); printRec(T->getBase(), Label::optional("base")); @@ -4926,14 +4926,10 @@ public: TRIVIAL_ATTR_PRINTER(WarnUnqualifiedAccess, warn_unqualified_access) TRIVIAL_ATTR_PRINTER(WeakLinked, weak_linked) TRIVIAL_ATTR_PRINTER(Extensible, extensible) + TRIVIAL_ATTR_PRINTER(Concurrent, concurrent) #undef TRIVIAL_ATTR_PRINTER - void visitExecutionAttr(ExecutionAttr *Attr, Label label) { - printCommon(Attr, "execution_attr", label); - printField(Attr->getBehavior(), Label::always("behavior")); - printFoot(); - } void visitABIAttr(ABIAttr *Attr, Label label) { printCommon(Attr, "abi_attr", label); printRec(Attr->abiDecl, Label::always("decl")); @@ -5184,6 +5180,7 @@ public: void visitNonisolatedAttr(NonisolatedAttr *Attr, Label label) { printCommon(Attr, "nonisolated_attr", label); printFlag(Attr->isUnsafe(), "unsafe"); + printFlag(Attr->isNonSending(), "nonsending"); printFoot(); } void visitObjCAttr(ObjCAttr *Attr, Label label) { @@ -6341,7 +6338,7 @@ namespace { printFlag("@isolated(any)"); break; case FunctionTypeIsolation::Kind::NonIsolatedCaller: - printFlag("@execution(caller)"); + printFlag("nonisolated(nonsending)"); break; } } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index dade5c24613..1a775824f27 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3302,14 +3302,6 @@ suppressingFeatureAddressableTypes(PrintOptions &options, action(); } -static void -suppressingFeatureExecutionAttribute(PrintOptions &options, - llvm::function_ref action) { - llvm::SaveAndRestore scope1(options.SuppressExecutionAttribute, true); - ExcludeAttrRAII scope2(options.ExcludeAttrList, DeclAttrKind::Execution); - action(); -} - /// Suppress the printing of a particular feature. static void suppressingFeature(PrintOptions &options, Feature feature, llvm::function_ref action) { @@ -3856,13 +3848,13 @@ static void printParameterFlags(ASTPrinter &printer, const PrintOptions &options, const ParamDecl *param, ParameterTypeFlags flags, - bool escaping) { - if (!options.excludeAttrKind(TypeAttrKind::Autoclosure) && - flags.isAutoClosure()) - printer.printAttrName("@autoclosure "); - if (!options.excludeAttrKind(TypeAttrKind::NoDerivative) && - flags.isNoDerivative()) - printer.printAttrName("@noDerivative "); + bool escaping, + bool isIsolatedToCaller = false) { + // Always print `nonisolated(nonsending)` specifier on a parameter + // first, to avoid any issues with ordering. + if (isIsolatedToCaller) { + printer.printKeyword("nonisolated(nonsending)", options, " "); + } switch (flags.getOwnershipSpecifier()) { case ParamSpecifier::Default: @@ -3891,7 +3883,7 @@ static void printParameterFlags(ASTPrinter &printer, if (flags.isSending()) { if (!options.SuppressSendingArgsAndResults) { - printer.printAttrName("sending "); + printer.printKeyword("sending", options, " "); } else if (flags.getOwnershipSpecifier() == ParamSpecifier::ImplicitlyCopyableConsuming) { // Ok. We are suppressing sending. If our ownership specifier was @@ -3909,14 +3901,24 @@ static void printParameterFlags(ASTPrinter &printer, printer.printKeyword("isolated", options, " "); } - if (!options.excludeAttrKind(TypeAttrKind::Escaping) && escaping) - printer.printKeyword("@escaping", options, " "); - if (flags.isCompileTimeLiteral()) printer.printKeyword("_const", options, " "); - + + if (!options.excludeAttrKind(TypeAttrKind::Autoclosure) && + flags.isAutoClosure()) + printer.printAttrName("@autoclosure "); + if (!options.excludeAttrKind(TypeAttrKind::NoDerivative) && + flags.isNoDerivative()) + printer.printAttrName("@noDerivative "); + + // `inout` implies `@escaping` + if (flags.getOwnershipSpecifier() != ParamSpecifier::InOut) { + if (!options.excludeAttrKind(TypeAttrKind::Escaping) && escaping) + printer.printAttrName("@escaping "); + } + if (flags.isConstValue()) - printer.printKeyword("@const", options, " "); + printer.printAttrName("@const "); } void PrintAST::visitVarDecl(VarDecl *decl) { @@ -3932,11 +3934,18 @@ void PrintAST::visitVarDecl(VarDecl *decl) { if (decl->isStatic() && Options.PrintStaticKeyword) printStaticKeyword(decl->getCorrectStaticSpelling()); if (decl->getKind() == DeclKind::Var || Options.PrintParameterSpecifiers) { + // If InferPropertyIntroducerFromAccessors is set, turn all read-only + // properties to `let`. + auto introducer = decl->getIntroducer(); + if (Options.InferPropertyIntroducerFromAccessors && + !cast(decl)->isSettable(nullptr)) + introducer = VarDecl::Introducer::Let; + // Map all non-let specifiers to 'var'. This is not correct, but // SourceKit relies on this for info about parameter decls. Printer.printIntroducerKeyword( - decl->getIntroducer() == VarDecl::Introducer::Let ? "let" : "var", + introducer == VarDecl::Introducer::Let ? "let" : "var", Options, " "); } printContextIfNeeded(decl); @@ -4025,11 +4034,24 @@ void PrintAST::printOneParameter(const ParamDecl *param, printArgName(); + auto interfaceTy = param->getInterfaceType(); + + // If type of this parameter is isolated to a caller, let's + // strip the isolation from the type to avoid printing it as + // part of the function type because that would break ordering + // between specifiers and attributes. + if (param->isCallerIsolated()) { + if (auto *funcTy = dyn_cast(interfaceTy.getPointer())) { + interfaceTy = + funcTy->withIsolation(FunctionTypeIsolation::forNonIsolated()); + } + } + TypeLoc TheTypeLoc; if (auto *repr = param->getTypeRepr()) { - TheTypeLoc = TypeLoc(repr, param->getInterfaceType()); + TheTypeLoc = TypeLoc(repr, interfaceTy); } else { - TheTypeLoc = TypeLoc::withoutLoc(param->getInterfaceType()); + TheTypeLoc = TypeLoc::withoutLoc(interfaceTy); } { @@ -4041,7 +4063,8 @@ void PrintAST::printOneParameter(const ParamDecl *param, !willUseTypeReprPrinting(TheTypeLoc, CurrentType, Options)) { auto type = TheTypeLoc.getType(); printParameterFlags(Printer, Options, param, paramFlags, - isEscaping(type)); + isEscaping(type), + param->isCallerIsolated()); } printTypeLocForImplicitlyUnwrappedOptional( @@ -4221,7 +4244,8 @@ void PrintAST::visitAccessorDecl(AccessorDecl *decl) { Printer << getAccessorLabel(decl->getAccessorKind()); auto params = decl->getParameters(); - if (params->size() != 0 && !params->get(0)->isImplicit()) { + if (params->size() != 0 && !params->get(0)->isImplicit() + && Options.PrintExplicitAccessorParameters) { auto Name = params->get(0)->getName(); if (!Name.empty()) { Printer << "("; @@ -6501,8 +6525,7 @@ public: break; case FunctionTypeIsolation::Kind::NonIsolatedCaller: - if (!Options.SuppressExecutionAttribute) - Printer << "@execution(caller) "; + Printer << "nonisolated(nonsending) "; break; } diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 01d995e2215..31a8e44f921 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -2324,6 +2324,10 @@ bool Traversal::visitSendingTypeRepr(SendingTypeRepr *T) { return doIt(T->getBase()); } +bool Traversal::visitCallerIsolatedTypeRepr(CallerIsolatedTypeRepr *T) { + return doIt(T->getBase()); +} + bool Traversal::visitCompileTimeLiteralTypeRepr(CompileTimeLiteralTypeRepr *T) { return doIt(T->getBase()); } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 1f5ece96136..33d62846331 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -304,26 +304,6 @@ void IsolatedTypeAttr::printImpl(ASTPrinter &printer, printer.printStructurePost(PrintStructureKind::BuiltinAttribute); } -void ExecutionTypeAttr::printImpl(ASTPrinter &printer, - const PrintOptions &options) const { - if (options.SuppressExecutionAttribute) - return; - - printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); - printer.printAttrName("@execution"); - printer << "("; - switch (getBehavior()) { - case ExecutionKind::Concurrent: - printer << "concurrent"; - break; - case ExecutionKind::Caller: - printer << "caller"; - break; - } - printer << ")"; - printer.printStructurePost(PrintStructureKind::BuiltinAttribute); -} - /// Given a name like "inline", return the decl attribute ID that corresponds /// to it. Note that this is a many-to-one mapping, and that the identifier /// passed in may only be the first portion of the attribute (e.g. in the case @@ -1538,8 +1518,15 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DeclAttrKind::Nonisolated: { Printer.printAttrName("nonisolated"); - if (cast(this)->isUnsafe()) { + switch (cast(this)->getModifier()) { + case NonIsolatedModifier::None: + break; + case NonIsolatedModifier::Unsafe: Printer << "(unsafe)"; + break; + case NonIsolatedModifier::NonSending: + Printer << "(nonsending)"; + break; } break; } @@ -1722,19 +1709,6 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, break; } - case DeclAttrKind::Execution: { - auto *attr = cast(this); - switch (attr->getBehavior()) { - case ExecutionKind::Concurrent: - Printer << "@execution(concurrent)"; - break; - case ExecutionKind::Caller: - Printer << "@execution(caller)"; - break; - } - break; - } - #define SIMPLE_DECL_ATTR(X, CLASS, ...) case DeclAttrKind::CLASS: #include "swift/AST/DeclAttr.def" llvm_unreachable("handled above"); @@ -1933,10 +1907,13 @@ StringRef DeclAttribute::getAttrName() const { case DeclAttrKind::Documentation: return "_documentation"; case DeclAttrKind::Nonisolated: - if (cast(this)->isUnsafe()) { - return "nonisolated(unsafe)"; - } else { - return "nonisolated"; + switch (cast(this)->getModifier()) { + case NonIsolatedModifier::None: + return "nonisolated"; + case NonIsolatedModifier::Unsafe: + return "nonisolated(unsafe)"; + case NonIsolatedModifier::NonSending: + return "nonisolated(nonsending)"; } case DeclAttrKind::MacroRole: switch (cast(this)->getMacroSyntax()) { @@ -1958,15 +1935,6 @@ StringRef DeclAttribute::getAttrName() const { } case DeclAttrKind::Lifetime: return "lifetime"; - case DeclAttrKind::Execution: { - switch (cast(this)->getBehavior()) { - case ExecutionKind::Concurrent: - return "execution(concurrent)"; - case ExecutionKind::Caller: - return "execution(caller)"; - } - llvm_unreachable("Invalid execution kind"); - } } llvm_unreachable("bad DeclAttrKind"); } diff --git a/lib/AST/AvailabilityConstraint.cpp b/lib/AST/AvailabilityConstraint.cpp index 9968cdcf2d3..5377057ad94 100644 --- a/lib/AST/AvailabilityConstraint.cpp +++ b/lib/AST/AvailabilityConstraint.cpp @@ -113,25 +113,59 @@ DeclAvailabilityConstraints::getPrimaryConstraint() const { return result; } +static bool canIgnoreConstraintInUnavailableContexts( + const Decl *decl, const AvailabilityConstraint &constraint) { + auto domain = constraint.getDomain(); + + switch (constraint.getReason()) { + case AvailabilityConstraint::Reason::UnconditionallyUnavailable: + // Always reject uses of universally unavailable declarations, regardless + // of context, since there are no possible compilation configurations in + // which they are available. However, make an exception for types and + // conformances, which can sometimes be awkward to avoid references to. + if (!isa(decl) && !isa(decl)) { + if (domain.isUniversal() || domain.isSwiftLanguage()) + return false; + } + return true; + + case AvailabilityConstraint::Reason::PotentiallyUnavailable: + switch (domain.getKind()) { + case AvailabilityDomain::Kind::Universal: + case AvailabilityDomain::Kind::SwiftLanguage: + case AvailabilityDomain::Kind::PackageDescription: + case AvailabilityDomain::Kind::Embedded: + case AvailabilityDomain::Kind::Custom: + return false; + case AvailabilityDomain::Kind::Platform: + // Platform availability only applies to the target triple that the + // binary is being compiled for. Since the same declaration can be + // potentially unavailable from a given context when compiling for one + // platform, but available from that context when compiling for a + // different platform, it is overly strict to enforce potential platform + // unavailability constraints in contexts that are unavailable to that + // platform. + return true; + } + return constraint.getDomain().isPlatform(); + + case AvailabilityConstraint::Reason::Obsoleted: + case AvailabilityConstraint::Reason::UnavailableForDeployment: + return false; + } +} + static bool -isInsideCompatibleUnavailableDeclaration(const Decl *decl, - const SemanticAvailableAttr &attr, - const AvailabilityContext &context) { +shouldIgnoreConstraintInContext(const Decl *decl, + const AvailabilityConstraint &constraint, + const AvailabilityContext &context) { if (!context.isUnavailable()) return false; - if (!attr.isUnconditionallyUnavailable()) + if (!canIgnoreConstraintInUnavailableContexts(decl, constraint)) return false; - // Refuse calling universally unavailable functions from unavailable code, - // but allow the use of types. - auto domain = attr.getDomain(); - if (!isa(decl) && !isa(decl)) { - if (domain.isUniversal() || domain.isSwiftLanguage()) - return false; - } - - return context.containsUnavailableDomain(domain); + return context.containsUnavailableDomain(constraint.getDomain()); } /// Returns the `AvailabilityConstraint` that describes how \p attr restricts @@ -218,8 +252,7 @@ static void getAvailabilityConstraintsForDecl( // declaration is unconditionally unavailable in a domain for which // the context is already unavailable. llvm::erase_if(constraints, [&](const AvailabilityConstraint &constraint) { - return isInsideCompatibleUnavailableDeclaration(decl, constraint.getAttr(), - context); + return shouldIgnoreConstraintInContext(decl, constraint, context); }); } diff --git a/lib/AST/Bridging/DeclAttributeBridging.cpp b/lib/AST/Bridging/DeclAttributeBridging.cpp index 937ba5589a7..6fffda31e53 100644 --- a/lib/AST/Bridging/DeclAttributeBridging.cpp +++ b/lib/AST/Bridging/DeclAttributeBridging.cpp @@ -627,12 +627,26 @@ BridgedNonSendableAttr BridgedNonSendableAttr_createParsed( NonSendableAttr(cAtLoc.unbridged(), cRange.unbridged(), unbridged(cKind)); } +static NonIsolatedModifier unbridged(BridgedNonIsolatedModifier modifier) { + switch (modifier) { + case BridgedNonIsolatedModifierNone: + return NonIsolatedModifier::None; + case BridgedNonIsolatedModifierUnsafe: + return NonIsolatedModifier::Unsafe; + case BridgedNonIsolatedModifierNonSending: + return NonIsolatedModifier::NonSending; + } + llvm_unreachable("unhandled enum value"); +} + BridgedNonisolatedAttr BridgedNonisolatedAttr_createParsed(BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceRange cRange, bool isUnsafe) { + BridgedSourceRange cRange, + BridgedNonIsolatedModifier modifier) { return new (cContext.unbridged()) NonisolatedAttr( - cAtLoc.unbridged(), cRange.unbridged(), isUnsafe, /*implicit=*/false); + cAtLoc.unbridged(), cRange.unbridged(), unbridged(modifier), + /*implicit=*/false); } BridgedObjCAttr @@ -885,22 +899,4 @@ BridgedUnavailableFromAsyncAttr BridgedUnavailableFromAsyncAttr_createParsed( return new (cContext.unbridged()) UnavailableFromAsyncAttr(cMessage.unbridged(), cAtLoc.unbridged(), cRange.unbridged(), /*implicit=*/false); -} - -static ExecutionKind unbridged(BridgedExecutionKind kind) { - switch (kind) { - case BridgedExecutionKindConcurrent: - return ExecutionKind::Concurrent; - case BridgedExecutionKindCaller: - return ExecutionKind::Caller; - } - llvm_unreachable("unhandled enum value"); -} - -BridgedExecutionAttr BridgedExecutionAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc atLoc, - BridgedSourceRange range, BridgedExecutionKind behavior) { - return new (cContext.unbridged()) - ExecutionAttr(atLoc.unbridged(), range.unbridged(), - unbridged(behavior), /*implicit=*/false); -} +} \ No newline at end of file diff --git a/lib/AST/Bridging/TypeAttributeBridging.cpp b/lib/AST/Bridging/TypeAttributeBridging.cpp index 8f8e92537ea..1cc5050d13f 100644 --- a/lib/AST/Bridging/TypeAttributeBridging.cpp +++ b/lib/AST/Bridging/TypeAttributeBridging.cpp @@ -87,25 +87,6 @@ BridgedDifferentiableTypeAttr BridgedDifferentiableTypeAttr_createParsed( {unbridged(cKind), cKindLoc.unbridged()}); } -BridgedExecutionTypeAttr BridgedExecutionTypeAttr_createParsed( - BridgedASTContext cContext, BridgedSourceLoc cAtLoc, - BridgedSourceLoc cNameLoc, BridgedSourceRange cParensRange, - BridgedExecutionTypeAttrExecutionKind behavior, - BridgedSourceLoc cBehaviorLoc) { - auto behaviorKind = [=] { - switch (behavior) { - case BridgedExecutionTypeAttrExecutionKind_Concurrent: - return ExecutionKind::Concurrent; - case BridgedExecutionTypeAttrExecutionKind_Caller: - return ExecutionKind::Caller; - } - llvm_unreachable("bad kind"); - }(); - return new (cContext.unbridged()) ExecutionTypeAttr( - cAtLoc.unbridged(), cNameLoc.unbridged(), cParensRange.unbridged(), - {behaviorKind, cBehaviorLoc.unbridged()}); -} - BridgedIsolatedTypeAttr BridgedIsolatedTypeAttr_createParsed( BridgedASTContext cContext, BridgedSourceLoc cAtLoc, BridgedSourceLoc cNameLoc, BridgedSourceRange cParensRange, diff --git a/lib/AST/Bridging/TypeReprBridging.cpp b/lib/AST/Bridging/TypeReprBridging.cpp index e87a215fa36..ea72e40ccda 100644 --- a/lib/AST/Bridging/TypeReprBridging.cpp +++ b/lib/AST/Bridging/TypeReprBridging.cpp @@ -211,6 +211,14 @@ BridgedSendingTypeRepr_createParsed(BridgedASTContext cContext, SendingTypeRepr(base.unbridged(), cSpecifierLoc.unbridged()); } +BridgedCallerIsolatedTypeRepr +BridgedCallerIsolatedTypeRepr_createParsed(BridgedASTContext cContext, + BridgedTypeRepr base, + BridgedSourceLoc cSpecifierLoc) { + return new (cContext.unbridged()) + CallerIsolatedTypeRepr(base.unbridged(), cSpecifierLoc.unbridged()); +} + BridgedVarargTypeRepr BridgedVarargTypeRepr_createParsed(BridgedASTContext cContext, BridgedTypeRepr base, diff --git a/lib/AST/ConformanceLookup.cpp b/lib/AST/ConformanceLookup.cpp index dc02e369d7d..671b2e2e3ca 100644 --- a/lib/AST/ConformanceLookup.cpp +++ b/lib/AST/ConformanceLookup.cpp @@ -66,6 +66,15 @@ swift::collectExistentialConformances(CanType fromType, return fromType->getASTContext().AllocateCopy(conformances); } +static bool containsNonMarkerProtocols(ArrayRef protocols) { + for (auto proto : protocols) { + if (!proto->isMarkerProtocol()) + return true; + } + + return false; +} + ProtocolConformanceRef swift::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { ASTContext &ctx = protocol->getASTContext(); @@ -146,6 +155,12 @@ swift::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { if (auto conformance = lookupSuperclassConformance(layout.getSuperclass())) return conformance; + // If the protocol is SendableMetatype, and there are no non-marker protocol + // requirements, allow it via self-conformance. + if (protocol->isSpecificProtocol(KnownProtocolKind::SendableMetatype) && + !layout.containsNonMarkerProtocols()) + return ProtocolConformanceRef(ctx.getSelfConformance(protocol)); + // We didn't find our protocol in the existential's list; it doesn't // conform. return ProtocolConformanceRef::forInvalid(); @@ -377,6 +392,48 @@ static ProtocolConformanceRef getBuiltinFunctionTypeConformance( return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); } +/// Given the instance type of a metatype, determine whether the metatype is +/// Sendable. +/// +// Metatypes are generally Sendable, but with isolated conformances we +// cannot assume that metatypes based on type parameters are Sendable. +// Therefore, check for conformance to SendableMetatype. +static bool metatypeWithInstanceTypeIsSendable(Type instanceType) { + ASTContext &ctx = instanceType->getASTContext(); + + // If we don't have the SendableMetatype protocol at all, just assume all + // metatypes are Sendable. + auto sendableMetatypeProto = + ctx.getProtocol(KnownProtocolKind::SendableMetatype); + if (!sendableMetatypeProto) + return true; + + // If the instance type is a type parameter, it is not necessarily + // SendableMetatype. There will need to be a SendableMetatype requirement, + // but we do not have the generic environment to check that. + if (instanceType->isTypeParameter()) + return false; + + // If the instance type conforms to SendableMetatype, then its + // metatype is Sendable. + auto instanceConformance = lookupConformance( + instanceType, sendableMetatypeProto); + if (!instanceConformance.isInvalid() && + !instanceConformance.hasMissingConformance()) + return true; + + // If this is an archetype that is non-SendableMetatype, but there are no + // non-marker protocol requirements that could carry conformances, treat + // the metatype as Sendable. + if (auto archetype = instanceType->getAs()) { + if (!containsNonMarkerProtocols(archetype->getConformsTo())) + return true; + } + + // The instance type is non-Sendable. + return false; +} + /// Synthesize a builtin metatype type conformance to the given protocol, if /// appropriate. static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance( @@ -387,31 +444,9 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance( if (auto kp = protocol->getKnownProtocolKind()) { switch (*kp) { case KnownProtocolKind::Sendable: - // Metatypes are generally Sendable, but with isolated conformances we - // cannot assume that metatypes based on type parameters are Sendable. - // Therefore, check for conformance to SendableMetatype. - if (ctx.LangOpts.hasFeature(Feature::IsolatedConformances)) { - auto sendableMetatypeProto = - ctx.getProtocol(KnownProtocolKind::SendableMetatype); - if (sendableMetatypeProto) { - Type instanceType = metatypeType->getInstanceType(); + if (!metatypeWithInstanceTypeIsSendable(metatypeType->getInstanceType())) + break; - // If the instance type is a type parameter, it is not necessarily - // Sendable. There will need to be a Sendable requirement. - if (instanceType->isTypeParameter()) - break; - - // If the instance type conforms to SendableMetatype, then its - // metatype is Sendable. - auto instanceConformance = lookupConformance( - instanceType, sendableMetatypeProto); - if (instanceConformance.isInvalid() || - instanceConformance.hasMissingConformance()) - break; - } - - // Every other metatype is Sendable. - } LLVM_FALLTHROUGH; case KnownProtocolKind::Copyable: diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index ff77c9dcf8c..5f2127f3602 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -8756,14 +8756,6 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const { } } -std::optional -AbstractFunctionDecl::getExecutionBehavior() const { - auto *attr = getAttrs().getAttribute(); - if (!attr) - return {}; - return attr->getBehavior(); -} - clang::PointerAuthQualifier VarDecl::getPointerAuthQualifier() const { if (auto *clangDecl = getClangDecl()) { if (auto *valueDecl = dyn_cast(clangDecl)) { @@ -8948,6 +8940,12 @@ void ParamDecl::setTypeRepr(TypeRepr *repr) { continue; } + if (auto *callerIsolated = + dyn_cast(unwrappedType)) { + setCallerIsolated(true); + unwrappedType = callerIsolated->getBase(); + } + break; } } @@ -11042,6 +11040,9 @@ AccessorDecl *AccessorDecl::createParsed( if (subscriptParam->isSending()) param->setSending(); + if (subscriptParam->isCallerIsolated()) + param->setCallerIsolated(); + newParams.push_back(param); } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index e5d7bfee031..140872ecbf9 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -2441,11 +2441,22 @@ bool Expr::isSelfExprOf(const AbstractFunctionDecl *AFD, bool sameBase) const { return false; } -TypeValueExpr *TypeValueExpr::createForDecl(DeclNameLoc loc, - GenericTypeParamDecl *paramDecl) { - auto &ctx = paramDecl->getASTContext(); +TypeValueExpr *TypeValueExpr::createForDecl(DeclNameLoc loc, TypeDecl *decl, + DeclContext *dc) { + auto &ctx = decl->getASTContext(); ASSERT(loc.isValid()); - return new (ctx) TypeValueExpr(loc, paramDecl); + auto repr = UnqualifiedIdentTypeRepr::create(ctx, loc, decl->createNameRef()); + repr->setValue(decl, dc); + return new (ctx) TypeValueExpr(repr); +} + +GenericTypeParamDecl *TypeValueExpr::getParamDecl() const { + auto declRefRepr = cast(getRepr()); + return cast(declRefRepr->getBoundDecl()); +} + +SourceRange TypeValueExpr::getSourceRange() const { + return getRepr()->getSourceRange(); } ExistentialArchetypeType *OpenExistentialExpr::getOpenedArchetype() const { diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index a788ec5eae1..eade9de82d2 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -12,6 +12,7 @@ #include "FeatureSet.h" +#include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericParamList.h" @@ -430,7 +431,7 @@ UNINTERESTING_FEATURE(StrictMemorySafety) UNINTERESTING_FEATURE(SafeInteropWrappers) UNINTERESTING_FEATURE(AssumeResilientCxxTypes) UNINTERESTING_FEATURE(ImportNonPublicCxxMembers) -UNINTERESTING_FEATURE(CXXForeignReferenceTypeInitializers) +UNINTERESTING_FEATURE(SuppressCXXForeignReferenceTypeInitializers) UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError) UNINTERESTING_FEATURE(AllowRuntimeSymbolDeclarations) @@ -475,6 +476,54 @@ static bool usesFeatureValueGenerics(Decl *decl) { return false; } +class UsesTypeValueExpr : public ASTWalker { +public: + bool used = false; + + PreWalkResult walkToExprPre(Expr *expr) override { + if (isa(expr)) { + used = true; + return Action::Stop(); + } + + return Action::Continue(expr); + } +}; + +static bool usesFeatureValueGenericsNameLookup(Decl *decl) { + // Be conservative and mark any function that has a TypeValueExpr in its body + // as having used this feature. It's a little difficult to fine grain this + // check because the following: + // + // func a() -> Int { + // A<123>.n + // } + // + // Would appear to have the same expression as something like: + // + // extension A where n == 123 { + // func b() -> Int { + // n + // } + // } + + auto fn = dyn_cast(decl); + + if (!fn) + return false; + + auto body = fn->getMacroExpandedBody(); + + if (!body) + return false; + + UsesTypeValueExpr utve; + + body->walk(utve); + + return utve.used; +} + static bool usesFeatureCoroutineAccessors(Decl *decl) { auto accessorDeclUsesFeatureCoroutineAccessors = [](AccessorDecl *accessor) { return requiresFeatureCoroutineAccessors(accessor->getAccessorKind()); @@ -507,42 +556,52 @@ static bool usesFeatureBuiltinEmplaceTypedThrows(Decl *decl) { return false; } -static bool usesFeatureExecutionAttribute(Decl *decl) { - if (!DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind::Execution, decl)) { - return false; - } - - if (decl->getAttrs().hasAttribute()) +static bool usesFeatureAsyncExecutionBehaviorAttributes(Decl *decl) { + // Explicit `@concurrent` attribute on the declaration. + if (decl->getAttrs().hasAttribute()) return true; - auto hasExecutionAttr = [](TypeRepr *R) { + // Explicit `nonisolated(nonsending)` attribute on the declaration. + if (auto *nonisolated = decl->getAttrs().getAttribute()) { + if (nonisolated->isNonSending()) + return true; + } + + auto hasCallerIsolatedAttr = [](TypeRepr *R) { if (!R) return false; return R->findIf([](TypeRepr *repr) { - if (auto *AT = dyn_cast(repr)) { - return llvm::any_of(AT->getAttrs(), [](TypeOrCustomAttr attr) { - if (auto *TA = attr.dyn_cast()) { - return isa(TA); - } - return false; - }); - } + if (isa(repr)) + return true; + + // We don't check for @concurrent here because it's + // not printed in type positions since it indicates + // old "nonisolated" state. + return false; }); }; - auto *VD = cast(decl); + auto *VD = dyn_cast(decl); + if (!VD) + return false; - // Check if any parameters that have `@execution` attribute. + // The declaration is going to be printed with `nonisolated(nonsending)` + // attribute. + if (getActorIsolation(VD).isCallerIsolationInheriting()) + return true; + + // Check if any parameters that have `nonisolated(nonsending)` attribute. if (auto *PL = VD->getParameterList()) { - for (auto *P : *PL) { - if (hasExecutionAttr(P->getTypeRepr())) - return true; - } + if (llvm::any_of(*PL, [&](const ParamDecl *P) { + return hasCallerIsolatedAttr(P->getTypeRepr()); + })) + return true; } - if (hasExecutionAttr(VD->getResultTypeRepr())) + // Check if result type has explicit `nonisolated(nonsending)` attribute. + if (hasCallerIsolatedAttr(VD->getResultTypeRepr())) return true; return false; diff --git a/lib/AST/GenericSignature.cpp b/lib/AST/GenericSignature.cpp index a0b672e6847..3ac5bc6f80b 100644 --- a/lib/AST/GenericSignature.cpp +++ b/lib/AST/GenericSignature.cpp @@ -371,6 +371,44 @@ bool GenericSignatureImpl::requiresProtocol(Type type, return getRequirementMachine()->requiresProtocol(type, proto); } +std::optional> +GenericSignatureImpl::prohibitsIsolatedConformance(Type type) const { + type = getReducedType(type); + + if (!type->isTypeParameter()) + return std::nullopt; + + // An isolated conformance cannot be used in a context where the type + // parameter can escape the isolation domain in which the conformance + // was formed. To establish this, we look for Sendable or SendableMetatype + // requirements on the type parameter itself. + ASTContext &ctx = type->getASTContext(); + auto sendableProto = ctx.getProtocol(KnownProtocolKind::Sendable); + auto sendableMetatypeProto = + ctx.getProtocol(KnownProtocolKind::SendableMetatype); + + // Check for a conformance requirement to SendableMetatype, which is + // implied by Sendable. + if (sendableMetatypeProto && requiresProtocol(type, sendableMetatypeProto)) { + // Check for a conformance requirement to Sendable and return that if + // it exists, because it's more recognizable and specific. + if (sendableProto && requiresProtocol(type, sendableProto)) + return std::make_pair(type, sendableProto); + + return std::make_pair(type, sendableMetatypeProto); + } + + // If this is a nested type, also check whether the parent type conforms to + // SendableMetatype, because one can derive this type from the parent type. + // FIXME: This is not a complete check, because there are other ways in which + // one might be able to derive this type. This needs to determine whether + // there is any path from a SendableMetatype-conforming type to this type. + if (auto depMemTy = type->getAs()) + return prohibitsIsolatedConformance(depMemTy->getBase()); + + return std::nullopt; +} + /// Determine whether the given dependent type is equal to a concrete type. bool GenericSignatureImpl::isConcreteType(Type type) const { assert(type->isTypeParameter() && "Expected a type parameter"); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index d9d81f5644e..f1eb3e9967f 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2739,6 +2739,18 @@ QualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, } } + // Qualified name lookup can find generic value parameters. + auto gpList = current->getGenericParams(); + + // .. But not in type contexts (yet) + if (!(options & NL_OnlyTypes) && gpList && !member.isSpecial()) { + auto gp = gpList->lookUpGenericParam(member.getBaseIdentifier()); + + if (gp && gp->isValue()) { + decls.push_back(gp); + } + } + // If we're not looking at a protocol and we're not supposed to // visit the protocols that this type conforms to, skip the next // step. @@ -3209,6 +3221,12 @@ directReferencesForTypeRepr(Evaluator &evaluator, ASTContext &ctx, isolated->getBase(), dc, options); } + case TypeReprKind::CallerIsolated: { + auto callerIsolated = cast(typeRepr); + return directReferencesForTypeRepr(evaluator, ctx, + callerIsolated->getBase(), dc, options); + } + case TypeReprKind::Composition: { auto composition = cast(typeRepr); for (auto component : composition->getTypes()) { diff --git a/lib/AST/PluginLoader.cpp b/lib/AST/PluginLoader.cpp index 59646d77211..271c484fc99 100644 --- a/lib/AST/PluginLoader.cpp +++ b/lib/AST/PluginLoader.cpp @@ -13,11 +13,12 @@ #include "swift/AST/PluginLoader.h" #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" -#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/DiagnosticsSema.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/SourceManager.h" #include "swift/Parse/Lexer.h" #include "llvm/Config/config.h" +#include "llvm/Support/PrefixMapper.h" #include "llvm/Support/VirtualFileSystem.h" using namespace swift; @@ -96,9 +97,45 @@ PluginLoader::getPluginMap() { map[moduleNameIdentifier] = {libPath, execPath}; }; + std::optional mapper; + if (!PathRemap.empty()) { + SmallVector prefixes; + llvm::MappedPrefix::transformJoinedIfValid(PathRemap, prefixes); + mapper.emplace(); + mapper->addRange(prefixes); + mapper->sort(); + } + auto remapPath = [&mapper](StringRef path) { + if (!mapper) + return path.str(); + return mapper->mapToString(path); + }; + auto fs = getPluginLoadingFS(Ctx); std::error_code ec; + auto validateLibrary = [&](StringRef path) -> llvm::Expected { + auto remappedPath = remapPath(path); + if (!Ctx.SearchPathOpts.ResolvedPluginVerification || path.empty()) + return remappedPath; + + auto currentStat = fs->status(remappedPath); + if (!currentStat) + return llvm::createFileError(remappedPath, currentStat.getError()); + + auto goldStat = Ctx.SourceMgr.getFileSystem()->status(path); + if (!goldStat) + return llvm::createStringError( + "cannot open gold reference library to compare"); + + // Compare the size for difference for now. + if (currentStat->getSize() != goldStat->getSize()) + return llvm::createStringError( + "plugin has changed since dependency scanning"); + + return remappedPath; + }; + for (auto &entry : Ctx.SearchPathOpts.PluginSearchOpts) { switch (entry.getKind()) { @@ -156,9 +193,17 @@ PluginLoader::getPluginMap() { // Respect resolved plugin config above other search path, and it can // overwrite plugins found by other options or previous resolved // configuration. - for (auto &moduleName : val.ModuleNames) - try_emplace(moduleName, val.LibraryPath, val.ExecutablePath, + for (auto &moduleName : val.ModuleNames) { + auto libPath = validateLibrary(val.LibraryPath); + if (!libPath) { + Ctx.Diags.diagnose(SourceLoc(), diag::resolved_macro_changed, + remapPath(val.LibraryPath), + toString(libPath.takeError())); + continue; + } + try_emplace(moduleName, *libPath, remapPath(val.ExecutablePath), /*overwrite*/ true); + } continue; } } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 66ce36b9db3..1af4bc152f3 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1186,6 +1186,15 @@ bool ExistentialLayout::isExistentialWithError(ASTContext &ctx) const { return false; } +bool ExistentialLayout::containsNonMarkerProtocols() const { + for (auto proto : getProtocols()) { + if (!proto->isMarkerProtocol()) + return true; + } + + return false; +} + LayoutConstraint ExistentialLayout::getLayoutConstraint() const { if (hasExplicitAnyObject) { return LayoutConstraint::getLayoutConstraint( @@ -5006,11 +5015,15 @@ StringRef swift::getNameForParamSpecifier(ParamSpecifier specifier) { llvm_unreachable("bad ParamSpecifier"); } -std::optional -TypeBase::getConcurrencyDiagnosticBehaviorLimit(DeclContext *declCtx) const { - auto *self = const_cast(this); +static std::optional +getConcurrencyDiagnosticBehaviorLimitRec( + Type type, DeclContext *declCtx, + llvm::SmallPtrSetImpl &visited) { + if (auto *nomDecl = type->getNominalOrBoundGenericNominal()) { + // If we have already seen this type, treat it as having no limit. + if (!visited.insert(nomDecl).second) + return std::nullopt; - if (auto *nomDecl = self->getNominalOrBoundGenericNominal()) { // First try to just grab the exact concurrency diagnostic behavior. if (auto result = swift::getConcurrencyDiagnosticBehaviorLimit(nomDecl, declCtx)) { @@ -5021,11 +5034,12 @@ TypeBase::getConcurrencyDiagnosticBehaviorLimit(DeclContext *declCtx) const { // merging our fields if we have a struct. if (auto *structDecl = dyn_cast(nomDecl)) { std::optional diagnosticBehavior; - auto substMap = self->getContextSubstitutionMap(); + auto substMap = type->getContextSubstitutionMap(); for (auto storedProperty : structDecl->getStoredProperties()) { auto lhs = diagnosticBehavior.value_or(DiagnosticBehavior::Unspecified); auto astType = storedProperty->getInterfaceType().subst(substMap); - auto rhs = astType->getConcurrencyDiagnosticBehaviorLimit(declCtx); + auto rhs = getConcurrencyDiagnosticBehaviorLimitRec(astType, declCtx, + visited); auto result = lhs.merge(rhs.value_or(DiagnosticBehavior::Unspecified)); if (result != DiagnosticBehavior::Unspecified) diagnosticBehavior = result; @@ -5036,13 +5050,14 @@ TypeBase::getConcurrencyDiagnosticBehaviorLimit(DeclContext *declCtx) const { // When attempting to determine the diagnostic behavior limit of a tuple, just // merge for each of the elements. - if (auto *tupleType = self->getAs()) { + if (auto *tupleType = type->getAs()) { std::optional diagnosticBehavior; for (auto tupleType : tupleType->getElements()) { auto lhs = diagnosticBehavior.value_or(DiagnosticBehavior::Unspecified); auto type = tupleType.getType()->getCanonicalType(); - auto rhs = type->getConcurrencyDiagnosticBehaviorLimit(declCtx); + auto rhs = getConcurrencyDiagnosticBehaviorLimitRec(type, declCtx, + visited); auto result = lhs.merge(rhs.value_or(DiagnosticBehavior::Unspecified)); if (result != DiagnosticBehavior::Unspecified) diagnosticBehavior = result; @@ -5050,7 +5065,21 @@ TypeBase::getConcurrencyDiagnosticBehaviorLimit(DeclContext *declCtx) const { return diagnosticBehavior; } - return {}; + // Metatypes that aren't Sendable were introduced in Swift 6.2, so downgrade + // them to warnings prior to Swift 7. + if (type->is()) { + if (!type->getASTContext().LangOpts.isSwiftVersionAtLeast(7)) + return DiagnosticBehavior::Warning; + } + + return std::nullopt; +} + +std::optional +TypeBase::getConcurrencyDiagnosticBehaviorLimit(DeclContext *declCtx) const { + auto *self = const_cast(this); + llvm::SmallPtrSet visited; + return getConcurrencyDiagnosticBehaviorLimitRec(Type(self), declCtx, visited); } GenericTypeParamKind diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 0980e8dacfa..318a9214013 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -923,6 +923,11 @@ ValueOwnership OwnershipTypeRepr::getValueOwnership() const { return ParamDecl::getValueOwnershipForSpecifier(getSpecifier()); } +void CallerIsolatedTypeRepr::printImpl(ASTPrinter &Printer, + const PrintOptions &Opts) const { + Printer.printKeyword("nonisolated(nonsending)", Opts); +} + void PlaceholderTypeRepr::printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const { Printer.printText("_"); diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index b409845e7ed..d82d391df62 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -117,8 +117,6 @@ extension ASTGenVisitor { let attrName = identTy.name.rawText let attrKind = BridgedDeclAttrKind(from: attrName.bridged) switch attrKind { - case .execution: - return handle(self.generateExecutionAttr(attribute: node)?.asDeclAttribute) case .ABI: return handle(self.generateABIAttr(attribute: node)?.asDeclAttribute) case .alignment: @@ -197,6 +195,8 @@ extension ASTGenVisitor { return handle(self.generateSimpleDeclAttr(attribute: node, kind: .atReasync)) case .rethrows: return handle(self.generateSimpleDeclAttr(attribute: node, kind: .atRethrows)) + case .concurrent: + return handle(self.generateSimpleDeclAttr(attribute: node, kind: .concurrent)) case .none where attrName == "_unavailableInEmbedded": return handle(self.generateUnavailableInEmbeddedAttr(attribute: node)?.asDeclAttribute) @@ -359,33 +359,6 @@ extension ASTGenVisitor { return handle(self.generateCustomAttr(attribute: node)?.asDeclAttribute) } - /// E.g.: - /// ``` - /// @execution(concurrent) - /// @execution(caller) - /// ``` - func generateExecutionAttr(attribute node: AttributeSyntax) -> BridgedExecutionAttr? { - let behavior: BridgedExecutionKind? = self.generateSingleAttrOption( - attribute: node, - { - switch $0.rawText { - case "concurrent": return .concurrent - case "caller": return .caller - default: return nil - } - } - ) - guard let behavior else { - return nil - } - return .createParsed( - self.ctx, - atLoc: self.generateSourceLoc(node.atSign), - range: self.generateAttrSourceRange(node), - behavior: behavior - ) - } - /// E.g.: /// ``` /// @abi(func fn()) @@ -1415,27 +1388,25 @@ extension ASTGenVisitor { // FIXME: This is a decl modifier func generateNonisolatedAttr(attribute node: AttributeSyntax) -> BridgedNonisolatedAttr? { - let isUnsafe = self.generateSingleAttrOption( + let modifier: BridgedNonIsolatedModifier? = self.generateSingleAttrOption( attribute: node, { switch $0.rawText { - case "unsafe": - return true - default: - // FIXME: Diagnose. - return nil + case "unsafe": return .unsafe + case "nonsending": return .nonSending + default: return nil } }, - valueIfOmitted: false + valueIfOmitted: BridgedNonIsolatedModifier.none ) - guard let isUnsafe else { + guard let modifier else { return nil } return .createParsed( self.ctx, atLoc: self.generateSourceLoc(node.atSign), range: self.generateAttrSourceRange(node), - isUnsafe: isUnsafe + modifier: modifier ) } @@ -2409,12 +2380,14 @@ extension ASTGenVisitor { } func generateNonisolatedAttr(declModifier node: DeclModifierSyntax) -> BridgedNonisolatedAttr? { - let isUnsafe: Bool + let modifier: BridgedNonIsolatedModifier switch node.detail?.detail.rawText { case "unsafe": - isUnsafe = true + modifier = .unsafe + case "nonsending": + modifier = .nonSending case nil: - isUnsafe = false + modifier = .none case let text?: // TODO: Diagnose _ = text @@ -2425,7 +2398,7 @@ extension ASTGenVisitor { self.ctx, atLoc: nil, range: self.generateSourceRange(node), - isUnsafe: isUnsafe + modifier: modifier ) } diff --git a/lib/ASTGen/Sources/ASTGen/TypeAttrs.swift b/lib/ASTGen/Sources/ASTGen/TypeAttrs.swift index 0aea4fe145c..ede0bb3fb31 100644 --- a/lib/ASTGen/Sources/ASTGen/TypeAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/TypeAttrs.swift @@ -42,6 +42,7 @@ extension ASTGenVisitor { // Simple type attributes. case .autoclosure, .addressable, + .concurrent, .escaping, .noEscape, .noDerivative, @@ -75,9 +76,6 @@ extension ASTGenVisitor { case .differentiable: return (self.generateDifferentiableTypeAttr(attribute: node)?.asTypeAttribute) .map(BridgedTypeOrCustomAttr.typeAttr(_:)) - case .execution: - return (self.generateExecutionTypeAttr(attribute: node)?.asTypeAttribute) - .map(BridgedTypeOrCustomAttr.typeAttr(_:)) case .opaqueReturnTypeOf: return (self.generateOpaqueReturnTypeOfTypeAttr(attribute: node)?.asTypeAttribute) .map(BridgedTypeOrCustomAttr.typeAttr(_:)) @@ -239,34 +237,6 @@ extension ASTGenVisitor { kindLoc: differentiabilityLoc ) } - - func generateExecutionTypeAttr(attribute node: AttributeSyntax) -> BridgedExecutionTypeAttr? { - let behaviorLoc = self.generateSourceLoc(node.arguments) - let behavior: BridgedExecutionTypeAttrExecutionKind? = self.generateSingleAttrOption( - attribute: node, - { - switch $0.rawText { - case "concurrent": return .concurrent - case "caller": return .caller - default: - // TODO: Diagnose. - return nil - } - } - ) - guard let behavior else { - return nil - } - - return .createParsed( - self.ctx, - atLoc: self.generateSourceLoc(node.atSign), - nameLoc: self.generateSourceLoc(node.attributeName), - parensRange: self.generateAttrParensRange(attribute: node), - behavior: behavior, - behaviorLoc: behaviorLoc - ) - } func generateIsolatedTypeAttr(attribute node: AttributeSyntax) -> BridgedIsolatedTypeAttr? { let isolationKindLoc = self.generateSourceLoc(node.arguments) diff --git a/lib/ASTGen/Sources/ASTGen/Types.swift b/lib/ASTGen/Sources/ASTGen/Types.swift index a0cd884131f..2aba9eb2c09 100644 --- a/lib/ASTGen/Sources/ASTGen/Types.swift +++ b/lib/ASTGen/Sources/ASTGen/Types.swift @@ -362,6 +362,7 @@ extension ASTGenVisitor { var constLoc: BridgedSourceLoc = nil var sendingLoc: BridgedSourceLoc = nil var lifetimeEntry: BridgedLifetimeEntry? = nil + var nonisolatedLoc: BridgedSourceLoc = nil // TODO: Diagnostics for duplicated specifiers, and ordering. for node in node.specifiers { @@ -398,6 +399,8 @@ extension ASTGenVisitor { ), sources: node.arguments.lazy.compactMap(self.generateLifetimeDescriptor(lifetimeSpecifierArgument:)).bridgedArray(in: self) ) + case .nonisolatedTypeSpecifier(_): + nonisolatedLoc = loc } } @@ -454,6 +457,14 @@ extension ASTGenVisitor { ).asTypeRepr } + if nonisolatedLoc.isValid { + type = BridgedCallerIsolatedTypeRepr.createParsed( + self.ctx, + base: type, + specifierLoc: nonisolatedLoc + ).asTypeRepr + } + return type } } diff --git a/lib/Basic/BlockList.cpp b/lib/Basic/BlockList.cpp index f89eccbc238..0e9af708dc7 100644 --- a/lib/Basic/BlockList.cpp +++ b/lib/Basic/BlockList.cpp @@ -115,8 +115,10 @@ void swift::BlockListStore::Implementation::addConfigureFilePath(StringRef path) SM.getLLVMSourceMgr()); for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) { assert(DI != Stream.end() && "Failed to read a document"); - yaml::Node *N = DI->getRoot(); - for (auto &pair: *dyn_cast(N)) { + auto *MapNode = dyn_cast(DI->getRoot()); + if (!MapNode) + continue; + for (auto &pair: *MapNode) { std::string key = getScalaString(pair.getKey()); auto action = llvm::StringSwitch(key) #define BLOCKLIST_ACTION(X) .Case(#X, BlockListAction::X) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index c22a49bc18d..8cc8e1bf8d4 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2555,8 +2555,8 @@ namespace { result->addMember(ctor); } } else { - if (Impl.SwiftContext.LangOpts.hasFeature( - Feature::CXXForeignReferenceTypeInitializers)) { + if (!Impl.SwiftContext.LangOpts.hasFeature( + Feature::SuppressCXXForeignReferenceTypeInitializers)) { assert( isa(result) && "Expected result to be a ClassDecl as it cannot be a StructDecl"); @@ -3625,7 +3625,18 @@ namespace { isa(decl) ? cast(decl)->getReturnType() : cast(decl)->getReturnType(); - if (isForeignReferenceTypeWithoutImmortalAttrs(retType)) { + clang::QualType pointeeType = retType; + if (retType->isPointerType() || retType->isReferenceType()) { + pointeeType = retType->getPointeeType(); + } + + clang::RecordDecl *recordDecl = nullptr; + if (const auto *recordType = pointeeType->getAs()) { + recordDecl = recordType->getDecl(); + } + + if (recordDecl && recordHasReferenceSemantics(recordDecl) && + !hasImmortalAttrs(recordDecl)) { if (returnsRetainedAttrIsPresent && returnsUnretainedAttrIsPresent) { Impl.diagnose(loc, diag::both_returns_retained_returns_unretained, decl); @@ -8826,8 +8837,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { // Hard-code @actorIndependent, until Objective-C clients start // using nonisolated. if (swiftAttr->getAttribute() == "@actorIndependent") { - auto attr = new (SwiftContext) - NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true); + auto attr = NonisolatedAttr::createImplicit(SwiftContext); MappedDecl->getAttrs().add(attr); continue; } @@ -8960,8 +8970,8 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { auto *mappedVar = cast(MappedDecl); if (mappedVar->isStatic() && mappedVar->isLet() && isNSNotificationName(cast(ClangDecl)->getType())) { - MappedDecl->getAttrs().add(new (SwiftContext) NonisolatedAttr( - /*unsafe=*/false, /*implicit=*/true)); + MappedDecl->getAttrs().add( + NonisolatedAttr::createImplicit(SwiftContext)); } } } diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index ba072a640b6..25e044e1274 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1967,6 +1967,14 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, case clang::OverloadedOperatorKind::OO_GreaterEqual: case clang::OverloadedOperatorKind::OO_AmpAmp: case clang::OverloadedOperatorKind::OO_PipePipe: { + // If the operator has a parameter that is an rvalue reference, it would + // cause name lookup collision with an overload that has lvalue reference + // parameter, if it exists. + for (auto paramDecl : functionDecl->parameters()) { + if (paramDecl->getType()->isRValueReferenceType()) + return ImportedName(); + } + auto operatorName = isa(functionDecl) ? getOperatorName(swiftCtx, op) diff --git a/lib/ClangImporter/SwiftDeclSynthesizer.cpp b/lib/ClangImporter/SwiftDeclSynthesizer.cpp index 9c835ad038d..9b33324c55a 100644 --- a/lib/ClangImporter/SwiftDeclSynthesizer.cpp +++ b/lib/ClangImporter/SwiftDeclSynthesizer.cpp @@ -436,9 +436,7 @@ ValueDecl *SwiftDeclSynthesizer::createConstant(Identifier name, // Mark the function transparent so that we inline it away completely. func->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true)); - auto nonisolatedAttr = - new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true); - var->getAttrs().add(nonisolatedAttr); + var->getAttrs().add(NonisolatedAttr::createImplicit(C)); // Set the function up as the getter. ImporterImpl.makeComputed(var, func, nullptr); @@ -2538,6 +2536,9 @@ llvm::SmallVector SwiftDeclSynthesizer::synthesizeStaticFactoryForCXXForeignRef( const clang::CXXRecordDecl *cxxRecordDecl) { + if (cxxRecordDecl->isAbstract()) + return {}; + clang::ASTContext &clangCtx = cxxRecordDecl->getASTContext(); clang::Sema &clangSema = ImporterImpl.getClangSema(); diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 15a268e8b2f..f2799cd7995 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -3142,7 +3142,7 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, Printer << "@isolated(any) "; return nullptr; case Node::Kind::NonIsolatedCallerFunctionType: - Printer << "@execution(caller) "; + Printer << "nonisolated(nonsending) "; return nullptr; case Node::Kind::SendingResultFunctionType: Printer << "sending "; diff --git a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp index 7dd4d09569e..13a5486b5a8 100644 --- a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp +++ b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp @@ -218,9 +218,6 @@ bool ModuleDependenciesCacheDeserializer::readGraph( bool hasCurrentModule = false; std::string currentModuleName; - std::vector currentModuleImports; - std::vector currentOptionalModuleImports; - std::vector importedSwiftDependenciesIDs; std::vector importedClangDependenciesIDs; std::vector crossImportOverlayDependenciesIDs; @@ -235,18 +232,8 @@ bool ModuleDependenciesCacheDeserializer::readGraph( std::vector auxiliaryFiles; auto addCommonDependencyInfo = - [¤tModuleImports, ¤tOptionalModuleImports, - &importedClangDependenciesIDs, &auxiliaryFiles, + [&importedClangDependenciesIDs, &auxiliaryFiles, ¯oDependencies](ModuleDependencyInfo &moduleDep) { - // Add imports of this module - for (const auto &moduleName : currentModuleImports) - moduleDep.addModuleImport(moduleName.importIdentifier, - moduleName.isExported); - // Add optional imports of this module - for (const auto &moduleName : currentOptionalModuleImports) - moduleDep.addOptionalModuleImport(moduleName.importIdentifier, - moduleName.isExported); - // Add qualified dependencies of this module moduleDep.setImportedClangDependencies(importedClangDependenciesIDs); @@ -297,7 +284,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph( llvm::report_fatal_error("Bad bridging module dependencies"); llvm::StringSet<> alreadyAdded; std::vector bridgingModuleDepIDs; - for (const auto &mod : bridgingModuleDeps.value()) + for (const auto &mod : *bridgingModuleDeps) bridgingModuleDepIDs.push_back( ModuleDependencyID{mod, ModuleDependencyKind::Clang}); moduleDep.setHeaderClangDependencies(bridgingModuleDepIDs); @@ -364,7 +351,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph( llvm::report_fatal_error("Bad link library identifier"); LinkLibraries.emplace_back( - libraryIdentifier.value(), + *libraryIdentifier, isFramework ? LibraryKind::Framework : LibraryKind::Library, isStatic, shouldForceLoad); @@ -395,8 +382,8 @@ bool ModuleDependenciesCacheDeserializer::readGraph( llvm::report_fatal_error("Bad macro dependency: no executable path"); MacroDependencies.push_back( - {macroModuleName.value(), - MacroPluginDependency{libraryPath.value(), executablePath.value()}}); + {*macroModuleName, + MacroPluginDependency{*libraryPath, *executablePath}}); break; } @@ -422,10 +409,14 @@ bool ModuleDependenciesCacheDeserializer::readGraph( if (!bufferIdentifier) llvm::report_fatal_error( "Bad import statement info: no buffer identifier"); - ImportStatements.push_back(ScannerImportStatementInfo( - importIdentifier.value(), isExported, - ScannerImportStatementInfo::ImportDiagnosticLocationInfo( - bufferIdentifier.value(), lineNumber, columnNumber))); + if (bufferIdentifier->empty()) + ImportStatements.push_back(ScannerImportStatementInfo( + *importIdentifier, isExported)); + else + ImportStatements.push_back(ScannerImportStatementInfo( + *importIdentifier, isExported, + ScannerImportStatementInfo::ImportDiagnosticLocationInfo( + *bufferIdentifier, lineNumber, columnNumber))); break; } @@ -470,18 +461,16 @@ bool ModuleDependenciesCacheDeserializer::readGraph( getImportStatementInfoArray(moduleImportsArrayID); if (!optionalImportStatementInfos) llvm::report_fatal_error("Bad direct Swift dependencies: no imports"); - importStatements = optionalImportStatementInfos.value(); + importStatements = *optionalImportStatementInfos; auto optionalOptionalImportStatementInfos = getOptionalImportStatementInfoArray(optionalImportsArrayID); - if (!optionalOptionalImportStatementInfos) - llvm::report_fatal_error( - "Bad direct Swift dependencies: no optional imports"); - optionalImportStatements = optionalOptionalImportStatementInfos.value(); + if (optionalOptionalImportStatementInfos) + optionalImportStatements = *optionalOptionalImportStatementInfos; auto optionalAuxiliaryFiles = getStringArray(AuxiliaryFilesArrayID); - if (optionalAuxiliaryFiles.has_value()) - for (const auto &af : optionalAuxiliaryFiles.value()) + if (optionalAuxiliaryFiles) + for (const auto &af : *optionalAuxiliaryFiles) auxiliaryFiles.push_back(af); auto optionalImportedSwiftDependenciesIDs = @@ -490,7 +479,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph( llvm::report_fatal_error( "Bad direct Swift dependencies: no qualified dependencies"); importedSwiftDependenciesIDs = - optionalImportedSwiftDependenciesIDs.value(); + *optionalImportedSwiftDependenciesIDs; auto optionalImportedClangDependenciesIDs = getModuleDependencyIDArray(importedClangDependenciesIDsArrayID); @@ -498,7 +487,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph( llvm::report_fatal_error( "Bad direct Clang dependencies: no qualified dependencies"); importedClangDependenciesIDs = - optionalImportedClangDependenciesIDs.value(); + *optionalImportedClangDependenciesIDs; auto optionalCrossImportOverlayDependenciesIDs = getModuleDependencyIDArray(crossImportOverlayDependenciesIDsArrayID); @@ -506,14 +495,14 @@ bool ModuleDependenciesCacheDeserializer::readGraph( llvm::report_fatal_error( "Bad Cross-Import Overlay dependencies: no qualified dependencies"); crossImportOverlayDependenciesIDs = - optionalCrossImportOverlayDependenciesIDs.value(); + *optionalCrossImportOverlayDependenciesIDs; auto optionalSwiftOverlayDependenciesIDs = getModuleDependencyIDArray(swiftOverlayDependenciesIDsArrayID); if (!optionalSwiftOverlayDependenciesIDs) llvm::report_fatal_error( "Bad Swift Overlay dependencies: no qualified dependencies"); - swiftOverlayDependenciesIDs = optionalSwiftOverlayDependenciesIDs.value(); + swiftOverlayDependenciesIDs = *optionalSwiftOverlayDependenciesIDs; auto optionalLinkLibraries = getLinkLibraryArray(linkLibraryArrayID); if (!optionalLinkLibraries) @@ -587,7 +576,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph( // Form the dependencies storage object auto moduleDep = ModuleDependencyInfo::forSwiftInterfaceModule( - optionalSwiftInterfaceFile.value(), compiledCandidatesRefs, + *optionalSwiftInterfaceFile, compiledCandidatesRefs, buildCommandRefs, importStatements, optionalImportStatements, linkLibraries, isFramework, isStatic, *rootFileSystemID, *moduleCacheKey, *userModuleVersion); @@ -711,7 +700,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph( // Form the dependencies storage object auto moduleDep = ModuleDependencyInfo::forSwiftBinaryModule( *compiledModulePath, *moduleDocPath, *moduleSourceInfoPath, - currentModuleImports, currentOptionalModuleImports, linkLibraries, + importStatements, optionalImportStatements, linkLibraries, *headerImport, *definingInterfacePath, isFramework, isStatic, *moduleCacheKey, *userModuleVersion); @@ -984,7 +973,7 @@ ModuleDependenciesCacheDeserializer::getOptionalImportStatementInfoArray( std::optional> ModuleDependenciesCacheDeserializer::getModuleDependencyIDArray(unsigned n) { auto encodedIdentifierStringArray = getStringArray(n); - if (encodedIdentifierStringArray.has_value()) { + if (encodedIdentifierStringArray) { static const std::string textualPrefix("swiftTextual"); static const std::string binaryPrefix("swiftBinary"); static const std::string placeholderPrefix("swiftPlaceholder"); @@ -1288,8 +1277,8 @@ void ModuleDependenciesCacheSerializer::writeLinkLibraries( for (const auto &entry : modMap) { ModuleDependencyID moduleID = {entry.getKey().str(), kind}; auto optionalDependencyInfo = cache.findDependency(moduleID); - assert(optionalDependencyInfo.has_value() && "Expected dependency info."); - auto dependencyInfo = optionalDependencyInfo.value(); + assert(optionalDependencyInfo && "Expected dependency info."); + auto dependencyInfo = *optionalDependencyInfo; unsigned numLLs = writeLinkLibraryInfos(*dependencyInfo); moduleLLArrayMap.insert({moduleID, std::make_pair(lastLLIndex, numLLs)}); lastLLIndex += numLLs; @@ -1343,8 +1332,8 @@ void ModuleDependenciesCacheSerializer::writeMacroDependencies( for (const auto &entry : modMap) { ModuleDependencyID moduleID = {entry.getKey().str(), kind}; auto optionalDependencyInfo = cache.findDependency(moduleID); - assert(optionalDependencyInfo.has_value() && "Expected dependency info."); - auto dependencyInfo = optionalDependencyInfo.value(); + assert(optionalDependencyInfo && "Expected dependency info."); + auto dependencyInfo = *optionalDependencyInfo; unsigned numMDs = writeMacroDependencies(*dependencyInfo); moduleMacroDepArrayMap.insert( {moduleID, std::make_pair(lastMDIndex, numMDs)}); @@ -1399,11 +1388,11 @@ void ModuleDependenciesCacheSerializer::writeImportStatementInfos( for (auto kind = ModuleDependencyKind::FirstKind; kind != ModuleDependencyKind::LastKind; ++kind) { auto modMap = cache.getDependenciesMap(kind); - for (const auto &entry : modMap) { - ModuleDependencyID moduleID = {entry.getKey().str(), kind}; + for (const auto &entry : modMap.keys()) { + ModuleDependencyID moduleID = {entry.str(), kind}; auto optionalDependencyInfo = cache.findDependency(moduleID); - assert(optionalDependencyInfo.has_value() && "Expected dependency info."); - auto dependencyInfo = optionalDependencyInfo.value(); + assert(optionalDependencyInfo && "Expected dependency info."); + auto dependencyInfo = *optionalDependencyInfo; auto numImportInfos = writeImportStatementInfos(*dependencyInfo, /* optional */ false); @@ -1414,22 +1403,28 @@ void ModuleDependenciesCacheSerializer::writeImportStatementInfos( auto numOptionalImportInfos = writeImportStatementInfos(*dependencyInfo, /* optional */ true); optionalImportInfoArrayMap.insert( - {moduleID, std::make_pair(lastImportInfoIndex, numImportInfos)}); + {moduleID, std::make_pair(lastImportInfoIndex, numOptionalImportInfos)}); lastImportInfoIndex += numOptionalImportInfos; } } unsigned lastImportInfoArrayIndex = 1; + unsigned lastOptionalImportInfoArrayIndex = 1; for (auto kind = ModuleDependencyKind::FirstKind; kind != ModuleDependencyKind::LastKind; ++kind) { auto modMap = cache.getDependenciesMap(kind); - for (const auto &entry : modMap) { - ModuleDependencyID moduleID = {entry.getKey().str(), kind}; + for (const auto &entry : modMap.keys()) { + ModuleDependencyID moduleID = {entry.str(), kind}; auto entries = importInfoArrayMap.at(moduleID); - if (entries.second == 0) - continue; - writeImportStatementInfosArray(entries.first, entries.second); - ImportInfosArrayIDsMap.insert({moduleID, lastImportInfoArrayIndex++}); + if (entries.second != 0) { + writeImportStatementInfosArray(entries.first, entries.second); + ImportInfosArrayIDsMap.insert({moduleID, lastImportInfoArrayIndex++}); + } + auto optionalEntries = optionalImportInfoArrayMap.at(moduleID); + if (optionalEntries.second != 0) { + writeImportStatementInfosArray(optionalEntries.first, optionalEntries.second); + OptionalImportInfosArrayIDsMap.insert({moduleID, lastOptionalImportInfoArrayIndex++}); + } } } } @@ -1440,22 +1435,31 @@ unsigned ModuleDependenciesCacheSerializer::writeImportStatementInfos( size_t count = 0; auto emitImportStatementInfo = [this, &count](const auto &importInfo, bool isOptional) { - for (auto &importLoc : importInfo.importLocations) { + if (importInfo.importLocations.empty()) { ImportStatementLayout::emitRecord( Out, ScratchRecord, AbbrCodes[ImportStatementLayout::Code], getIdentifier(importInfo.importIdentifier), - getIdentifier(importLoc.bufferIdentifier), importLoc.lineNumber, - importLoc.columnNumber, isOptional, importInfo.isExported); + 0, 0, 0, isOptional, importInfo.isExported); count++; + } else { + for (auto &importLoc : importInfo.importLocations) { + ImportStatementLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[ImportStatementLayout::Code], + getIdentifier(importInfo.importIdentifier), + getIdentifier(importLoc.bufferIdentifier), importLoc.lineNumber, + importLoc.columnNumber, isOptional, importInfo.isExported); + count++; + } } }; - for (auto &importInfo : dependencyInfo.getModuleImports()) - emitImportStatementInfo(importInfo, false); - - for (auto &importInfo : dependencyInfo.getOptionalModuleImports()) - emitImportStatementInfo(importInfo, true); - + if (!optional) { + for (auto &importInfo : dependencyInfo.getModuleImports()) + emitImportStatementInfo(importInfo, false); + } else { + for (auto &importInfo : dependencyInfo.getOptionalModuleImports()) + emitImportStatementInfo(importInfo, true); + } return count; } @@ -1500,8 +1504,8 @@ void ModuleDependenciesCacheSerializer::writeModuleInfo( getIdentifier(swiftTextDeps->swiftInterfaceFile); unsigned bridgingHeaderFileId = swiftTextDeps->textualModuleDetails.bridgingHeaderFile - ? getIdentifier(swiftTextDeps->textualModuleDetails - .bridgingHeaderFile.value()) + ? getIdentifier(*(swiftTextDeps->textualModuleDetails + .bridgingHeaderFile)) : 0; SwiftInterfaceModuleDetailsLayout::emitRecord( Out, ScratchRecord, AbbrCodes[SwiftInterfaceModuleDetailsLayout::Code], @@ -1529,8 +1533,8 @@ void ModuleDependenciesCacheSerializer::writeModuleInfo( assert(swiftSourceDeps); unsigned bridgingHeaderFileId = swiftSourceDeps->textualModuleDetails.bridgingHeaderFile - ? getIdentifier(swiftSourceDeps->textualModuleDetails - .bridgingHeaderFile.value()) + ? getIdentifier(*(swiftSourceDeps->textualModuleDetails + .bridgingHeaderFile)) : 0; SwiftSourceModuleDetailsLayout::emitRecord( Out, ScratchRecord, AbbrCodes[SwiftSourceModuleDetailsLayout::Code], @@ -1732,20 +1736,14 @@ void ModuleDependenciesCacheSerializer::collectStringsAndArrays( for (auto kind = ModuleDependencyKind::FirstKind; kind != ModuleDependencyKind::LastKind; ++kind) { auto modMap = cache.getDependenciesMap(kind); - for (const auto &entry : modMap) { - ModuleDependencyID moduleID = {entry.getKey().str(), kind}; + for (const auto &entry : modMap.keys()) { + ModuleDependencyID moduleID = {entry.str(), kind}; auto optionalDependencyInfo = cache.findDependency(moduleID); - assert(optionalDependencyInfo.has_value() && "Expected dependency info."); - auto dependencyInfo = optionalDependencyInfo.value(); + assert(optionalDependencyInfo && "Expected dependency info."); + auto dependencyInfo = *optionalDependencyInfo; // Add the module's name addIdentifier(moduleID.ModuleName); - // Map import infos to their respective module identifiers - auto importInfoArrayToIdentifier = - [](const auto &importInfo) -> std::string { - return importInfo.importIdentifier; - }; - for (const auto &ll : dependencyInfo->getLinkLibraries()) addIdentifier(ll.getName().str()); @@ -1754,22 +1752,18 @@ void ModuleDependenciesCacheSerializer::collectStringsAndArrays( addIdentifier(md.second.LibraryPath); addIdentifier(md.second.ExecutablePath); } + + for (const auto &ii : dependencyInfo->getModuleImports()) { + addIdentifier(ii.importIdentifier); + for (const auto &il : ii.importLocations) + addIdentifier(il.bufferIdentifier); + } - // Add the module's imports - std::vector importIdentifiers; - llvm::transform(dependencyInfo->getModuleImports(), - std::back_inserter(importIdentifiers), - importInfoArrayToIdentifier); - std::vector optionalImportIdentifiers; - llvm::transform(dependencyInfo->getOptionalModuleImports(), - std::back_inserter(optionalImportIdentifiers), - importInfoArrayToIdentifier); - - addStringArray(moduleID, ModuleIdentifierArrayKind::DependencyImports, - importIdentifiers); - addStringArray(moduleID, - ModuleIdentifierArrayKind::OptionalDependencyImports, - optionalImportIdentifiers); + for (const auto &oii : dependencyInfo->getOptionalModuleImports()) { + addIdentifier(oii.importIdentifier); + for (const auto &oil : oii.importLocations) + addIdentifier(oil.bufferIdentifier); + } addDependencyIDArray( moduleID, ModuleIdentifierArrayKind::ImportedSwiftDependenciesIDs, @@ -1806,9 +1800,9 @@ void ModuleDependenciesCacheSerializer::collectStringsAndArrays( addStringArray(moduleID, ModuleIdentifierArrayKind::BuildCommandLine, swiftTextDeps->textualModuleDetails.buildCommandLine); addIdentifier(swiftTextDeps->contextHash); - if (swiftTextDeps->textualModuleDetails.bridgingHeaderFile.has_value()) + if (swiftTextDeps->textualModuleDetails.bridgingHeaderFile) addIdentifier( - swiftTextDeps->textualModuleDetails.bridgingHeaderFile.value()); + *(swiftTextDeps->textualModuleDetails.bridgingHeaderFile)); addStringArray(moduleID, ModuleIdentifierArrayKind::SourceFiles, std::vector()); addStringArray(moduleID, ModuleIdentifierArrayKind::BridgingSourceFiles, @@ -1855,10 +1849,9 @@ void ModuleDependenciesCacheSerializer::collectStringsAndArrays( case swift::ModuleDependencyKind::SwiftSource: { auto swiftSourceDeps = dependencyInfo->getAsSwiftSourceModule(); assert(swiftSourceDeps); - if (swiftSourceDeps->textualModuleDetails.bridgingHeaderFile - .has_value()) + if (swiftSourceDeps->textualModuleDetails.bridgingHeaderFile) addIdentifier( - swiftSourceDeps->textualModuleDetails.bridgingHeaderFile.value()); + *(swiftSourceDeps->textualModuleDetails.bridgingHeaderFile)); addStringArray(moduleID, ModuleIdentifierArrayKind::SourceFiles, swiftSourceDeps->sourceFiles); addStringArray( diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index f032321f773..dbe75b944e1 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -201,6 +201,7 @@ ModuleDependencyScanningWorker::ModuleDependencyScanningWorker( ScanASTContext.SourceMgr, Diagnostics)); auto loader = std::make_unique( *workerASTContext, /*DepTracker=*/nullptr, + workerCompilerInvocation->getFrontendOptions().CacheReplayPrefixMap, workerCompilerInvocation->getFrontendOptions().DisableSandbox); workerASTContext->setPluginLoader(std::move(loader)); diff --git a/lib/DependencyScan/ScanDependencies.cpp b/lib/DependencyScan/ScanDependencies.cpp index 12d900ffd7c..d13e926e8a8 100644 --- a/lib/DependencyScan/ScanDependencies.cpp +++ b/lib/DependencyScan/ScanDependencies.cpp @@ -214,9 +214,23 @@ private: depInfo.addMacroDependency(macro.first(), macro.second.LibraryPath, macro.second.ExecutablePath); + bool needPathRemapping = instance.getInvocation() + .getSearchPathOptions() + .ResolvedPluginVerification && + cache.getScanService().hasPathMapping(); + auto mapPath = [&](StringRef path) { + if (!needPathRemapping) + return path.str(); + + return cache.getScanService().remapPath(path); + }; + if (needPathRemapping) + commandline.push_back("-resolved-plugin-verification"); + for (auto ¯o : depInfo.getMacroDependencies()) { - std::string arg = macro.second.LibraryPath + "#" + - macro.second.ExecutablePath + "#" + macro.first; + std::string arg = mapPath(macro.second.LibraryPath) + "#" + + mapPath(macro.second.ExecutablePath) + "#" + + macro.first; commandline.push_back("-load-resolved-plugin"); commandline.push_back(arg); } @@ -480,9 +494,10 @@ private: llvm::for_each( sourceDep->auxiliaryFiles, [this](const std::string &file) { tracker->trackFile(file); }); - llvm::for_each(sourceDep->macroDependencies, [this](const auto &entry) { - tracker->trackFile(entry.second.LibraryPath); - }); + llvm::for_each(dependencyInfoCopy.getMacroDependencies(), + [this](const auto &entry) { + tracker->trackFile(entry.second.LibraryPath); + }); auto root = tracker->createTreeFromDependencies(); if (!root) return root.takeError(); @@ -496,7 +511,7 @@ private: llvm::for_each( textualDep->auxiliaryFiles, [this](const std::string &file) { tracker->trackFile(file); }); - llvm::for_each(textualDep->macroDependencies, + llvm::for_each(dependencyInfoCopy.getMacroDependencies(), [this](const auto &entry) { tracker->trackFile(entry.second.LibraryPath); }); diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 4c8fb514d92..739e26953bb 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -379,10 +379,6 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, arguments.push_back(inputArgs.MakeArgString(globalRemapping)); } - if (inputArgs.hasArg(options::OPT_executor_factory)) { - inputArgs.AddLastArg(arguments, options::OPT_executor_factory); - } - // Pass through the values passed to -Xfrontend. inputArgs.AddAllArgValues(arguments, options::OPT_Xfrontend); diff --git a/lib/DriverTool/sil_opt_main.cpp b/lib/DriverTool/sil_opt_main.cpp index d3e4b17daa2..1ad27b4f144 100644 --- a/lib/DriverTool/sil_opt_main.cpp +++ b/lib/DriverTool/sil_opt_main.cpp @@ -597,6 +597,16 @@ struct SILOptOptions { "enable-address-dependencies", llvm::cl::desc("Enable enforcement of lifetime dependencies on addressable values.")); + llvm::cl::opt EnableCalleeAllocatedCoroAbi = llvm::cl::opt( + "enable-callee-allocated-coro-abi", + llvm::cl::desc("Override per-platform settings and use yield_once_2."), + llvm::cl::init(false)); + llvm::cl::opt DisableCalleeAllocatedCoroAbi = llvm::cl::opt( + "disable-callee-allocated-coro-abi", + llvm::cl::desc( + "Override per-platform settings and don't use yield_once_2."), + llvm::cl::init(false)); + llvm::cl::opt MergeableTraps = llvm::cl::opt( "mergeable-traps", llvm::cl::desc("Enable cond_fail merging.")); @@ -918,6 +928,10 @@ int sil_opt_main(ArrayRef argv, void *MainAddr) { options.EnablePackMetadataStackPromotion; SILOpts.EnableAddressDependencies = options.EnableAddressDependencies; + if (options.EnableCalleeAllocatedCoroAbi) + SILOpts.CoroutineAccessorsUseYieldOnce2 = true; + if (options.DisableCalleeAllocatedCoroAbi) + SILOpts.CoroutineAccessorsUseYieldOnce2 = false; SILOpts.MergeableTraps = options.MergeableTraps; if (options.OptModeFlag == OptimizationMode::NotSet) { diff --git a/lib/DriverTool/swift_symbolgraph_extract_main.cpp b/lib/DriverTool/swift_symbolgraph_extract_main.cpp index 1dedd9bb722..8648672cae9 100644 --- a/lib/DriverTool/swift_symbolgraph_extract_main.cpp +++ b/lib/DriverTool/swift_symbolgraph_extract_main.cpp @@ -203,6 +203,25 @@ int swift_symbolgraph_extract_main(ArrayRef Args, .Default(AccessLevel::Public); } + if (auto *A = ParsedArgs.getLastArg(OPT_allow_availability_platforms)) { + llvm::SmallVector AvailabilityPlatforms; + StringRef(A->getValue()) + .split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1, + /*KeepEmpty*/ false); + Options.AvailabilityPlatforms = llvm::DenseSet( + AvailabilityPlatforms.begin(), AvailabilityPlatforms.end()); + Options.AvailabilityIsBlockList = false; + } else if (auto *A = + ParsedArgs.getLastArg(OPT_block_availability_platforms)) { + llvm::SmallVector AvailabilityPlatforms; + StringRef(A->getValue()) + .split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1, + /*KeepEmpty*/ false); + Options.AvailabilityPlatforms = llvm::DenseSet( + AvailabilityPlatforms.begin(), AvailabilityPlatforms.end()); + Options.AvailabilityIsBlockList = true; + } + Invocation.getLangOptions().setCxxInteropFromArgs(ParsedArgs, Diags); std::string InstanceSetupError; diff --git a/lib/Frontend/CachedDiagnostics.cpp b/lib/Frontend/CachedDiagnostics.cpp index b1b9c48780d..3c2975c5add 100644 --- a/lib/Frontend/CachedDiagnostics.cpp +++ b/lib/Frontend/CachedDiagnostics.cpp @@ -18,7 +18,9 @@ #include "swift/AST/DiagnosticBridge.h" #include "swift/AST/DiagnosticConsumer.h" +#include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/DiagnosticsSema.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/SourceManager.h" #include "swift/Frontend/Frontend.h" @@ -755,6 +757,13 @@ public: auto &Serializer = getSerializer(); assert(SM.getFileSystem() == Serializer.getSourceMgr().getFileSystem() && "Caching for a different file system"); + + // Bypass the caching. + if (BypassDiagIDs.count(Info.ID)) { + for (auto *Diag : OrigConsumers) + Diag->handleDiagnostic(Serializer.getSourceMgr(), Info); + return; + } Serializer.handleDiagnostic(SM, Info, [&](const DiagnosticInfo &Info) { for (auto *Diag : OrigConsumers) Diag->handleDiagnostic(Serializer.getSourceMgr(), Info); @@ -809,6 +818,8 @@ private: // Processor/Serializer alive until then. std::unique_ptr Serializer; + const llvm::SmallDenseSet BypassDiagIDs = {diag::macro_loaded.ID}; + SourceManager &InstanceSourceMgr; const FrontendInputsAndOutputs &InAndOut; DiagnosticEngine &Diags; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 32ff6a863f1..fbfe951a519 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1364,12 +1364,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.enableFeature(Feature::RegionBasedIsolation); } - // Get the executor factory name - if (const Arg *A = Args.getLastArg(OPT_executor_factory)) { - printf("Got executor-factory option\n"); - Opts.ExecutorFactory = A->getValue(); - } - Opts.WarnImplicitOverrides = Args.hasArg(OPT_warn_implicit_overrides); @@ -2221,6 +2215,25 @@ static void ParseSymbolGraphArgs(symbolgraphgen::SymbolGraphOptions &Opts, Opts.MinimumAccessLevel = AccessLevel::Public; } + if (auto *A = Args.getLastArg(OPT_symbol_graph_allow_availability_platforms)) { + llvm::SmallVector AvailabilityPlatforms; + StringRef(A->getValue()) + .split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1, + /*KeepEmpty*/ false); + Opts.AvailabilityPlatforms = llvm::DenseSet( + AvailabilityPlatforms.begin(), AvailabilityPlatforms.end()); + Opts.AvailabilityIsBlockList = false; + } else if (auto *A = Args.getLastArg( + OPT_symbol_graph_block_availability_platforms)) { + llvm::SmallVector AvailabilityPlatforms; + StringRef(A->getValue()) + .split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1, + /*KeepEmpty*/ false); + Opts.AvailabilityPlatforms = llvm::DenseSet( + AvailabilityPlatforms.begin(), AvailabilityPlatforms.end()); + Opts.AvailabilityIsBlockList = true; + } + // default values for generating symbol graphs during a build Opts.PrettyPrint = false; Opts.EmitSynthesizedMembers = true; @@ -2417,6 +2430,9 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, ArgList &Args, Opts.ScannerPrefixMapper.push_back(Opt.str()); } + Opts.ResolvedPluginVerification |= + Args.hasArg(OPT_resolved_plugin_verification); + // rdar://132340493 disable scanner-side validation for non-caching builds Opts.ScannerModuleValidation |= Args.hasFlag(OPT_scanner_module_validation, OPT_no_scanner_module_validation, @@ -3130,6 +3146,16 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, Opts.EnableAddressDependencies = Args.hasFlag( OPT_enable_address_dependencies, OPT_disable_address_dependencies, Opts.EnableAddressDependencies); + + if (LangOpts.Target.isOSDarwin() || LangOpts.Target.isOSLinux()) { + // On Darwin and Linux, use yield_once_2 by default. + Opts.CoroutineAccessorsUseYieldOnce2 = true; + } + Opts.CoroutineAccessorsUseYieldOnce2 = + Args.hasFlag(OPT_enable_callee_allocated_coro_abi, + OPT_disable_callee_allocated_coro_abi, + Opts.CoroutineAccessorsUseYieldOnce2); + Opts.MergeableTraps = Args.hasArg(OPT_mergeable_traps); return false; diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index c33c3d98127..3d02ccca658 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -878,6 +878,7 @@ bool CompilerInstance::setUpPluginLoader() { /// FIXME: If Invocation has 'PluginRegistry', we can set it. But should we? auto loader = std::make_unique( *Context, getDependencyTracker(), + Invocation.getFrontendOptions().CacheReplayPrefixMap, Invocation.getFrontendOptions().DisableSandbox); Context->setPluginLoader(std::move(loader)); return false; diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 33d8d7ed084..98a9addcf21 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -1932,6 +1932,8 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl( // Load plugin libraries for macro expression as default arguments genericSubInvocation.getSearchPathOptions().PluginSearchOpts = searchPathOpts.PluginSearchOpts; + genericSubInvocation.getSearchPathOptions().ResolvedPluginVerification = + searchPathOpts.ResolvedPluginVerification; // Get module loading behavior options. genericSubInvocation.getSearchPathOptions().ScannerModuleValidation = searchPathOpts.ScannerModuleValidation; diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index b2ba2355dab..ed34980b88a 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -305,13 +305,14 @@ static void addDereferenceableAttributeToBuilder(IRGenModule &IGM, static void addIndirectValueParameterAttributes(IRGenModule &IGM, llvm::AttributeList &attrs, const TypeInfo &ti, - unsigned argIndex) { + unsigned argIndex, + bool addressable) { llvm::AttrBuilder b(IGM.getLLVMContext()); // Value parameter pointers can't alias or be captured. b.addAttribute(llvm::Attribute::NoAlias); // Bitwise takable value types are guaranteed not to capture // a pointer into itself. - if (ti.isBitwiseTakable(ResilienceExpansion::Maximal)) + if (!addressable && ti.isBitwiseTakable(ResilienceExpansion::Maximal)) b.addAttribute(llvm::Attribute::NoCapture); // The parameter must reference dereferenceable memory of the type. addDereferenceableAttributeToBuilder(IGM, b, ti); @@ -340,7 +341,7 @@ static void addPackParameterAttributes(IRGenModule &IGM, static void addInoutParameterAttributes(IRGenModule &IGM, SILType paramSILType, llvm::AttributeList &attrs, const TypeInfo &ti, unsigned argIndex, - bool aliasable) { + bool aliasable, bool addressable) { llvm::AttrBuilder b(IGM.getLLVMContext()); // Thanks to exclusivity checking, it is not possible to alias inouts except // those that are inout_aliasable. @@ -351,7 +352,7 @@ static void addInoutParameterAttributes(IRGenModule &IGM, SILType paramSILType, } // Bitwise takable value types are guaranteed not to capture // a pointer into itself. - if (ti.isBitwiseTakable(ResilienceExpansion::Maximal)) + if (!addressable && ti.isBitwiseTakable(ResilienceExpansion::Maximal)) b.addAttribute(llvm::Attribute::NoCapture); // The inout must reference dereferenceable memory of the type. addDereferenceableAttributeToBuilder(IGM, b, ti); @@ -600,9 +601,11 @@ namespace { Signature getSignature(); private: - const TypeInfo &expand(SILParameterInfo param); + const TypeInfo &expand(unsigned paramIdx); llvm::Type *addIndirectResult(SILType resultType, bool useInReg = false); + bool isAddressableParam(unsigned paramIdx); + SILFunctionConventions getSILFuncConventions() const { return SILFunctionConventions(FnType, IGM.getSILModule()); } @@ -1781,7 +1784,8 @@ static ArrayRef expandScalarOrStructTypeToArray(llvm::Type *&ty) { return expandedTys; } -const TypeInfo &SignatureExpansion::expand(SILParameterInfo param) { +const TypeInfo &SignatureExpansion::expand(unsigned paramIdx) { + auto param = FnType->getParameters()[paramIdx]; auto paramSILType = getSILFuncConventions().getSILType( param, IGM.getMaximalTypeExpansionContext()); auto &ti = IGM.getTypeInfo(paramSILType); @@ -1789,7 +1793,8 @@ const TypeInfo &SignatureExpansion::expand(SILParameterInfo param) { case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Indirect_In_CXX: - addIndirectValueParameterAttributes(IGM, Attrs, ti, ParamIRTypes.size()); + addIndirectValueParameterAttributes(IGM, Attrs, ti, ParamIRTypes.size(), + isAddressableParam(paramIdx)); addPointerParameter(IGM.getStorageType(getSILFuncConventions().getSILType( param, IGM.getMaximalTypeExpansionContext()))); return ti; @@ -1797,8 +1802,9 @@ const TypeInfo &SignatureExpansion::expand(SILParameterInfo param) { case ParameterConvention::Indirect_Inout: case ParameterConvention::Indirect_InoutAliasable: addInoutParameterAttributes( - IGM, paramSILType, Attrs, ti, ParamIRTypes.size(), - conv == ParameterConvention::Indirect_InoutAliasable); + IGM, paramSILType, Attrs, ti, ParamIRTypes.size(), + conv == ParameterConvention::Indirect_InoutAliasable, + isAddressableParam(paramIdx)); addPointerParameter(IGM.getStorageType(getSILFuncConventions().getSILType( param, IGM.getMaximalTypeExpansionContext()))); return ti; @@ -1822,7 +1828,8 @@ const TypeInfo &SignatureExpansion::expand(SILParameterInfo param) { auto &nativeSchema = ti.nativeParameterValueSchema(IGM); if (nativeSchema.requiresIndirect()) { addIndirectValueParameterAttributes(IGM, Attrs, ti, - ParamIRTypes.size()); + ParamIRTypes.size(), + /*addressable*/ false); ParamIRTypes.push_back(ti.getStorageType()->getPointerTo()); return ti; } @@ -1842,6 +1849,13 @@ const TypeInfo &SignatureExpansion::expand(SILParameterInfo param) { llvm_unreachable("bad parameter convention"); } +bool SignatureExpansion::isAddressableParam(unsigned paramIdx) { + return FnType->isAddressable(paramIdx, IGM.IRGen.SIL, + IGM.getGenericEnvironment(), + IGM.getSILTypes(), + IGM.getMaximalTypeExpansionContext()); +} + /// Does the given function type have a self parameter that should be /// given the special treatment for self parameters? /// @@ -1887,7 +1901,6 @@ static void addParamInfo(SignatureExpansionABIDetails *details, } void SignatureExpansion::expandKeyPathAccessorParameters() { - auto params = FnType->getParameters(); unsigned numArgsToExpand; SmallVector tailParams; @@ -1933,7 +1946,7 @@ void SignatureExpansion::expandKeyPathAccessorParameters() { llvm_unreachable("non keypath accessor convention"); } for (unsigned i = 0; i < numArgsToExpand; i++) { - expand(params[i]); + expand(i); } for (auto tailParam : tailParams) { ParamIRTypes.push_back(tailParam); @@ -1987,9 +2000,9 @@ void SignatureExpansion::expandParameters( params = params.drop_back(); } - for (auto param : params) { - const TypeInfo &ti = expand(param); - addParamInfo(recordedABIDetails, ti, param.getConvention()); + for (auto pair : enumerate(params)) { + const TypeInfo &ti = expand(pair.index()); + addParamInfo(recordedABIDetails, ti, pair.value().getConvention()); } if (recordedABIDetails && FnType->hasSelfParam() && !hasSelfContext) recordedABIDetails->parameters.back().isSelf = true; @@ -2015,7 +2028,7 @@ void SignatureExpansion::expandParameters( if (claimSelf()) IGM.addSwiftSelfAttributes(Attrs, curLength); - expand(FnType->getSelfParameter()); + expand(FnType->getSelfParameterIndex()); if (recordedABIDetails) recordedABIDetails->hasTrailingSelfParam = true; assert(ParamIRTypes.size() == curLength + 1 && @@ -2260,8 +2273,8 @@ void SignatureExpansion::expandAsyncEntryType() { params = params.drop_back(); } - for (auto param : params) { - expand(param); + for (unsigned i : range(params.size())) { + expand(i); } // Next, the generic signature. @@ -2279,7 +2292,7 @@ void SignatureExpansion::expandAsyncEntryType() { if (hasSelfContext) { auto curLength = ParamIRTypes.size(); (void)curLength; - expand(FnType->getSelfParameter()); + expand(FnType->getSelfParameterIndex()); assert(ParamIRTypes.size() == curLength + 1 && "adding 'self' added unexpected number of parameters"); if (claimSelf()) diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 2247e96de46..695beeca367 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -3390,16 +3390,15 @@ MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF, assert(entry.isOutOfLineBase()); auto inheritedProtocol = entry.getBase(); - sourceKey.Kind = - LocalTypeDataKind::forAbstractProtocolWitnessTable(inheritedProtocol); if (sourceKey.Kind.isConcreteProtocolConformance()) { auto inheritedConformance = sourceKey.Kind.getConcreteProtocolConformance() ->getInheritedConformance(inheritedProtocol); - if (inheritedConformance) { - sourceKey.Kind = LocalTypeDataKind::forConcreteProtocolWitnessTable( - inheritedConformance); - } + sourceKey.Kind = LocalTypeDataKind::forConcreteProtocolWitnessTable( + inheritedConformance); + } else { + sourceKey.Kind = + LocalTypeDataKind::forAbstractProtocolWitnessTable(inheritedProtocol); } if (!source) return MetadataResponse(); diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 1ce436971e0..7dbfeca1942 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -2577,8 +2577,13 @@ private: // Scope outermost fileprivate decls in an inline private discriminator // namespace. + // + // We need to don't do this for decls imported from Clang modules because + // the scopes of C/C++ symbols are not restricted to a particular file unit. if (auto *Decl = DbgTy.getDecl()) - if (Decl->isOutermostPrivateOrFilePrivateScope()) + if (Decl->isOutermostPrivateOrFilePrivateScope() && + !isa( + Decl->getDeclContext()->getModuleScopeContext())) Scope = getFilePrivateScope(Scope, Decl); return Scope; diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 3aabe05c04b..fb526f734e7 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1205,8 +1205,9 @@ public: // SIL instruction lowering //===--------------------------------------------------------------------===// - void visitSILBasicBlock(SILBasicBlock *BB); + bool shouldUseDispatchThunk(SILDeclRef method); + void visitSILBasicBlock(SILBasicBlock *BB); void emitErrorResultVar(CanSILFunctionType FnTy, SILResultInfo ErrorInfo, DebugValueInst *DbgValue); @@ -8414,28 +8415,17 @@ void IRGenSILFunction::visitObjCSuperMethodInst(swift::ObjCSuperMethodInst *i) { /*startAtSuper=*/true); } -void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) { - assert(!i->getMember().isForeign); - - Explosion base = getLoweredExplosion(i->getOperand()); - llvm::Value *baseValue = base.claimNext(); - - SILDeclRef method = i->getMember().getOverriddenVTableEntry(); - PrettyStackTraceSILDeclRef entry("lowering class method call to", method); - - auto methodType = i->getType().castTo(); - +bool IRGenSILFunction::shouldUseDispatchThunk(SILDeclRef method) { AccessLevel methodAccess = method.getDecl()->getEffectiveAccess(); auto *classDecl = cast(method.getDecl()->getDeclContext()); bool shouldUseDispatchThunk = false; // Because typechecking for the debugger has more lax rules, check the access // level of the getter to decide whether to use a dispatch thunk for the // debugger. - bool shouldUseDispatchThunkIfInDebugger = - !classDecl->getASTContext().LangOpts.DebuggerSupport || - methodAccess == AccessLevel::Public; + bool inDebugger = classDecl->getASTContext().LangOpts.DebuggerSupport; + bool shouldUseDispatchThunkIfInDebugger = methodAccess >= AccessLevel::Public; if (IGM.hasResilientMetadata(classDecl, ResilienceExpansion::Maximal) && - shouldUseDispatchThunkIfInDebugger) { + (!inDebugger || shouldUseDispatchThunkIfInDebugger)) { shouldUseDispatchThunk = true; } else if (IGM.getOptions().VirtualFunctionElimination) { // For VFE, use a thunk if the target class is in another module. This @@ -8452,9 +8442,22 @@ void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) { shouldUseDispatchThunk = classDecl->getModuleContext() != IGM.getSwiftModule(); } + return shouldUseDispatchThunk; +} - if (shouldUseDispatchThunk) { - llvm::Constant *fnPtr = IGM.getAddrOfDispatchThunk(method, NotForDefinition); +void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) { + assert(!i->getMember().isForeign); + + Explosion base = getLoweredExplosion(i->getOperand()); + llvm::Value *baseValue = base.claimNext(); + + SILDeclRef method = i->getMember().getOverriddenVTableEntry(); + PrettyStackTraceSILDeclRef entry("lowering class method call to", method); + + auto methodType = i->getType().castTo(); + if (shouldUseDispatchThunk(method)) { + llvm::Constant *fnPtr = + IGM.getAddrOfDispatchThunk(method, NotForDefinition); if (methodType->isAsync()) { auto *fnPtrType = fnPtr->getType(); diff --git a/lib/IRGen/LocalTypeDataKind.h b/lib/IRGen/LocalTypeDataKind.h index a816daa62c4..47cdf2b89d1 100644 --- a/lib/IRGen/LocalTypeDataKind.h +++ b/lib/IRGen/LocalTypeDataKind.h @@ -117,19 +117,19 @@ public: /// same function. static LocalTypeDataKind forAbstractProtocolWitnessTable(ProtocolDecl *protocol) { - assert(protocol && "protocol reference may not be null"); + ASSERT(protocol && "protocol reference may not be null"); return LocalTypeDataKind(uintptr_t(protocol) | Kind_Decl); } /// A reference to a protocol witness table for a concrete type. static LocalTypeDataKind forConcreteProtocolWitnessTable(ProtocolConformance *conformance) { - assert(conformance && "conformance reference may not be null"); + ASSERT(conformance && "conformance reference may not be null"); return LocalTypeDataKind(uintptr_t(conformance) | Kind_Conformance); } static LocalTypeDataKind forProtocolWitnessTablePack(PackConformance *pack) { - assert(pack && "pack conformance reference may not be null"); + ASSERT(pack && "pack conformance reference may not be null"); return LocalTypeDataKind(uintptr_t(pack) | Kind_PackConformance); } diff --git a/lib/Migrator/APIDiffMigratorPass.cpp b/lib/Migrator/APIDiffMigratorPass.cpp index 042a6e13bc1..627f5fb5c13 100644 --- a/lib/Migrator/APIDiffMigratorPass.cpp +++ b/lib/Migrator/APIDiffMigratorPass.cpp @@ -162,6 +162,10 @@ public: return visit(T->getBase()); } + FoundResult visitCallerIsolatedTypeRepr(CallerIsolatedTypeRepr *T) { + return visit(T->getBase()); + } + FoundResult visitArrayTypeRepr(ArrayTypeRepr *T) { return handleParent(T, T->getBase()); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 19bf74e76b5..4b117df7f0e 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3746,19 +3746,20 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } case DeclAttrKind::Nonisolated: { AttrRange = Loc; - std::optional isUnsafe(false); + std::optional Modifier(NonIsolatedModifier::None); if (EnableParameterizedNonisolated) { - isUnsafe = - parseSingleAttrOption(*this, Loc, AttrRange, AttrName, DK, - {{Context.Id_unsafe, true}}, *isUnsafe, - ParameterizedDeclAttributeKind::Nonisolated); - if (!isUnsafe) { + Modifier = parseSingleAttrOption( + *this, Loc, AttrRange, AttrName, DK, + {{Context.Id_unsafe, NonIsolatedModifier::Unsafe}, + {Context.Id_nonsending, NonIsolatedModifier::NonSending}}, + *Modifier, ParameterizedDeclAttributeKind::Nonisolated); + if (!Modifier) { return makeParserSuccess(); } } if (!DiscardAttribute) { - Attributes.add(new (Context) NonisolatedAttr(AtLoc, AttrRange, *isUnsafe, + Attributes.add(new (Context) NonisolatedAttr(AtLoc, AttrRange, *Modifier, /*implicit*/ false)); } break; @@ -3948,21 +3949,6 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, Attributes.add(Attr.get()); break; } - - case DeclAttrKind::Execution: { - auto behavior = parseSingleAttrOption( - *this, Loc, AttrRange, AttrName, DK, - {{Context.Id_concurrent, ExecutionKind::Concurrent}, - {Context.Id_caller, ExecutionKind::Caller}}); - if (!behavior) - return makeParserSuccess(); - - if (!DiscardAttribute) - Attributes.add(new (Context) ExecutionAttr(AtLoc, AttrRange, *behavior, - /*Implicit*/ false)); - - break; - } } if (DuplicateAttribute) { @@ -4248,10 +4234,6 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, checkInvalidAttrName("_functionBuilder", "resultBuilder", DeclAttrKind::ResultBuilder, diag::attr_renamed_warning); - // Historical name for @Sendable. - checkInvalidAttrName("concurrent", "Sendable", DeclAttrKind::Sendable, - diag::attr_renamed_warning); - // Historical name for 'nonisolated'. if (!DK && Tok.getText() == "actorIndependent") { diagnose( @@ -4605,23 +4587,6 @@ ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result, // Determine which attribute it is, and diagnose it if unknown. auto optAttr = TypeAttribute::getAttrKindFromString(Tok.getText()); - auto checkInvalidAttrName = - [&](StringRef invalidName, StringRef correctName, TypeAttrKind kind, - std::optional> diag = std::nullopt) { - if (!optAttr && Tok.getText() == invalidName) { - optAttr = kind; - - if (diag) { - diagnose(Tok, *diag, invalidName, correctName) - .fixItReplace(Tok.getLoc(), correctName); - } - } - }; - - // Historical name for @Sendable. - checkInvalidAttrName("concurrent", "Sendable", TypeAttrKind::Sendable, - diag::attr_renamed_warning); - if (!optAttr) { auto declAttrID = DeclAttribute::getAttrKindFromString(Tok.getText()); if (declAttrID) { @@ -4771,56 +4736,6 @@ ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result, return makeParserSuccess(); } - case TypeAttrKind::Execution: { - if (!Context.LangOpts.hasFeature(Feature::ExecutionAttribute)) { - diagnose(Tok, diag::requires_experimental_feature, "@execution", false, - Feature::ExecutionAttribute.getName()); - return makeParserError(); - } - - SourceLoc lpLoc = Tok.getLoc(), behaviorLoc, rpLoc; - if (!consumeIfNotAtStartOfLine(tok::l_paren)) { - if (!justChecking) { - diagnose(Tok, diag::attr_execution_expected_lparen); - // TODO: should we suggest removing the `@`? - } - return makeParserError(); - } - - bool invalid = false; - std::optional behavior; - if (isIdentifier(Tok, "concurrent")) { - behaviorLoc = consumeToken(tok::identifier); - behavior = ExecutionKind::Concurrent; - } else if (isIdentifier(Tok, "caller")) { - behaviorLoc = consumeToken(tok::identifier); - behavior = ExecutionKind::Caller; - } else { - if (!justChecking) { - diagnose(Tok, diag::attr_execution_expected_kind); - } - invalid = true; - consumeIf(tok::identifier); - } - - if (justChecking && !Tok.is(tok::r_paren)) - return makeParserError(); - if (parseMatchingToken(tok::r_paren, rpLoc, - diag::attr_execution_expected_rparen, - lpLoc)) - return makeParserError(); - - if (invalid) - return makeParserError(); - assert(behavior); - - if (!justChecking) { - result = new (Context) ExecutionTypeAttr(AtLoc, attrLoc, {lpLoc, rpLoc}, - {*behavior, behaviorLoc}); - } - return makeParserSuccess(); - } - case TypeAttrKind::Opened: { // Parse the opened existential ID string in parens SourceLoc beginLoc = Tok.getLoc(), idLoc, endLoc; @@ -5323,6 +5238,7 @@ ParserStatus Parser::parseDeclModifierList(DeclAttributes &Attributes, /// '__shared' attribute-list-clause attribute-list /// '__owned' attribute-list-clause attribute-list /// 'some' attribute-list-clause attribute-list +/// 'nonisolated(nonsending)' attribute-list-clause attribute-list /// attribute-list-clause: /// '@' attribute /// '@' attribute attribute-list-clause @@ -5349,6 +5265,43 @@ ParserStatus Parser::ParsedTypeAttributeList::slowParse(Parser &P) { continue; } + // nonisolated(nonsending) + if (Tok.isContextualKeyword("nonisolated")) { + Tok.setKind(tok::contextual_keyword); + + auto kwLoc = P.consumeToken(); + + if (CallerIsolatedLoc.isValid()) { + P.diagnose(kwLoc, diag::nonisolated_nonsending_repeated) + .fixItRemove(SpecifierLoc); + } + + // '(' + if (!P.consumeIfAttributeLParen()) { + P.diagnose(Tok, diag::nonisolated_nonsending_expected_lparen); + status.setIsParseError(); + continue; + } + + if (!Tok.isContextualKeyword("nonsending")) { + P.diagnose(Tok, diag::nonisolated_nonsending_incorrect_modifier); + status.setIsParseError(); + continue; + } + + (void)P.consumeToken(); + + // ')' + if (!P.consumeIf(tok::r_paren)) { + P.diagnose(Tok, diag::nonisolated_nonsending_expected_rparen); + status.setIsParseError(); + continue; + } + + CallerIsolatedLoc = kwLoc; + continue; + } + // Perform an extra check for 'sending'. Since it is a specifier, we use // the actual parsing logic below. if (Tok.isContextualKeyword("sending")) { @@ -5687,9 +5640,10 @@ static bool consumeIfParenthesizedUnowned(Parser &P) { } /// Given a current token of 'nonisolated', check to see if it is followed by an -/// "(unsafe)" specifier and consumes if it is. +/// "(unsafe)" or "(nonsending)" specifier and consumes if it is. static bool consumeIfParenthesizedNonisolated(Parser &P) { - return consumeIfParenthesizedModifier(P, "nonisolated", {"unsafe"}); + return consumeIfParenthesizedModifier(P, "nonisolated", + {"unsafe", "nonsending"}); } static void skipAttribute(Parser &P) { diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 9fa9e8100d8..75b068bacb3 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -542,8 +542,9 @@ ParserResult Parser::parseExprSequenceElement(Diag<> message, consumeToken(); } - // Try to parse '@' sign or 'inout' as a attributed typerepr. - if (Tok.isAny(tok::at_sign, tok::kw_inout)) { + // Try to parse '@' sign, 'inout' or 'nonisolated' as a attributed typerepr. + if (Tok.isAny(tok::at_sign, tok::kw_inout) || + Tok.isContextualKeyword("nonisolated")) { bool isType = false; { BacktrackingScope backtrack(*this); diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 414b6056bac..18cfad4d45e 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -59,6 +59,10 @@ Parser::ParsedTypeAttributeList::applyAttributesToType(Parser &p, ty = new (p.Context) SendingTypeRepr(ty, SendingLoc); } + if (CallerIsolatedLoc.isValid()) { + ty = new (p.Context) CallerIsolatedTypeRepr(ty, CallerIsolatedLoc); + } + if (lifetimeEntry) { ty = LifetimeDependentTypeRepr::create(p.Context, ty, lifetimeEntry); } @@ -163,9 +167,7 @@ ParserResult Parser::parseTypeSimple( Diag<> MessageID, ParseTypeReason reason) { ParserResult ty; - if (isParameterSpecifier() && - !(!Context.LangOpts.hasFeature(Feature::IsolatedConformances) && - Tok.isContextualKeyword("isolated"))) { + if (isParameterSpecifier()) { // Type specifier should already be parsed before here. This only happens // for construct like 'P1 & inout P2'. diagnose(Tok.getLoc(), diag::attr_only_on_parameters, Tok.getRawText()); @@ -1730,6 +1732,36 @@ bool Parser::canParseTypeSimpleOrComposition() { return true; } +bool Parser::canParseNonisolatedAsTypeModifier() { + assert(Tok.isContextualKeyword("nonisolated")); + + BacktrackingScope scope(*this); + + // Consume 'nonisolated' + consumeToken(); + + // Something like: + // + // nonisolated + // (42) + if (Tok.isAtStartOfLine()) + return false; + + // Always requires `(nonsending)`, together + // we don't want eagerly interpret something + // like `nonisolated(0)` as a modifier. + + if (!consumeIf(tok::l_paren)) + return false; + + if (!Tok.isContextualKeyword("nonsending")) + return false; + + consumeToken(); + + return consumeIf(tok::r_paren); +} + bool Parser::canParseTypeScalar() { // Accept 'inout' at for better recovery. consumeIf(tok::kw_inout); @@ -1737,6 +1769,16 @@ bool Parser::canParseTypeScalar() { if (Tok.isContextualKeyword("sending")) consumeToken(); + if (Tok.isContextualKeyword("nonisolated")) { + if (!canParseNonisolatedAsTypeModifier()) + return false; + + // consume 'nonisolated' + consumeToken(); + // skip '(nonsending)' + skipSingle(); + } + if (!canParseTypeSimpleOrComposition()) return false; diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index c8c714fef21..134ecc7d77e 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -1855,5 +1855,8 @@ bool SILDeclRef::isCalleeAllocatedCoroutine() const { if (!accessor) return false; - return requiresFeatureCoroutineAccessors(accessor->getAccessorKind()); + if (!requiresFeatureCoroutineAccessors(accessor->getAccessorKind())) + return false; + + return getASTContext().SILOpts.CoroutineAccessorsUseYieldOnce2; } diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index b54605cb24c..a52a1608532 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2481,8 +2481,10 @@ static CanSILFunctionType getSILFunctionType( if (auto accessor = getAsCoroutineAccessor(constant)) { auto origAccessor = cast(origConstant->getDecl()); + auto &ctx = origAccessor->getASTContext(); coroutineKind = - requiresFeatureCoroutineAccessors(accessor->getAccessorKind()) + (requiresFeatureCoroutineAccessors(accessor->getAccessorKind()) && + ctx.SILOpts.CoroutineAccessorsUseYieldOnce2) ? SILCoroutineKind::YieldOnce2 : SILCoroutineKind::YieldOnce; @@ -2620,19 +2622,23 @@ static CanSILFunctionType getSILFunctionType( { std::optional actorIsolation; if (constant) { + // TODO: It should to be possible to `getActorIsolation` if + // reference is to a decl instead of trying to get isolation + // from the reference kind, the attributes, or the context. + if (constant->kind == SILDeclRef::Kind::Deallocator) { actorIsolation = ActorIsolation::forNonisolated(false); - } else if (auto *decl = constant->getAbstractFunctionDecl(); - decl && decl->getExecutionBehavior().has_value()) { - switch (*decl->getExecutionBehavior()) { - case ExecutionKind::Concurrent: + } else if (auto *decl = constant->getAbstractFunctionDecl()) { + if (auto *nonisolatedAttr = + decl->getAttrs().getAttribute()) { + if (nonisolatedAttr->isNonSending()) + actorIsolation = ActorIsolation::forCallerIsolationInheriting(); + } else if (decl->getAttrs().hasAttribute()) { actorIsolation = ActorIsolation::forNonisolated(false /*unsafe*/); - break; - case ExecutionKind::Caller: - actorIsolation = ActorIsolation::forCallerIsolationInheriting(); - break; } - } else { + } + + if (!actorIsolation) { actorIsolation = getActorIsolationOfContext(constant->getInnermostDeclContext()); } diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index 52c21c45797..f919f98f2d0 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -707,6 +707,17 @@ bool SILFunctionType::isNoReturnFunction(SILModule &M, } bool SILFunctionType::isAddressable(unsigned paramIdx, SILFunction *caller) { + return isAddressable(paramIdx, caller->getModule(), + caller->getGenericEnvironment(), + caller->getModule().Types, + caller->getTypeExpansionContext()); +} + +// 'genericEnv' may be null. +bool SILFunctionType::isAddressable(unsigned paramIdx, SILModule &module, + GenericEnvironment *genericEnv, + Lowering::TypeConverter &typeConverter, + TypeExpansionContext expansion) { SILParameterInfo paramInfo = getParameters()[paramIdx]; for (auto &depInfo : getLifetimeDependencies()) { auto *addressableIndices = depInfo.getAddressableIndices(); @@ -715,13 +726,12 @@ bool SILFunctionType::isAddressable(unsigned paramIdx, SILFunction *caller) { } auto *condAddressableIndices = depInfo.getConditionallyAddressableIndices(); if (condAddressableIndices && condAddressableIndices->contains(paramIdx)) { - CanType argType = paramInfo.getArgumentType( - caller->getModule(), this, caller->getTypeExpansionContext()); - CanType contextType = - argType->hasTypeParameter() - ? caller->mapTypeIntoContext(argType)->getCanonicalType() + CanType argType = paramInfo.getArgumentType(module, this, expansion); + CanType contextType = genericEnv + ? genericEnv->mapTypeIntoContext(argType)->getCanonicalType() : argType; - auto &tl = caller->getTypeLowering(contextType); + assert(!contextType->hasTypeParameter()); + auto &tl = typeConverter.getTypeLowering(contextType, expansion); if (tl.getRecursiveProperties().isAddressableForDependencies()) return true; } diff --git a/lib/SIL/Utils/OSSALifetimeCompletion.cpp b/lib/SIL/Utils/OSSALifetimeCompletion.cpp index 358eb4388bd..d72e1d8422c 100644 --- a/lib/SIL/Utils/OSSALifetimeCompletion.cpp +++ b/lib/SIL/Utils/OSSALifetimeCompletion.cpp @@ -501,8 +501,9 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime( }; Walker walker(*this, scopedAddress, boundary, liveness); AddressUseKind result = walker.walk(scopedAddress.value); - if (VerifyLifetimeCompletion && boundary != Boundary::Availability - && result != AddressUseKind::NonEscaping) { + if ((VerifyLifetimeCompletion || ForceLivenessVerification) && + boundary != Boundary::Availability && + result != AddressUseKind::NonEscaping) { llvm::errs() << "Incomplete liveness for:\n" << scopedAddress.value; if (auto *escapingUse = walker.getEscapingUse()) { llvm::errs() << " escapes at:\n"; diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index cc4d3a1c508..7f1a1f98bfc 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -514,25 +514,14 @@ FuncDecl *SILGenModule::getExit() { Type SILGenModule::getConfiguredExecutorFactory() { auto &ctx = getASTContext(); - ModuleDecl *module; + // Look in the main module for a typealias + Type factory = ctx.getNamedSwiftType(ctx.MainModule, "DefaultExecutorFactory"); - // Parse the executor factory name - StringRef qualifiedName = *ctx.LangOpts.ExecutorFactory; - StringRef typeName; + // If we don't find it, fall back to _Concurrency.PlatformExecutorFactory + if (!factory) + factory = getDefaultExecutorFactory(); - auto parts = qualifiedName.split('.'); - - if (parts.second.empty()) { - // This was an unqualified name; assume it's relative to the main module - module = ctx.MainModule; - typeName = qualifiedName; - } else { - Identifier moduleName = ctx.getIdentifier(parts.first); - module = ctx.getModuleByIdentifier(moduleName); - typeName = parts.second; - } - - return ctx.getNamedSwiftType(module, typeName); + return factory; } Type SILGenModule::getDefaultExecutorFactory() { diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index b3902ff2e39..81f8033691c 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4999,6 +4999,7 @@ class CallEmission { FormalEvaluationScope initialWritebackScope; std::optional implicitActorHopTarget; bool implicitlyThrows; + bool canUnwind; public: /// Create an emission for a call of the given callee. @@ -5006,7 +5007,8 @@ public: FormalEvaluationScope &&writebackScope) : SGF(SGF), callee(std::move(callee)), initialWritebackScope(std::move(writebackScope)), - implicitActorHopTarget(std::nullopt), implicitlyThrows(false) {} + implicitActorHopTarget(std::nullopt), implicitlyThrows(false), + canUnwind(false) {} /// A factory method for decomposing the apply expr \p e into a call /// emission. @@ -5057,6 +5059,8 @@ public: /// function can throw or not. void setImplicitlyThrows(bool flag) { implicitlyThrows = flag; } + void setCanUnwind(bool flag) { canUnwind = flag; } + CleanupHandle applyCoroutine(SmallVectorImpl &yields); RValue apply(SGFContext C = SGFContext()) { @@ -5114,9 +5118,11 @@ namespace { /// Cleanup to end a coroutine application. class EndCoroutineApply : public Cleanup { SILValue ApplyToken; + bool CanUnwind; std::vector BorrowedMoveOnlyValues; public: - EndCoroutineApply(SILValue applyToken) : ApplyToken(applyToken) {} + EndCoroutineApply(SILValue applyToken, bool CanUnwind) + : ApplyToken(applyToken), CanUnwind(CanUnwind) {} void setBorrowedMoveOnlyValues(ArrayRef values) { BorrowedMoveOnlyValues.insert(BorrowedMoveOnlyValues.end(), @@ -5129,14 +5135,7 @@ public: SGF.B.createEndBorrow(l, *i); SGF.B.createDestroyValue(l, (*i)->getOperand()); } - auto *beginApply = - cast(ApplyToken->getDefiningInstruction()); - auto isCalleeAllocated = beginApply->isCalleeAllocated(); - auto unwindOnCallerError = - !isCalleeAllocated || - SGF.SGM.getASTContext().LangOpts.hasFeature( - Feature::CoroutineAccessorsUnwindOnCallerError); - if (forUnwind && unwindOnCallerError) { + if (forUnwind && CanUnwind) { SGF.B.createAbortApply(l, ApplyToken); } else { SGF.B.createEndApply(l, ApplyToken, @@ -5179,18 +5178,15 @@ CallEmission::applyCoroutine(SmallVectorImpl &yields) { auto fnValue = callee.getFnValue(SGF, borrowedSelf); - return SGF.emitBeginApply(uncurriedLoc.value(), fnValue, + return SGF.emitBeginApply(uncurriedLoc.value(), fnValue, canUnwind, callee.getSubstitutions(), uncurriedArgs, calleeTypeInfo.substFnType, options, yields); } -CleanupHandle -SILGenFunction::emitBeginApply(SILLocation loc, ManagedValue fn, - SubstitutionMap subs, - ArrayRef args, - CanSILFunctionType substFnType, - ApplyOptions options, - SmallVectorImpl &yields) { +CleanupHandle SILGenFunction::emitBeginApply( + SILLocation loc, ManagedValue fn, bool canUnwind, SubstitutionMap subs, + ArrayRef args, CanSILFunctionType substFnType, + ApplyOptions options, SmallVectorImpl &yields) { // Emit the call. SmallVector rawResults; emitRawApply(*this, loc, fn, subs, args, substFnType, options, @@ -5206,7 +5202,7 @@ SILGenFunction::emitBeginApply(SILLocation loc, ManagedValue fn, // Push a cleanup to end the application. // TODO: destroy all the arguments at exactly this point? - Cleanups.pushCleanup(token); + Cleanups.pushCleanup(token, canUnwind); auto endApplyHandle = getTopCleanup(); // Manage all the yielded values. @@ -6183,7 +6179,7 @@ SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn, std::tuple SILGenFunction::emitBeginApplyWithRethrow(SILLocation loc, SILValue fn, - SILType substFnType, + SILType substFnType, bool canUnwind, SubstitutionMap subs, ArrayRef args, SmallVectorImpl &yields) { @@ -6208,7 +6204,7 @@ SILGenFunction::emitBeginApplyWithRethrow(SILLocation loc, SILValue fn, deallocCleanup = enterDeallocStackCleanup(allocation); } - Cleanups.pushCleanup(token); + Cleanups.pushCleanup(token, canUnwind); auto abortCleanup = Cleanups.getTopCleanup(); return {token, abortCleanup, allocation, deallocCleanup}; @@ -7561,6 +7557,21 @@ ManagedValue SILGenFunction::emitAddressorAccessor( return ManagedValue::forLValue(address); } +bool SILGenFunction::canUnwindAccessorDeclRef(SILDeclRef accessorRef) { + auto *accessor = + dyn_cast_or_null(accessorRef.getAbstractFunctionDecl()); + ASSERT(accessor && "only accessors can unwind"); + auto kind = accessor->getAccessorKind(); + ASSERT(isYieldingAccessor(kind) && "only yielding accessors can unwind"); + if (!requiresFeatureCoroutineAccessors(kind)) { + // _read and _modify can unwind + return true; + } + // Coroutine accessors can only unwind with the experimental feature. + return getASTContext().LangOpts.hasFeature( + Feature::CoroutineAccessorsUnwindOnCallerError); +} + CleanupHandle SILGenFunction::emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor, SubstitutionMap substitutions, @@ -7596,6 +7607,8 @@ SILGenFunction::emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor, emission.addCallSite(loc, std::move(subscriptIndices)); + emission.setCanUnwind(canUnwindAccessorDeclRef(accessor)); + auto endApplyHandle = emission.applyCoroutine(yields); return endApplyHandle; diff --git a/lib/SILGen/SILGenDynamicCast.cpp b/lib/SILGen/SILGenDynamicCast.cpp index 567cd78a7e6..88cf06f27b2 100644 --- a/lib/SILGen/SILGenDynamicCast.cpp +++ b/lib/SILGen/SILGenDynamicCast.cpp @@ -18,6 +18,7 @@ #include "ExitableFullExpr.h" #include "swift/Basic/Assertions.h" #include "swift/AST/ConformanceLookup.h" +#include "swift/AST/ExistentialLayout.h" #include "swift/SIL/DynamicCasts.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/TypeLowering.h" @@ -309,7 +310,7 @@ namespace { return CastingIsolatedConformances::Allow; // If there is a conformance to SendableMetatype, then this existential - // can leave the current isolation domain. Prohibit isolated conformances. + // can leave the current isolation domain. ASTContext &ctx = TargetType->getASTContext(); Type checkType; if (auto existentialMetatype = TargetType->getAs()) @@ -317,6 +318,14 @@ namespace { else checkType = TargetType; + // If there are no non-marker protocols in the existential, there's no + // need to prohibit isolated conformances. + auto layout = checkType->getExistentialLayout(); + if (!layout.containsNonMarkerProtocols()) + return CastingIsolatedConformances::Allow; + + // If the type conforms to SendableMetatype, prohibit isolated + // conformances. auto proto = ctx.getProtocol(KnownProtocolKind::SendableMetatype); if (proto && lookupConformance(checkType, proto, /*allowMissing=*/false)) return CastingIsolatedConformances::Prohibit; diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 4341c4d024e..855581cc373 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -489,11 +489,11 @@ namespace { SGFContext C); /// Helper method for handling function conversion expr to - /// @execution(caller). Returns an empty RValue on failure. + /// nonisolated(nonsending). Returns an empty RValue on failure. RValue emitFunctionCvtToExecutionCaller(FunctionConversionExpr *E, SGFContext C); /// Helper method for handling function conversion expr to a global actor - /// from an @execution(caller) function. + /// from an nonisolated(nonsending) function. RValue emitFunctionCvtFromExecutionCallerToGlobalActor(FunctionConversionExpr *E, SGFContext C); @@ -1968,17 +1968,17 @@ RValueEmitter::emitFunctionCvtToExecutionCaller(FunctionConversionExpr *e, // // Swift 6: // - // (fn_cvt_expr type="@execution(caller) () async -> ()" - // (fn_cvt_expr type="@execution(caller) @Sendable () async -> ()" + // (fn_cvt_expr type="nonisolated(nonsending) () async -> ()" + // (fn_cvt_expr type="nonisolated(nonsending) @Sendable () async -> ()" // (declref_expr type="() async -> ()" // // Swift 5: // - // (fn_cvt_expr type="@execution(caller) () async -> ()" + // (fn_cvt_expr type="nonisolated(nonsending) () async -> ()" // (declref_expr type="() async -> ()" // // The @Sendable in Swift 6 mode is due to us not representing - // @execution(caller) or @Sendable in the constraint evaluator. + // nonisolated(nonsending) or @Sendable in the constraint evaluator. // // The reason why we need to evaluate this especially is that otherwise we // generate multiple @@ -2038,7 +2038,7 @@ RValue RValueEmitter::emitFunctionCvtFromExecutionCallerToGlobalActor( // We are pattern matching a conversion sequence like the following: // // (fn_cvt_expr implicit type="@GlobalActor @Sendable () async -> () - // (fn_cvt_expr implicit type="@execution(caller) @Sendable () async -> ()" + // (fn_cvt_expr implicit type="nonisolated(nonsending) @Sendable () async -> ()" // (declref_expr type="() async -> ()" // // Where the declref referred to by the declref_expr has execution(caller) @@ -2047,9 +2047,9 @@ RValue RValueEmitter::emitFunctionCvtFromExecutionCallerToGlobalActor( // fix it up later. // // What we want to emit first a direct reference to the caller as an - // @execution(caller) function, then we convert it to @execution(caller) - // @Sendable. Finally, we thunk @execution(caller) to @GlobalActor. The - // thunking is important so that we can ensure that @execution(caller) runs on + // nonisolated(nonsending) function, then we convert it to nonisolated(nonsending) + // @Sendable. Finally, we thunk nonisolated(nonsending) to @GlobalActor. The + // thunking is important so that we can ensure that nonisolated(nonsending) runs on // that specific @GlobalActor. CanAnyFunctionType destType = @@ -2201,13 +2201,13 @@ RValue RValueEmitter::visitFunctionConversionExpr(FunctionConversionExpr *e, } } - // Check if we are converting a function to an @execution(caller) from a - // declref that is also @execution(caller). In such a case, this was a case + // Check if we are converting a function to an nonisolated(nonsending) from a + // declref that is also nonisolated(nonsending). In such a case, this was a case // that was put in by Sema. We do not need a thunk, but just need to recognize // this case and elide the conversion. The reason why we need to do this is - // that otherwise, we put in extra thunks that convert @execution(caller) to - // @execution(concurrent) back to @execution(caller). This is done b/c we do - // not represent @execution(caller) in interface types, so the actual decl ref + // that otherwise, we put in extra thunks that convert nonisolated(nonsending) to + // @concurrent back to nonisolated(nonsending). This is done b/c we do + // not represent nonisolated(nonsending) in interface types, so the actual decl ref // will be viewed as @async () -> (). if (destType->getIsolation().isNonIsolatedCaller()) { if (RValue rv = emitFunctionCvtToExecutionCaller(e, C)) @@ -7178,6 +7178,14 @@ RValue RValueEmitter::visitCopyExpr(CopyExpr *E, SGFContext C) { auto address = SGF.emitAddressOfLValue(subExpr, std::move(lv)); if (subType.isLoadable(SGF.F)) { + // Trivial types don't undergo any lifetime analysis, so simply load + // the value. + if (subType.isTrivial(SGF.F) + && !address.getType().isMoveOnlyWrapped()) { + return RValue(SGF, {SGF.B.createLoadCopy(E, address)}, + subType.getASTType()); + } + // Use a formal access load borrow so this closes in the writeback scope // above. ManagedValue value = SGF.B.createFormalAccessLoadBorrow(E, address); diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index fe47d7a623c..77bee84901d 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -1424,51 +1424,44 @@ void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) { B.setInsertionPoint(entryBlock); - // If we're using a new enough deployment target, call swift_createExecutors() - if (ctx.LangOpts.ExecutorFactory) { - if (!isCreateExecutorsFunctionAvailable(SGM)) { - ctx.Diags.diagnose(SourceLoc(), diag::executor_factory_not_supported); - } else { - CanType factoryTy = SGM.getConfiguredExecutorFactory()->getCanonicalType(); + // If we're using a new enough deployment target, and we can find a + // DefaultExecutorFactory type, call swift_createExecutors() + Type factoryNonCanTy = SGM.getConfiguredExecutorFactory(); - if (!factoryTy) { - ctx.Diags.diagnose(SourceLoc(), diag::cannot_find_executor_factory_type, - *ctx.LangOpts.ExecutorFactory); - } + if (isCreateExecutorsFunctionAvailable(SGM) && factoryNonCanTy) { + CanType factoryTy = factoryNonCanTy->getCanonicalType(); - ProtocolDecl *executorFactoryProtocol - = ctx.getProtocol(KnownProtocolKind::ExecutorFactory); - auto conformance = lookupConformance(factoryTy, executorFactoryProtocol); + ProtocolDecl *executorFactoryProtocol + = ctx.getProtocol(KnownProtocolKind::ExecutorFactory); + auto conformance = lookupConformance(factoryTy, executorFactoryProtocol); - if (conformance.isInvalid()) { - // If this type doesn't conform, ignore it and use the default factory - SourceLoc loc = extractNearestSourceLoc(factoryTy); + if (conformance.isInvalid()) { + // If this type doesn't conform, ignore it and use the default factory + SourceLoc loc = extractNearestSourceLoc(factoryTy); - ctx.Diags.diagnose(loc, diag::executor_factory_must_conform, - *ctx.LangOpts.ExecutorFactory); + ctx.Diags.diagnose(loc, diag::executor_factory_must_conform); - factoryTy = SGM.getDefaultExecutorFactory()->getCanonicalType(); - conformance = lookupConformance(factoryTy, executorFactoryProtocol); + factoryTy = SGM.getDefaultExecutorFactory()->getCanonicalType(); + conformance = lookupConformance(factoryTy, executorFactoryProtocol); - assert(!conformance.isInvalid()); - } - - FuncDecl *createExecutorsFuncDecl = SGM.getCreateExecutors(); - assert(createExecutorsFuncDecl - && "Failed to find swift_createExecutors function decl"); - SILFunction *createExecutorsSILFunc = - SGM.getFunction(SILDeclRef(createExecutorsFuncDecl, SILDeclRef::Kind::Func), - NotForDefinition); - SILValue createExecutorsFunc = - B.createFunctionRefFor(moduleLoc, createExecutorsSILFunc); - MetatypeType *factoryThickMetaTy - = MetatypeType::get(factoryTy, MetatypeRepresentation::Thick); - SILValue factorySILMetaTy - = B.createMetatype(moduleLoc, getLoweredType(factoryThickMetaTy)); - auto ceSubs = SubstitutionMap::getProtocolSubstitutions( - conformance.getProtocol(), factoryTy, conformance); - B.createApply(moduleLoc, createExecutorsFunc, ceSubs, { factorySILMetaTy }); + assert(!conformance.isInvalid()); } + + FuncDecl *createExecutorsFuncDecl = SGM.getCreateExecutors(); + assert(createExecutorsFuncDecl + && "Failed to find swift_createExecutors function decl"); + SILFunction *createExecutorsSILFunc = + SGM.getFunction(SILDeclRef(createExecutorsFuncDecl, SILDeclRef::Kind::Func), + NotForDefinition); + SILValue createExecutorsFunc = + B.createFunctionRefFor(moduleLoc, createExecutorsSILFunc); + MetatypeType *factoryThickMetaTy + = MetatypeType::get(factoryTy, MetatypeRepresentation::Thick); + SILValue factorySILMetaTy + = B.createMetatype(moduleLoc, getLoweredType(factoryThickMetaTy)); + auto ceSubs = SubstitutionMap::getProtocolSubstitutions( + conformance.getProtocol(), factoryTy, conformance); + B.createApply(moduleLoc, createExecutorsFunc, ceSubs, { factorySILMetaTy }); } auto wrapCallArgs = [this, &moduleLoc](SILValue originalValue, FuncDecl *fd, diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index ce11a54719c..c0e5386a79d 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -2034,6 +2034,7 @@ public: PreparedArguments &&optionalSubscripts, SILType addressType, bool isOnSelfParameter); + bool canUnwindAccessorDeclRef(SILDeclRef accessorRef); CleanupHandle emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor, SubstitutionMap substitutions, ArgumentSource &&optionalSelfValue, @@ -2301,8 +2302,9 @@ public: PreparedArguments &&args, Type overriddenSelfType, SGFContext ctx); - CleanupHandle emitBeginApply(SILLocation loc, ManagedValue fn, - SubstitutionMap subs, ArrayRef args, + CleanupHandle emitBeginApply(SILLocation loc, ManagedValue fn, bool canUnwind, + SubstitutionMap subs, + ArrayRef args, CanSILFunctionType substFnType, ApplyOptions options, SmallVectorImpl &yields); @@ -2315,7 +2317,8 @@ public: std::tuple emitBeginApplyWithRethrow(SILLocation loc, SILValue fn, SILType substFnType, - SubstitutionMap subs, ArrayRef args, + bool canUnwind, SubstitutionMap subs, + ArrayRef args, SmallVectorImpl &yields); void emitEndApplyWithRethrow(SILLocation loc, MultipleValueInstructionResult *token, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index e360e5b7ae3..cc6e0af5208 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -2535,9 +2535,9 @@ namespace { // Perform the begin_apply. SmallVector yields; - auto cleanup = - SGF.emitBeginApply(loc, projectFnRef, subs, { base, keyPathValue }, - substFnType, ApplyOptions(), yields); + auto cleanup = SGF.emitBeginApply(loc, projectFnRef, /*canUnwind=*/true, + subs, {base, keyPathValue}, substFnType, + ApplyOptions(), yields); // Push an operation to do the end_apply. pushEndApplyWriteback(SGF, loc, cleanup, getTypeData()); diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 409894e5882..17d712f5e8e 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -7045,8 +7045,8 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, case SILCoroutineKind::YieldOnce2: { SmallVector derivedYields; auto tokenAndCleanups = emitBeginApplyWithRethrow( - loc, derivedRef, SILType::getPrimitiveObjectType(derivedFTy), subs, - args, derivedYields); + loc, derivedRef, SILType::getPrimitiveObjectType(derivedFTy), + canUnwindAccessorDeclRef(base), subs, args, derivedYields); auto token = std::get<0>(tokenAndCleanups); auto abortCleanup = std::get<1>(tokenAndCleanups); auto allocation = std::get<2>(tokenAndCleanups); @@ -7513,7 +7513,8 @@ void SILGenFunction::emitProtocolWitness( case SILCoroutineKind::YieldOnce2: { SmallVector witnessYields; auto tokenAndCleanups = emitBeginApplyWithRethrow( - loc, witnessFnRef, witnessSILTy, witnessSubs, args, witnessYields); + loc, witnessFnRef, witnessSILTy, canUnwindAccessorDeclRef(requirement), + witnessSubs, args, witnessYields); auto token = std::get<0>(tokenAndCleanups); auto abortCleanup = std::get<1>(tokenAndCleanups); auto allocation = std::get<2>(tokenAndCleanups); diff --git a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp index 9881890ac65..02253cc16f1 100644 --- a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp @@ -93,21 +93,40 @@ regionanalysisimpl::getApplyIsolationCrossing(SILInstruction *inst) { namespace { -struct UseDefChainVisitor - : public AccessUseDefChainVisitor { - bool isMerge = false; +/// A visitor that walks from uses -> def attempting to determine an object or +/// address base for the passed in address. +/// +/// It also is used to determine if we are assigning into a part of an aggregate +/// or are assigning over an entire value. +/// +/// RULES: +/// +/// 1. We allow for sendable types to be rooted in non-Sendable types. We allow +/// for our caller to reason about this by placing in our metadata if the entire +/// search path was just sendable types. If any are non-Sendable except for our +/// base, we need to be more conservative. +/// +/// 2. We stop when we find a non-Sendable type rooted in a Sendable type. In +/// such a case, we want to process the non-Sendable type as if it is rooted in +/// the Sendable type. +struct AddressBaseComputingVisitor + : public AccessUseDefChainVisitor { + bool isProjectedFromAggregate = false; - /// The actor isolation that we found while walking from use->def. Always set - /// to the first one encountered. - std::optional actorIsolation; + SILValue value; SILValue visitAll(SILValue sourceAddr) { + // If our initial value is Sendable, then it is our "value". + if (SILIsolationInfo::isSendableType(sourceAddr)) + value = sourceAddr; + SILValue result = visit(sourceAddr); if (!result) return sourceAddr; - while (SILValue nextAddr = visit(result)) + while (SILValue nextAddr = visit(result)) { result = nextAddr; + } return result; } @@ -153,23 +172,24 @@ struct UseDefChainVisitor return SILValue(); } - // If we do not have an identity cast, mark this as a merge. - isMerge |= castType != AccessStorageCast::Identity; + // If we do not have an identity cast, mark this as if we are projecting out + // of an aggregate that will require us to merge. + isProjectedFromAggregate |= castType != AccessStorageCast::Identity; return sourceAddr->get(); } - SILValue visitAccessProjection(SingleValueInstruction *inst, + SILValue visitAccessProjection(SingleValueInstruction *projInst, Operand *sourceAddr) { // See if this access projection is into a single element value. If so, we // do not want to treat this as a merge. - if (auto p = Projection(inst)) { + if (auto p = Projection(projInst)) { switch (p.getKind()) { // Currently if we load and then project_box from a memory location, // we treat that as a projection. This follows the semantics/notes in // getAccessProjectionOperand. case ProjectionKind::Box: - return cast(inst)->getOperand(); + return cast(projInst)->getOperand(); case ProjectionKind::Upcast: case ProjectionKind::RefCast: case ProjectionKind::BlockStorageCast: @@ -179,44 +199,58 @@ struct UseDefChainVisitor llvm_unreachable("Shouldn't see this here"); case ProjectionKind::Index: // Index is always a merge. - isMerge = true; + isProjectedFromAggregate = true; break; case ProjectionKind::Enum: { - auto op = cast(inst)->getOperand(); + auto op = cast(projInst)->getOperand(); - // See if our operand type is a sendable type. In such a case, we do not - // want to look through our operand. - if (!SILIsolationInfo::isNonSendableType(op->getType(), - op->getFunction())) - return SILValue(); + bool isOperandSendable = !SILIsolationInfo::isNonSendableType( + op->getType(), op->getFunction()); + + // If our operand is Sendable and our field is non-Sendable and we have + // not stashed a value yet, stash value. + if (!value && isOperandSendable && + SILIsolationInfo::isNonSendableType(projInst->getType(), + projInst->getFunction())) { + value = projInst; + } break; } case ProjectionKind::Tuple: { // These are merges if we have multiple fields. - auto op = cast(inst)->getOperand(); + auto op = cast(projInst)->getOperand(); - if (!SILIsolationInfo::isNonSendableType(op->getType(), - op->getFunction())) - return SILValue(); + bool isOperandSendable = !SILIsolationInfo::isNonSendableType( + op->getType(), op->getFunction()); - isMerge |= op->getType().getNumTupleElements() > 1; + // If our operand is Sendable and our field is non-Sendable, we need to + // bail since we want to root the non-Sendable type in the Sendable + // type. + if (!value && isOperandSendable && + SILIsolationInfo::isNonSendableType(projInst->getType(), + projInst->getFunction())) + value = projInst; + + isProjectedFromAggregate |= op->getType().getNumTupleElements() > 1; break; } case ProjectionKind::Struct: - auto op = cast(inst)->getOperand(); + auto op = cast(projInst)->getOperand(); - // See if our result type is a sendable type. In such a case, we do not - // want to look through the struct_element_addr since we do not want to - // identify the sendable type with the non-sendable operand. These we - // are always going to ignore anyways since a sendable let/var field of - // a struct can always be used. - if (!SILIsolationInfo::isNonSendableType(op->getType(), - op->getFunction())) - return SILValue(); + bool isOperandSendable = !SILIsolationInfo::isNonSendableType( + op->getType(), op->getFunction()); + + // If our operand is Sendable and our field is non-Sendable, we need to + // bail since we want to root the non-Sendable type in the Sendable + // type. + if (!value && isOperandSendable && + SILIsolationInfo::isNonSendableType(projInst->getType(), + projInst->getFunction())) + value = projInst; // These are merges if we have multiple fields. - isMerge |= op->getType().getNumNominalFields() > 1; + isProjectedFromAggregate |= op->getType().getNumNominalFields() > 1; break; } } @@ -275,6 +309,9 @@ static bool isStaticallyLookThroughInst(SILInstruction *inst) { case SILInstructionKind::RefToUnmanagedInst: case SILInstructionKind::UnmanagedToRefInst: case SILInstructionKind::InitExistentialValueInst: + case SILInstructionKind::UncheckedEnumDataInst: + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::TupleElementAddrInst: return true; case SILInstructionKind::MoveValueInst: // Look through if it isn't from a var decl. @@ -292,24 +329,6 @@ static bool isStaticallyLookThroughInst(SILInstruction *inst) { } } -static bool isLookThroughIfResultNonSendable(SILInstruction *inst) { - switch (inst->getKind()) { - default: - return false; - case SILInstructionKind::RawPointerToRefInst: - return true; - } -} - -static bool isLookThroughIfOperandNonSendable(SILInstruction *inst) { - switch (inst->getKind()) { - default: - return false; - case SILInstructionKind::RefToRawPointerInst: - return true; - } -} - static bool isLookThroughIfOperandAndResultNonSendable(SILInstruction *inst) { switch (inst->getKind()) { default: @@ -317,11 +336,10 @@ static bool isLookThroughIfOperandAndResultNonSendable(SILInstruction *inst) { case SILInstructionKind::UncheckedTrivialBitCastInst: case SILInstructionKind::UncheckedBitwiseCastInst: case SILInstructionKind::UncheckedValueCastInst: - case SILInstructionKind::StructElementAddrInst: - case SILInstructionKind::TupleElementAddrInst: - case SILInstructionKind::UncheckedTakeEnumDataAddrInst: case SILInstructionKind::ConvertEscapeToNoEscapeInst: case SILInstructionKind::ConvertFunctionInst: + case SILInstructionKind::RefToRawPointerInst: + case SILInstructionKind::RawPointerToRefInst: return true; } } @@ -395,9 +413,9 @@ private: static bool isProjectedFromAggregate(SILValue value) { assert(value->getType().isAddress()); - UseDefChainVisitor visitor; + AddressBaseComputingVisitor visitor; visitor.visitAll(value); - return visitor.isMerge; + return visitor.isProjectedFromAggregate; } namespace { @@ -623,13 +641,8 @@ RegionAnalysisValueMap::initializeTrackableValue( return {{iter.first->first, iter.first->second}, true}; } -/// If \p isAddressCapturedByPartialApply is set to true, then this value is -/// an address that is captured by a partial_apply and we want to treat it as -/// may alias. -TrackableValue RegionAnalysisValueMap::getTrackableValue( +TrackableValue RegionAnalysisValueMap::getTrackableValueHelper( SILValue value, bool isAddressCapturedByPartialApply) const { - auto info = getUnderlyingTrackedValue(value); - value = info.value; auto *self = const_cast(this); auto iter = self->equivalenceClassValuesToState.try_emplace( @@ -667,23 +680,6 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue( // Ok, at this point we have a non-Sendable value. First process addresses. if (value->getType().isAddress()) { - // If we were able to find this was actor isolated from finding our - // underlying object, use that. It is never wrong. - if (info.actorIsolation) { - SILIsolationInfo isolation; - if (info.value->getType().isAnyActor()) { - isolation = SILIsolationInfo::getActorInstanceIsolated( - value, info.value, info.actorIsolation->getActor()); - } else if (info.actorIsolation->isGlobalActor()) { - isolation = SILIsolationInfo::getGlobalActorIsolated( - value, info.actorIsolation->getGlobalActor()); - } - - if (isolation) { - iter.first->getSecond().setIsolationRegionInfo(isolation); - } - } - auto storage = AccessStorageWithBase::compute(value); if (storage.storage) { // Check if we have a uniquely identified address that was not captured @@ -705,19 +701,6 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue( if (isa(iter.first->first.getValue())) { auto *svi = cast(iter.first->first.getValue()); - // See if we can use get underlying tracked value to find if it is actor - // isolated. - // - // TODO: Instead of using AccessStorageBase, just use our own visitor - // everywhere. Just haven't done it due to possible perturbations. - auto parentAddrInfo = getUnderlyingTrackedValue(svi); - if (parentAddrInfo.actorIsolation) { - iter.first->getSecond().setIsolationRegionInfo( - SILIsolationInfo::getActorInstanceIsolated( - svi, parentAddrInfo.value, - parentAddrInfo.actorIsolation->getActor())); - } - auto storage = AccessStorageWithBase::compute(svi->getOperand(0)); if (storage.storage) { if (auto isolation = SILIsolationInfo::get(storage.base)) { @@ -740,6 +723,25 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue( return {iter.first->first, iter.first->second}; } +/// If \p isAddressCapturedByPartialApply is set to true, then this value is +/// an address that is captured by a partial_apply and we want to treat it as +/// may alias. +TrackableValueLookupResult RegionAnalysisValueMap::getTrackableValue( + SILValue inputValue, bool isAddressCapturedByPartialApply) const { + auto info = getUnderlyingTrackedValue(inputValue); + SILValue value = info.value; + SILValue base = info.base; + + auto trackedValue = + getTrackableValueHelper(value, isAddressCapturedByPartialApply); + + std::optional trackedBase; + if (base) + trackedBase = + getTrackableValueHelper(base, isAddressCapturedByPartialApply); + return {trackedValue, trackedBase}; +} + std::optional RegionAnalysisValueMap::getTrackableValueForActorIntroducingInst( SILInstruction *inst) const { @@ -752,10 +754,11 @@ RegionAnalysisValueMap::getTrackableValueForActorIntroducingInst( return {{iter->first, iter->second}}; } -std::optional +std::optional RegionAnalysisValueMap::tryToTrackValue(SILValue value) const { auto state = getTrackableValue(value); - if (state.isNonSendable()) + if (state.value.isNonSendable() || + (state.base && state.base->isNonSendable())) return state; return {}; } @@ -780,7 +783,7 @@ TrackableValue RegionAnalysisValueMap::getActorIntroducingRepresentative( } bool RegionAnalysisValueMap::valueHasID(SILValue value, bool dumpIfHasNoID) { - assert(getTrackableValue(value).isNonSendable() && + assert(getTrackableValue(value).value.isNonSendable() && "Can only accept non-Sendable values"); bool hasID = equivalenceClassValuesToState.count(value); if (!hasID && dumpIfHasNoID) { @@ -792,7 +795,7 @@ bool RegionAnalysisValueMap::valueHasID(SILValue value, bool dumpIfHasNoID) { } Element RegionAnalysisValueMap::lookupValueID(SILValue value) { - auto state = getTrackableValue(value); + auto state = getTrackableValue(value).value; assert(state.isNonSendable() && "only non-Sendable values should be entered in the map"); return state.getID(); @@ -815,15 +818,88 @@ void RegionAnalysisValueMap::print(llvm::raw_ostream &os) const { #endif } -static SILValue getUnderlyingTrackedObjectValue(SILValue value) { - auto *fn = value->getFunction(); - SILValue result = value; - while (true) { - SILValue temp = result; +namespace { - if (auto *svi = dyn_cast(temp)) { +/// Walk from use->def looking through instructions. We have two goals here: +/// determining the actual equivilance class representative for the passed in +/// value and looking more aggressively up through uses (even Sendable ones) to +/// see if we are potentially accessing a base address value as a result of our +/// value being loaded. We explain these two below in more detail. +/// +/// # Determining the equivalence class representative for our initial value +/// +/// We want to determine the actual underlying object that is meant to be used +/// as the underlying object for our initial value. This value must either be +/// Sendable (in case which we take the first one). Or if it is non-Sendable, we +/// need to walk back through instructions that we consider look through until +/// we hit a Sendable value. We want to view the non-Sendable value as rooted in +/// the Sendable value since that Sendable value will likely be global actor +/// isolated or unchecked Sendable and we want SILIsolationInfo to infer +/// isolation from that instruction. +/// +/// # Determining the "base" for our initial value +/// +/// While the above process would be enough if we lived just in a world of +/// objects, we have to consider additionally the possibility that our object +/// value was loaded from a var, a non-copyable let that was captured, or an +/// address only type. In that case, even though generally the frontend performs +/// all transformations before loading: +/// +/// ```sil +/// %0 = alloc_stack $Type +/// %1 = struct_element_addr %0, $Type, #Type.field +/// %2 = load [copy] %1 : $FieldType +/// ``` +/// +/// To truly be complete, we need to be able to handle cases where the frontend +/// has instead emitted the projection on the object: +/// +/// ```sil +/// %0 = alloc_stack $Type +/// %1 = load_borrow %1 : $Type +/// %2 = struct_extract %1 : $Type, #Type.field +/// ``` +/// +/// To ensure that we can always find that base, we need to do some work like we +/// do in the AddressBaseComputingVisitor: namely we look through sendable +/// object projections (and object projections in general) until we find what I +/// term an object base (e.x.: a function argument, an apply result, a cast from +/// a RawPointer) or a load instruction. If we find a load instruction, then our +/// caller can know to run AddressBaseComputingVisitor to determine if we are +/// loading from a box in order to grab the value. +/// +/// NOTE: The author has only seen this happen so far with unchecked_enum_data, +/// but relying on the frontend to emit code in a specific manner rather than +/// coming up with a model that works completely in SIL no matter what the +/// frontend does is the only way to have a coherent, non-brittle +/// model. Otherwise, there is an implicit contract in between the frontend and +/// the optimizer where both are following each other making it easy for the two +/// to get out of sync. +struct UnderlyingTrackedObjectValueVisitor { + SILValue value; + +private: + /// Visit \p sourceValue returning a load base if we find one. The actual + /// underlying object is value. + SILValue visit(SILValue sourceValue) { + auto *fn = sourceValue->getFunction(); + + // If our result is ever Sendable, we record that as our value if we do + // not have a value yet. We always want to take the first one. + if (SILIsolationInfo::isSendableType(sourceValue->getType(), fn)) { + if (!value) { + value = sourceValue; + } + } + + if (auto *svi = dyn_cast(sourceValue)) { if (isStaticallyLookThroughInst(svi)) { - temp = svi->getOperand(0); + if (!value && SILIsolationInfo::isSendableType(svi->getOperand(0)) && + SILIsolationInfo::isNonSendableType(svi)) { + value = svi; + } + + return svi->getOperand(0); } // If we have a cast and our operand and result are non-Sendable, treat it @@ -832,78 +908,159 @@ static SILValue getUnderlyingTrackedObjectValue(SILValue value) { if (SILIsolationInfo::isNonSendableType(svi->getType(), fn) && SILIsolationInfo::isNonSendableType(svi->getOperand(0)->getType(), fn)) { - temp = svi->getOperand(0); + return svi->getOperand(0); } - } - if (isLookThroughIfResultNonSendable(svi)) { - if (SILIsolationInfo::isNonSendableType(svi->getType(), fn)) { - temp = svi->getOperand(0); - } - } - - if (isLookThroughIfOperandNonSendable(svi)) { - // If our operand is a non-Sendable type, look through this instruction. - if (SILIsolationInfo::isNonSendableType(svi->getOperand(0)->getType(), - fn)) { - temp = svi->getOperand(0); + if (!value && SILIsolationInfo::isSendableType(svi->getOperand(0)) && + SILIsolationInfo::isNonSendableType(svi)) { + value = svi; } } } - if (auto *inst = temp->getDefiningInstruction()) { + if (auto *inst = sourceValue->getDefiningInstruction()) { if (isStaticallyLookThroughInst(inst)) { - temp = inst->getOperand(0); + if (!value && SILIsolationInfo::isSendableType(inst->getOperand(0)) && + SILIsolationInfo::isNonSendableType(sourceValue)) { + value = sourceValue; + } + + return inst->getOperand(0); } } - if (temp != result) { - result = temp; - continue; - } + return SILValue(); + } + +public: + SILValue visitAll(SILValue sourceValue) { + // Before we do anything, + if (SILIsolationInfo::isSendableType(sourceValue)) + value = sourceValue; + + SILValue result = visit(sourceValue); + if (!result) + return sourceValue; + + while (SILValue nextValue = visit(result)) + result = nextValue; return result; } +}; + +} // namespace + +RegionAnalysisValueMap::UnderlyingTrackedValueInfo +RegionAnalysisValueMap::getUnderlyingTrackedValueHelperObject( + SILValue value) const { + // Look through all look through values stopping at any Sendable values. + UnderlyingTrackedObjectValueVisitor visitor; + SILValue baseValue = visitor.visitAll(value); + assert(baseValue->getType().isObject()); + + if (!visitor.value) { + return UnderlyingTrackedValueInfo(baseValue); + } + + assert(visitor.value->getType().isObject()); + if (visitor.value == baseValue) + return UnderlyingTrackedValueInfo(visitor.value); + return UnderlyingTrackedValueInfo(visitor.value, baseValue); +} + +RegionAnalysisValueMap::UnderlyingTrackedValueInfo +RegionAnalysisValueMap::getUnderlyingTrackedValueHelperAddress( + SILValue value) const { + AddressBaseComputingVisitor visitor; + SILValue base = visitor.visitAll(value); + assert(base); + + // If we have an object base... + if (base->getType().isObject()) { + // Recurse. + // + // NOTE: We purposely recurse into getUnderlyingTrackedValueHelper instead + // of getUnderlyingTrackedValue since we could cause an invalidation to + // occur in the underlying DenseMap that backs getUnderlyingTrackedValue() + // if we insert another entry into the DenseMap. + if (!visitor.value) + return UnderlyingTrackedValueInfo( + getUnderlyingTrackedValueHelperObject(base)); + + // TODO: Should we us the base or value from + // getUnderlyingTrackedValueHelperObject as our base? + return UnderlyingTrackedValueInfo( + visitor.value, getUnderlyingTrackedValueHelperObject(base).value); + } + + // Otherwise, we return the actorIsolation that our visitor found. + if (!visitor.value) + return UnderlyingTrackedValueInfo(base); + return UnderlyingTrackedValueInfo(visitor.value, base); } RegionAnalysisValueMap::UnderlyingTrackedValueInfo RegionAnalysisValueMap::getUnderlyingTrackedValueHelper(SILValue value) const { - // Before a check if the value we are attempting to access is Sendable. In - // such a case, just return early. - if (!SILIsolationInfo::isNonSendableType(value)) - return UnderlyingTrackedValueInfo(value); + // If we have an address, immediately delegate to the address implementation. + if (value->getType().isAddress()) { + auto addressInfo = getUnderlyingTrackedValueHelperAddress(value); - // Look through a project_box, so that we process it like its operand object. - if (auto *pbi = dyn_cast(value)) { - value = pbi->getOperand(); - } - - if (!value->getType().isAddress()) { - SILValue underlyingValue = getUnderlyingTrackedObjectValue(value); - - // If we do not have a load inst, just return the value. - if (!isa(underlyingValue)) { - return UnderlyingTrackedValueInfo(underlyingValue); + // See if we are returned a value that is a load or a load_borrow. In that + // case, we want to continue. + if (isa(addressInfo.value)) { + return getUnderlyingTrackedValueHelper( + cast(addressInfo.value)->getOperand(0)); } - // If we got an address, lets see if we can do even better by looking at the - // address. - value = cast(underlyingValue)->getOperand(0); - } - assert(value->getType().isAddress()); + // If our value is not a load, check if we have a base that is a load. In + // such a case, we want to search for a better base. + if (llvm::isa_and_nonnull(addressInfo.base)) { + auto newBase = getUnderlyingTrackedValueHelper( + cast(addressInfo.base)->getOperand(0)); + if (newBase.base) + return UnderlyingTrackedValueInfo(addressInfo.value, newBase.base); + return UnderlyingTrackedValueInfo(addressInfo.value, newBase.value); + } - UseDefChainVisitor visitor; - SILValue base = visitor.visitAll(value); - assert(base); - if (base->getType().isObject()) { - // NOTE: We purposely recurse into the cached version of our computation - // rather than recurse into getUnderlyingTrackedObjectValueHelper. This is - // safe since we know that value was previously an address so if our base is - // an object, it cannot be the same object. - return {getUnderlyingTrackedValue(base).value, visitor.actorIsolation}; + return addressInfo; } - return {base, visitor.actorIsolation}; + // Otherwise, we have an object and need to be a little smarter. First try to + // find the underlying value and base. + // + // NOTE: the base and value are guaranteed by invariant to always be different + // values. If they are the same, we just return as if we did not have a + // base. This ensures that if we both value/base return the same load, + // load_borrow, we process it along the no-base path below. + UnderlyingTrackedValueInfo objectInfo = + getUnderlyingTrackedValueHelperObject(value); + assert(objectInfo.value); + + // If we do not have a base... + if (!objectInfo.base) { + // If we do not have a load or a load_borrow, just return the value + // immediately. + if (!isa(objectInfo.value)) + return UnderlyingTrackedValueInfo(objectInfo.value); + + // Otherwise, look through the load/load_borrow and process its address. + auto *svi = dyn_cast(objectInfo.value); + return getUnderlyingTrackedValueHelperAddress(svi->getOperand(0)); + } + + // Ok, we have a value and a base. First check if our base is not a load or + // load_borrow. In such a case, we can just return our value. + if (!isa(objectInfo.base)) + return UnderlyingTrackedValueInfo(objectInfo.value); + + // If we have a base that is a load or load_borrow, we need to perform the + // address helper to find the true base. + auto *svi = dyn_cast(objectInfo.base); + auto addressInfo = getUnderlyingTrackedValueHelperAddress(svi->getOperand(0)); + if (addressInfo.base) + return UnderlyingTrackedValueInfo(objectInfo.value, addressInfo.base); + return UnderlyingTrackedValueInfo(objectInfo.value, addressInfo.value); } //===----------------------------------------------------------------------===// @@ -960,6 +1117,30 @@ bool TrackableValue::isSendingParameter() const { return false; } +//===----------------------------------------------------------------------===// +// MARK: TrackableValueLookupResult +//===----------------------------------------------------------------------===// + +void TrackableValueLookupResult::print(llvm::raw_ostream &os) const { + os << "Value:\n"; + value.print(os); + if (base) { + os << "Base:\n"; + base->print(os); + } +} + +//===----------------------------------------------------------------------===// +// MARK: UnderlyingTrackedValueInfo +//===----------------------------------------------------------------------===// + +void RegionAnalysisValueMap::UnderlyingTrackedValueInfo::print( + llvm::raw_ostream &os) const { + os << "RegionAnalysisValueMap.\nValue: " << value; + if (base) + os << "Base: " << base; +} + //===----------------------------------------------------------------------===// // MARK: Partial Apply Reachability //===----------------------------------------------------------------------===// @@ -1429,17 +1610,30 @@ struct PartitionOpBuilder { Element getActorIntroducingRepresentative(SILIsolationInfo actorIsolation); - void addAssignFresh(SILValue value) { - std::array values = {lookupValueID(value)}; + void addAssignFresh(TrackableValue value) { + if (value.isSendable()) + return; + auto id = lookupValueID(value.getRepresentative().getValue()); currentInstPartitionOps.emplace_back( - PartitionOp::AssignFresh(values, currentInst)); + PartitionOp::AssignFresh(id, currentInst)); } void addAssignFresh(ArrayRef values) { - auto transformedCollection = makeTransformRange( - values, [&](SILValue value) { return lookupValueID(value); }); + if (values.empty()) + return; + + auto first = lookupValueID(values.front()); currentInstPartitionOps.emplace_back( - PartitionOp::AssignFresh(transformedCollection, currentInst)); + PartitionOp::AssignFresh(first, currentInst)); + + auto transformedCollection = + makeTransformRange(values.drop_front(), [&](SILValue value) { + return lookupValueID(value); + }); + for (auto id : transformedCollection) { + currentInstPartitionOps.emplace_back( + PartitionOp::AssignFreshAssign(id, first, currentInst)); + } } void addAssign(SILValue destValue, Operand *srcOperand) { @@ -1461,7 +1655,11 @@ struct PartitionOpBuilder { lookupValueID(srcOperand->get()), srcOperand)); } - void addSend(SILValue representative, Operand *op) { + void addSend(TrackableValue value, Operand *op) { + if (value.isSendable()) + return; + + auto representative = value.getRepresentative().getValue(); assert(valueHasID(representative) && "sent value should already have been encountered"); @@ -1477,17 +1675,19 @@ struct PartitionOpBuilder { PartitionOp::UndoSend(lookupValueID(representative), unsendingInst)); } - void addMerge(SILValue destValue, Operand *srcOperand) { - assert(valueHasID(destValue, /*dumpIfHasNoID=*/true) && + void addMerge(TrackableValue destValue, Operand *srcOperand) { + if (destValue.isSendable()) + return; + auto rep = destValue.getRepresentative().getValue(); + assert(valueHasID(rep, /*dumpIfHasNoID=*/true) && valueHasID(srcOperand->get(), /*dumpIfHasNoID=*/true) && "merged values should already have been encountered"); - if (lookupValueID(destValue) == lookupValueID(srcOperand->get())) + if (lookupValueID(rep) == lookupValueID(srcOperand->get())) return; - currentInstPartitionOps.emplace_back( - PartitionOp::Merge(lookupValueID(destValue), - lookupValueID(srcOperand->get()), srcOperand)); + currentInstPartitionOps.emplace_back(PartitionOp::Merge( + lookupValueID(rep), lookupValueID(srcOperand->get()), srcOperand)); } /// Mark \p value artifically as being part of an actor isolated region by @@ -1504,18 +1704,27 @@ struct PartitionOpBuilder { PartitionOp::Merge(lookupValueID(sourceValue), elt, sourceOperand)); } - void addRequire(SILValue value) { - assert(valueHasID(value, /*dumpIfHasNoID=*/true) && - "required value should already have been encountered"); - currentInstPartitionOps.emplace_back( - PartitionOp::Require(lookupValueID(value), currentInst)); + void addRequire(TrackableValueLookupResult value); + +private: + void addRequire(TrackableValue value, PartitionOp::Options options = {}); + + void addRequire(std::optional value, + PartitionOp::Options options = {}) { + if (!value) + return; + addRequire(*value, options); } - void addInOutSendingAtFunctionExit(SILValue value) { - assert(valueHasID(value, /*dumpIfHasNoID=*/true) && +public: + void addInOutSendingAtFunctionExit(TrackableValue value) { + if (value.isSendable()) + return; + auto rep = value.getRepresentative().getValue(); + assert(valueHasID(rep, /*dumpIfHasNoID=*/true) && "required value should already have been encountered"); currentInstPartitionOps.emplace_back( - PartitionOp::InOutSendingAtFunctionExit(lookupValueID(value), + PartitionOp::InOutSendingAtFunctionExit(lookupValueID(rep), currentInst)); } @@ -1528,9 +1737,12 @@ struct PartitionOpBuilder { PartitionOp::UnknownPatternError(lookupValueID(value), currentInst)); } - void addNonSendableIsolationCrossingResultError(SILValue value) { + void addNonSendableIsolationCrossingResultError(TrackableValue value) { + if (value.isSendable()) + return; + auto rep = value.getRepresentative().getValue(); currentInstPartitionOps.emplace_back( - PartitionOp::NonSendableIsolationCrossingResult(lookupValueID(value), + PartitionOp::NonSendableIsolationCrossingResult(lookupValueID(rep), currentInst)); } @@ -1803,25 +2015,30 @@ public: llvm::SmallVector nonSendableSeparateIndices; for (SILArgument *arg : functionArguments) { // This will decide what the isolation region is. - if (auto state = tryToTrackValue(arg)) { + if (auto lookupResult = tryToTrackValue(arg)) { + auto value = lookupResult->value; + assert(value.isNonSendable() && + "We should never see a sendable function argument from " + "tryToTrackValue since we will never have a non-Sendable base"); + // If we can send our parameter, just add it to // nonSendableSeparateIndices. // // NOTE: We do not support today the ability to have multiple parameters // send together as part of the same region. if (canFunctionArgumentBeSent(cast(arg))) { - REGIONBASEDISOLATION_LOG(llvm::dbgs() << " %%" << state->getID() + REGIONBASEDISOLATION_LOG(llvm::dbgs() << " %%" << value.getID() << " (sending): " << *arg); - nonSendableSeparateIndices.push_back(state->getID()); + nonSendableSeparateIndices.push_back(value.getID()); continue; } // Otherwise, it is one of our merged parameters. Add it to the never // send list and to the region join list. REGIONBASEDISOLATION_LOG( - llvm::dbgs() << " %%" << state->getID() << ": "; - state->print(llvm::dbgs()); llvm::dbgs() << *arg); - nonSendableJoinedIndices.push_back(state->getID()); + llvm::dbgs() << " %%" << value.getID() << ": "; + value.print(llvm::dbgs()); llvm::dbgs() << *arg); + nonSendableJoinedIndices.push_back(value.getID()); } else { REGIONBASEDISOLATION_LOG(llvm::dbgs() << " Sendable: " << *arg); } @@ -1853,13 +2070,14 @@ private: return SILIsolationInfo::isNonSendableType(type, function); } - TrackableValue + TrackableValueLookupResult getTrackableValue(SILValue value, bool isAddressCapturedByPartialApply = false) { return valueMap.getTrackableValue(value, isAddressCapturedByPartialApply); } - std::optional tryToTrackValue(SILValue value) const { + std::optional + tryToTrackValue(SILValue value) const { return valueMap.tryToTrackValue(value); } @@ -1934,11 +2152,11 @@ public: /// the same value. template void - translateSILMultiAssign(const TargetRange &resultValues, + translateSILMultiAssign(const TargetRange &directResultValues, const SourceRange &sourceValues, SILIsolationInfo resultIsolationInfoOverride = {}, bool requireSrcValues = true) { - SmallVector, 8> assignOperands; + SmallVector, 8> assignOperands; SmallVector assignResults; // A helper we use to emit an unknown patten error if our merge is @@ -1952,15 +2170,38 @@ public: mergedInfo = SILDynamicMergedIsolationInfo::getDisconnected(false); } + // For each operand... for (Operand *srcOperand : sourceValues) { + // Grab our value... auto src = srcOperand->get(); - if (auto value = tryToTrackValue(src)) { - assignOperands.push_back( - {srcOperand, value->getRepresentative().getValue()}); + + // Perform our lookup result. + if (auto lookupResult = tryToTrackValue(src)) { + auto value = lookupResult->value; + + // If our value is non-Sendable require it and if we have a base and + // that base is non-Sendable, require that as well. + // + // DISCUSSION: The reason that this may be useful is for special + // instructions like store_borrow. On the one hand, we want store_borrow + // to act like a store in the sense that we want to combine the regions + // of its src and dest... but at the same time, we do not want to treat + // the store itself as a use of its parent value. We want that to be any + // subsequent uses of the store_borrow. + if (requireSrcValues) { + builder.addRequire(*lookupResult); + } + + // If our value was actually Sendable, skip it, we do not want to merge + // anything. + if (value.isSendable()) + continue; + + assignOperands.push_back({srcOperand, value}); auto originalMergedInfo = mergedInfo; (void)originalMergedInfo; if (mergedInfo) - mergedInfo = mergedInfo->merge(value->getIsolationRegionInfo()); + mergedInfo = mergedInfo->merge(value.getIsolationRegionInfo()); // If we fail to merge, then we have an incompatibility in between some // of our arguments (consider isolated to different actors) or with the @@ -1973,10 +2214,10 @@ public: originalMergedInfo->printForDiagnostics(llvm::dbgs()); else llvm::dbgs() << "nil"; llvm::dbgs() << "\nValue Rep: " - << value->getRepresentative().getValue(); + << value.getRepresentative().getValue(); llvm::dbgs() << "Original Src: " << src; llvm::dbgs() << "Value Info: "; - value->getIsolationRegionInfo().printForDiagnostics(llvm::dbgs()); + value.getIsolationRegionInfo().printForDiagnostics(llvm::dbgs()); llvm::dbgs() << "\n"); builder.addUnknownPatternError(src); continue; @@ -1984,7 +2225,12 @@ public: } } - for (SILValue result : resultValues) { + // Merge all srcs. + for (unsigned i = 1; i < assignOperands.size(); i++) { + builder.addMerge(assignOperands[i - 1].second, assignOperands[i].first); + } + + for (SILValue result : directResultValues) { // If we had isolation info explicitly passed in... use our // resultIsolationInfoError. Otherwise, we want to infer. if (resultIsolationInfoOverride) { @@ -1999,29 +2245,15 @@ public: nonSendableValue->first.getRepresentative().getValue()); } } else { - if (auto value = tryToTrackValue(result)) { - assignResults.push_back(value->getRepresentative().getValue()); + if (auto lookupResult = tryToTrackValue(result)) { + if (lookupResult->value.isSendable()) + continue; + auto value = lookupResult->value; + assignResults.push_back(value.getRepresentative().getValue()); } } } - // Require all srcs if we are supposed to. (By default we do). - // - // DISCUSSION: The reason that this may be useful is for special - // instructions like store_borrow. On the one hand, we want store_borrow to - // act like a store in the sense that we want to combine the regions of its - // src and dest... but at the same time, we do not want to treat the store - // itself as a use of its parent value. We want that to be any subsequent - // uses of the store_borrow. - if (requireSrcValues) - for (auto src : assignOperands) - builder.addRequire(src.second); - - // Merge all srcs. - for (unsigned i = 1; i < assignOperands.size(); i++) { - builder.addMerge(assignOperands[i - 1].second, assignOperands[i].first); - } - // If we do not have any non sendable results, return early. if (assignResults.empty()) { // If we did not have any non-Sendable results and we did have @@ -2034,9 +2266,9 @@ public: // passed in a specific isolation info unlike earlier when processing // actual results. if (assignOperands.size() && resultIsolationInfoOverride) { - builder.addActorIntroducingInst(assignOperands.back().second, - assignOperands.back().first, - resultIsolationInfoOverride); + builder.addActorIntroducingInst( + assignOperands.back().second.getRepresentative().getValue(), + assignOperands.back().first, resultIsolationInfoOverride); } return; @@ -2072,7 +2304,8 @@ public: // Just track the result of the builtin inst as an assign fresh. We do this // so we properly track the partial_apply get. We already sent the // parameters. - builder.addAssignFresh(bi); + if (auto lookupResult = tryToTrackValue(bi)) + builder.addAssignFresh(lookupResult->value); } /// For discussion on how we handle async let, please see the comment on @@ -2097,12 +2330,23 @@ public: return; ApplySite applySite(pai); + // For each of our partial apply operands... for (auto pair : llvm::enumerate(applySite.getArgumentOperands())) { Operand &op = pair.value(); // If we are tracking the value... - if (auto trackedArgValue = tryToTrackValue(op.get())) { + if (auto lookupResult = tryToTrackValue(op.get())) { + auto trackedArgValue = lookupResult->value; + + // If we see that our arg value was Sendable, continue. We do not need + // to consider a base here since when we capture variables, we always + // capture them completely by reference meaning we will actually capture + // the base. + if (trackedArgValue.isSendable()) { + assert(!lookupResult->base.has_value() && "Should never have a base"); + continue; + } // Gather the isolation info from the AST for this operand... InferredCallerArgumentTypeInfo typeInfo; @@ -2123,8 +2367,7 @@ public: })) continue; - builder.addUndoSend(trackedArgValue->getRepresentative().getValue(), - ai); + builder.addUndoSend(trackedArgValue.getRepresentative().getValue(), ai); } } } @@ -2143,14 +2386,15 @@ public: // _ = await y // useValue(x2) for (auto &op : ApplySite(pai).getArgumentOperands()) { - if (auto trackedArgValue = tryToTrackValue(op.get())) { - builder.addRequire(trackedArgValue->getRepresentative().getValue()); - builder.addSend(trackedArgValue->getRepresentative().getValue(), &op); + if (auto lookupResult = tryToTrackValue(op.get())) { + builder.addRequire(*lookupResult); + builder.addSend(lookupResult->value, &op); } } // Then mark our partial_apply result as being returned fresh. - builder.addAssignFresh(pai); + if (auto lookupResult = tryToTrackValue(pai)) + builder.addAssignFresh(lookupResult->value); } /// Handles the semantics for SIL applies that cross isolation. @@ -2165,11 +2409,11 @@ public: // For each argument operand. for (auto &op : applySite.getArgumentOperands()) { // See if we tracked it. - if (auto value = tryToTrackValue(op.get())) { + if (auto lookupResult = tryToTrackValue(op.get())) { // If we are tracking it, sent it and if it is actor derived, mark // our partial apply as actor derived. - builder.addRequire(value->getRepresentative().getValue()); - builder.addSend(value->getRepresentative().getValue(), &op); + builder.addRequire(*lookupResult); + builder.addSend(lookupResult->value, &op); } } @@ -2231,10 +2475,9 @@ public: } void translateCreateAsyncTask(BuiltinInst *bi) { - if (auto value = tryToTrackValue(bi->getOperand(1))) { - builder.addRequire(value->getRepresentative().getValue()); - builder.addSend(value->getRepresentative().getValue(), - &bi->getAllOperands()[1]); + if (auto lookupResult = tryToTrackValue(bi->getOperand(1))) { + builder.addRequire(*lookupResult); + builder.addSend(lookupResult->value, &bi->getAllOperands()[1]); } } @@ -2274,10 +2517,12 @@ public: continue; } - if (auto value = tryToTrackValue(op.get())) { - builder.addRequire(value->getRepresentative().getValue()); - builder.addSend(value->getRepresentative().getValue(), &op); - continue; + // Attempt to lookup the value we are passing as sending. We want to + // require/send value if it is non-Sendable and require its base if it + // is non-Sendable as well. + if (auto lookupResult = tryToTrackValue(op.get())) { + builder.addRequire(*lookupResult); + builder.addSend(lookupResult->value, &op); } } else { nonSendingParameters.push_back(&op); @@ -2292,9 +2537,9 @@ public: auto &selfOperand = fas.getSelfArgumentOperand(); if (fas.getArgumentParameterInfo(selfOperand) .hasOption(SILParameterInfo::Sending)) { - if (auto value = tryToTrackValue(selfOperand.get())) { - builder.addRequire(value->getRepresentative().getValue()); - builder.addSend(value->getRepresentative().getValue(), &selfOperand); + if (auto lookupResult = tryToTrackValue(selfOperand.get())) { + builder.addRequire(*lookupResult); + builder.addSend(lookupResult->value, &selfOperand); } } else { nonSendingParameters.push_back(&selfOperand); @@ -2308,8 +2553,8 @@ public: // at the point of application. Otherwise, we will not emit errors if the // closure before this function application is already in the same region as // a sent value. In such a case, the function application must error. - if (auto value = tryToTrackValue(fas.getCallee())) { - builder.addRequire(value->getRepresentative().getValue()); + if (auto calleeResult = tryToTrackValue(fas.getCallee())) { + builder.addRequire(*calleeResult); } SmallVector applyResults; @@ -2331,15 +2576,15 @@ public: // Sending direct results. for (SILValue result : applyResults) { - if (auto value = tryToTrackValue(result)) { - builder.addAssignFresh(value->getRepresentative().getValue()); + if (auto lookupResult = tryToTrackValue(result)) { + builder.addAssignFresh(lookupResult->value); } } // Sending indirect results. for (Operand *op : sendingIndirectResults) { - if (auto value = tryToTrackValue(op->get())) { - builder.addAssignFresh(value->getRepresentative().getValue()); + if (auto lookupResult = tryToTrackValue(op->get())) { + builder.addAssignFresh(lookupResult->value); } } } @@ -2382,21 +2627,23 @@ public: /// Semantically this causes all arguments of the applysite to be sent. void translateIsolationCrossingSILApply(FullApplySite applySite) { // Require all operands first before we emit a send. - for (auto op : applySite.getArguments()) - if (auto value = tryToTrackValue(op)) - builder.addRequire(value->getRepresentative().getValue()); + for (auto op : applySite.getArguments()) { + if (auto lookupResult = tryToTrackValue(op)) { + builder.addRequire(*lookupResult); + } + } auto handleSILOperands = [&](MutableArrayRef operands) { for (auto &op : operands) { - if (auto value = tryToTrackValue(op.get())) { - builder.addSend(value->getRepresentative().getValue(), &op); + if (auto lookupResult = tryToTrackValue(op.get())) { + builder.addSend(lookupResult->value, &op); } } }; auto handleSILSelf = [&](Operand *self) { - if (auto value = tryToTrackValue(self->get())) { - builder.addSend(value->getRepresentative().getValue(), self); + if (auto lookupResult = tryToTrackValue(self->get())) { + builder.addSend(lookupResult->value, self); } }; @@ -2440,11 +2687,11 @@ public: .isNonisolated()); for (auto result : applyResults) { - if (auto value = tryToTrackValue(result)) { - builder.addAssignFresh(value->getRepresentative().getValue()); + if (auto lookupResult = tryToTrackValue(result)) { + builder.addAssignFresh(lookupResult->value); if (emitIsolationCrossingResultError) builder.addNonSendableIsolationCrossingResultError( - value->getRepresentative().getValue()); + lookupResult->value); } } @@ -2453,9 +2700,9 @@ public: // non-Sendable. if (emitIsolationCrossingResultError) { for (auto result : applySite.getIndirectSILResults()) { - if (auto value = tryToTrackValue(result)) { + if (auto lookupResult = tryToTrackValue(result)) { builder.addNonSendableIsolationCrossingResultError( - value->getRepresentative().getValue()); + lookupResult->value); } } } @@ -2463,12 +2710,22 @@ public: template void translateSILLookThrough(DestValues destValues, SILValue src) { - auto srcID = tryToTrackValue(src); + auto srcResult = tryToTrackValue(src); for (SILValue dest : destValues) { - auto destID = tryToTrackValue(dest); - assert(((!destID || !srcID) || destID->getID() == srcID->getID()) && - "srcID and dstID are different?!"); + // If we have a non-Sendable result and a Sendable src, we assign fresh. + if (auto destResult = tryToTrackValue(dest)) { + if (!srcResult || srcResult->value.isSendable()) { + builder.addAssignFresh(destResult->value); + continue; + } + + // Otherwise, we do a real look through. + assert(((!destResult || !srcResult || destResult->value.isSendable() || + srcResult->value.isSendable()) || + destResult->value.getID() == srcResult->value.getID()) && + "srcID and dstID are different?!"); + } } } @@ -2483,18 +2740,37 @@ public: /// PartitionOp. Doing such a thing obscures what is actually happening. template <> void translateSILLookThrough(SILValue dest, SILValue src) { - auto srcID = tryToTrackValue(src); - auto destID = tryToTrackValue(dest); - assert(((!destID || !srcID) || destID->getID() == srcID->getID()) && - "srcID and dstID are different?!"); + auto srcResult = tryToTrackValue(src); + + if (auto destResult = tryToTrackValue(dest)) { + if (!srcResult || srcResult->value.isSendable()) { + builder.addAssignFresh(destResult->value); + return; + } + + // Otherwise, we do a real look through. + assert(((!srcResult || destResult->value.isSendable() || + srcResult->value.isSendable()) || + destResult->value.getID() == srcResult->value.getID()) && + "srcID and dstID are different?!"); + } } void translateSILLookThrough(SingleValueInstruction *svi) { assert(svi->getNumRealOperands() == 1); - auto srcID = tryToTrackValue(svi->getOperand(0)); - auto destID = tryToTrackValue(svi); - assert(((!destID || !srcID) || destID->getID() == srcID->getID()) && - "srcID and dstID are different?!"); + auto srcResult = tryToTrackValue(svi->getOperand(0)); + if (auto destResult = tryToTrackValue(svi)) { + if (!srcResult || srcResult->value.isSendable()) { + builder.addAssignFresh(destResult->value); + return; + } + + // Otherwise, we do a real look through. + assert(((!srcResult || destResult->value.isSendable() || + srcResult->value.isSendable()) || + destResult->value.getID() == srcResult->value.getID()) && + "srcID and dstID are different?!"); + } } template @@ -2536,16 +2812,24 @@ public: template void translateSILMerge(SILValue dest, Collection srcCollection, bool requireOperands = true) { - auto trackableDest = tryToTrackValue(dest); - if (!trackableDest) + auto destResult = tryToTrackValue(dest); + if (!destResult) return; + + if (requireOperands) { + builder.addRequire(*destResult); + } + for (Operand *op : srcCollection) { - if (auto trackableSrc = tryToTrackValue(op->get())) { + // If we have a trackable src, we need to require both if asked to and + // then merge the dest/src. + if (auto srcResult = tryToTrackValue(op->get())) { if (requireOperands) { - builder.addRequire(trackableSrc->getRepresentative().getValue()); - builder.addRequire(trackableDest->getRepresentative().getValue()); + builder.addRequire(*srcResult); } - builder.addMerge(trackableDest->getRepresentative().getValue(), op); + + if (srcResult->value.isNonSendable()) + builder.addMerge(destResult->value, op); } } } @@ -2562,47 +2846,26 @@ public: if (array.size() < 2) return; - auto trackableDest = tryToTrackValue(array.front().get()); - if (!trackableDest) + auto destResult = tryToTrackValue(array.front().get()); + if (!destResult) return; + + if (requireOperands) { + builder.addRequire(*destResult); + } + for (Operand &op : array.drop_front()) { - if (auto trackableSrc = tryToTrackValue(op.get())) { + if (auto srcResult = tryToTrackValue(op.get())) { if (requireOperands) { - builder.addRequire(trackableSrc->getRepresentative().getValue()); - builder.addRequire(trackableDest->getRepresentative().getValue()); + builder.addRequire(*srcResult); } - builder.addMerge(trackableDest->getRepresentative().getValue(), &op); + + if (srcResult->value.isNonSendable()) + builder.addMerge(destResult->value, &op); } } } - void translateSILAssignmentToSendingParameter(TrackableValue destRoot, - Operand *destOperand, - TrackableValue srcRoot, - Operand *srcOperand) { - assert(isa(destRoot.getRepresentative().getValue()) && - "Destination should always be an alloc_stack"); - - // Send src. This ensures that we cannot use src again locally in this - // function... which makes sense since its value is now in the 'sending' - // parameter. - builder.addRequire(srcRoot.getRepresentative().getValue()); - builder.addSend(srcRoot.getRepresentative().getValue(), srcOperand); - - // Then check if we are assigning into an aggregate projection. In such a - // case, we want to ensure that we keep tracking the elements already in the - // region of sending. This is more conservative than we need to be - // (since we could forget anything reachable from the aggregate - // field)... but being more conservative is ok. - if (isProjectedFromAggregate(destOperand->get())) - return; - - // If we are assigning over the entire value though, we perform an assign - // fresh since we are guaranteed that any value that could be referenced via - // the old value is gone. - builder.addAssignFresh(destRoot.getRepresentative().getValue()); - } - /// If \p dest is known to be unaliased (computed through a combination of /// AccessStorage's inUniquelyIdenfitied check and a custom search for /// captures by applications), then these can be treated as assignments of \p @@ -2611,7 +2874,7 @@ public: void translateSILStore(Operand *dest, Operand *src) { SILValue destValue = dest->get(); - if (auto nonSendableDest = tryToTrackValue(destValue)) { + if (auto destResult = tryToTrackValue(destValue)) { // In the following situations, we can perform an assign: // // 1. A store to unaliased storage. @@ -2623,7 +2886,10 @@ public: // specifically in this projection... but that is better than // miscompiling. For memory like this, we probably need to track it on a // per field basis to allow for us to assign. - if (nonSendableDest.value().isNoAlias() && + // + // TODO: Should this change if we have a Sendable address with a + // non-Sendable base. + if (destResult.value().value.isNoAlias() && !isProjectedFromAggregate(destValue)) return translateSILAssign(destValue, src); @@ -2648,7 +2914,11 @@ public: // specifically in this projection... but that is better than // miscompiling. For memory like this, we probably need to track it on a // per field basis to allow for us to assign. - if (nonSendableTgt.value().isNoAlias() && !isProjectedFromAggregate(dest)) + // + // TODO: Should this change if we have a Sendable address with a + // non-Sendable base. + if (nonSendableTgt.value().value.isNoAlias() && + !isProjectedFromAggregate(dest)) return translateSILAssign( dest, makeOperandRefRange(inst->getElementOperands())); @@ -2661,8 +2931,9 @@ public: } void translateSILRequire(SILValue val) { - if (auto nonSendableVal = tryToTrackValue(val)) - return builder.addRequire(nonSendableVal->getRepresentative().getValue()); + if (auto lookupResult = tryToTrackValue(val)) { + builder.addRequire(*lookupResult); + } } /// An enum select is just a multi assign. @@ -2709,9 +2980,9 @@ public: /// unconditionally and that do not have a result. void translateSILSendingNoResult(MutableArrayRef values) { for (auto &op : values) { - if (auto ns = tryToTrackValue(op.get())) { - builder.addRequire(ns->getRepresentative().getValue()); - builder.addSend(ns->getRepresentative().getValue(), &op); + if (auto lookupResult = tryToTrackValue(op.get())) { + builder.addRequire(*lookupResult); + builder.addSend(lookupResult->value, &op); } } } @@ -2752,9 +3023,8 @@ public: auto *fArg = cast(arg); if (fArg->getArgumentConvention().isInoutConvention() && fArg->getKnownParameterInfo().hasOption(SILParameterInfo::Sending)) { - if (auto ns = tryToTrackValue(arg)) { - auto rep = ns->getRepresentative().getValue(); - builder.addInOutSendingAtFunctionExit(rep); + if (auto lookupResult = tryToTrackValue(arg)) { + builder.addInOutSendingAtFunctionExit(lookupResult->value); } } } @@ -2797,8 +3067,6 @@ public: case TranslationSemantics::LookThrough: assert(inst->getNumRealOperands() == 1); assert((isStaticallyLookThroughInst(inst) || - isLookThroughIfResultNonSendable(inst) || - isLookThroughIfOperandNonSendable(inst) || isLookThroughIfOperandAndResultNonSendable(inst)) && "Out of sync... should return true for one of these categories!"); return translateSILLookThrough(inst->getResults(), inst->getOperand(0)); @@ -2860,7 +3128,7 @@ Element PartitionOpBuilder::getActorIntroducingRepresentative( bool PartitionOpBuilder::valueHasID(SILValue value, bool dumpIfHasNoID) { auto v = translator->valueMap.getTrackableValue(value); - if (auto m = v.getRepresentative().maybeGetValue()) + if (auto m = v.value.getRepresentative().maybeGetValue()) return translator->valueHasID(m, dumpIfHasNoID); return true; } @@ -2898,10 +3166,7 @@ void PartitionOpBuilder::print(llvm::raw_ostream &os) const { SWIFT_DEFER { opsToPrint.clear(); }; for (const PartitionOp &op : ops) { // Now dump our the root value we map. - for (unsigned opArg : op.getOpArgs()) { - // If we didn't insert, skip this. We only emit this once. - opsToPrint.push_back(Element(opArg)); - } + op.getOpArgs(opsToPrint); } sortUnique(opsToPrint); for (Element opArg : opsToPrint) { @@ -2919,6 +3184,50 @@ void PartitionOpBuilder::print(llvm::raw_ostream &os) const { #endif } +void PartitionOpBuilder::addRequire(TrackableValue value, + PartitionOp::Options options) { + if (value.isSendable()) + return; + auto silValue = value.getRepresentative().getValue(); + assert(valueHasID(silValue, /*dumpIfHasNoID=*/true) && + "required value should already have been encountered"); + + // Check if this value + + currentInstPartitionOps.emplace_back( + PartitionOp::Require(lookupValueID(silValue), currentInst, options)); +} + +void PartitionOpBuilder::addRequire(TrackableValueLookupResult value) { + // Call addRequire which will short curcuit if we have a sendable value. + addRequire(value.value); + + if (value.value.isSendable()) { + auto options = PartitionOp::Options(); + if (value.base && value.base->isNonSendable()) { + // Check if our base was captured by a closure. In such a case, we need to + // treat this as a use of the base. + auto rep = value.base->getRepresentative().getValue(); + if (auto boxType = rep->getType().getAs()) { + // If we are not mutable, we are always safe to access the Sendable + // value. The reason why is that we will have copied the non-Sendable + // segment by value at +1 when we send it implying that we will + if (!boxType->getLayout()->isMutable()) + return; + + options |= PartitionOp::Flag::RequireOfMutableBaseOfSendableValue; + } else if (auto *asi = dyn_cast(rep)) { + if (asi->isLet()) + return; + + options |= PartitionOp::Flag::RequireOfMutableBaseOfSendableValue; + } + + addRequire(value.base, options); + } + } +} + //===----------------------------------------------------------------------===// // MARK: Translator - Instruction To Op Kind //===----------------------------------------------------------------------===// @@ -3000,7 +3309,6 @@ CONSTANT_TRANSLATION(TailAddrInst, Assign) CONSTANT_TRANSLATION(ThickToObjCMetatypeInst, Assign) CONSTANT_TRANSLATION(ThinToThickFunctionInst, Assign) CONSTANT_TRANSLATION(UncheckedAddrCastInst, Assign) -CONSTANT_TRANSLATION(UncheckedEnumDataInst, Assign) CONSTANT_TRANSLATION(UncheckedOwnershipConversionInst, Assign) CONSTANT_TRANSLATION(IndexRawPointerInst, Assign) CONSTANT_TRANSLATION(MarkDependenceAddrInst, Assign) @@ -3063,6 +3371,10 @@ CONSTANT_TRANSLATION(StrongCopyUnmanagedValueInst, LookThrough) CONSTANT_TRANSLATION(RefToUnmanagedInst, LookThrough) CONSTANT_TRANSLATION(UnmanagedToRefInst, LookThrough) CONSTANT_TRANSLATION(InitExistentialValueInst, LookThrough) +CONSTANT_TRANSLATION(UncheckedEnumDataInst, LookThrough) +CONSTANT_TRANSLATION(TupleElementAddrInst, LookThrough) +CONSTANT_TRANSLATION(StructElementAddrInst, LookThrough) +CONSTANT_TRANSLATION(UncheckedTakeEnumDataAddrInst, LookThrough) //===--- // Store @@ -3312,9 +3624,6 @@ IGNORE_IF_SENDABLE_RESULT_ASSIGN_OTHERWISE(StructExtractInst) LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedTrivialBitCastInst) LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedBitwiseCastInst) LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedValueCastInst) -LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(TupleElementAddrInst) -LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(StructElementAddrInst) -LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedTakeEnumDataAddrInst) LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(ConvertEscapeToNoEscapeInst) LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(ConvertFunctionInst) @@ -3335,7 +3644,7 @@ PartitionOpTranslator::visitStoreBorrowInst(StoreBorrowInst *sbi) { SILValue destValue = sbi->getDest(); auto nonSendableDest = tryToTrackValue(destValue); - if (!nonSendableDest) + if (!nonSendableDest || nonSendableDest->value.isSendable()) return TranslationSemantics::Ignored; // In the following situations, we can perform an assign: @@ -3349,7 +3658,7 @@ PartitionOpTranslator::visitStoreBorrowInst(StoreBorrowInst *sbi) { // specifically in this projection... but that is better than // miscompiling. For memory like this, we probably need to track it on a // per field basis to allow for us to assign. - if (nonSendableDest.value().isNoAlias() && + if (nonSendableDest.value().value.isNoAlias() && !isProjectedFromAggregate(destValue)) { translateSILMultiAssign(sbi->getResults(), makeOperandRefRange(sbi->getAllOperands()), @@ -3409,20 +3718,42 @@ PartitionOpTranslator::visitBeginBorrowInst(BeginBorrowInst *bbi) { return TranslationSemantics::LookThrough; } -/// LoadInst is technically a statically look through instruction, but we want -/// to handle it especially in the infrastructure, so we cannot mark it as -/// such. This makes marking it as a normal lookthrough instruction impossible -/// since the routine checks that invariant. -TranslationSemantics PartitionOpTranslator::visitLoadInst(LoadInst *limvi) { +/// LoadInst has two different semantics: +/// +/// 1. If the load produces a non-Sendable value, we want to treat it as a +/// statically look through instruction, but we want to handle it especially in +/// the infrastructure, so we cannot mark it as such. This makes marking it as a +/// normal lookthrough instruction impossible since the routine checks that +/// invariant. +/// +/// 2. If the load produces a Sendable value, we want to perform a require on +/// the address so that if we have a load from a non-Sendable base, we properly +/// require the base. +TranslationSemantics PartitionOpTranslator::visitLoadInst(LoadInst *li) { + if (SILIsolationInfo::isSendableType(li->getOperand())) { + translateSILRequire(li->getOperand()); + } + return TranslationSemantics::Special; } -/// LoadBorrowInst is technically a statically look through instruction, but we -/// want to handle it especially in the infrastructure, so we cannot mark it as -/// such. This makes marking it as a normal lookthrough instruction impossible -/// since the routine checks that invariant. +/// LoadBorrowInst has two different semantics: +/// +/// 1. If the load produces a non-Sendable value, we want to treat it as a +/// statically look through instruction, but we want to handle it especially in +/// the infrastructure, so we cannot mark it as such. This makes marking it as a +/// normal lookthrough instruction impossible since the routine checks that +/// invariant. +/// +/// 2. If the load produces a Sendable value, we want to perform a require on +/// the address so that if we have a load from a non-Sendable base, we properly +/// require the base. TranslationSemantics PartitionOpTranslator::visitLoadBorrowInst(LoadBorrowInst *lbi) { + if (SILIsolationInfo::isSendableType(lbi->getOperand())) { + translateSILRequire(lbi->getOperand()); + } + return TranslationSemantics::Special; } @@ -3483,8 +3814,11 @@ PartitionOpTranslator::visitPackElementSetInst(PackElementSetInst *r) { TranslationSemantics PartitionOpTranslator::visitRawPointerToRefInst(RawPointerToRefInst *r) { - assert(isLookThroughIfResultNonSendable(r) && "Out of sync"); + assert(isLookThroughIfOperandAndResultNonSendable(r) && "Out of sync"); // If our result is non sendable, perform a look through. + // + // NOTE: From RBI perspective, RawPointer is non-Sendable, so this is really + // just look through if operand and result non-Sendable. if (isNonSendableType(r->getType())) return TranslationSemantics::LookThrough; @@ -3494,9 +3828,12 @@ PartitionOpTranslator::visitRawPointerToRefInst(RawPointerToRefInst *r) { TranslationSemantics PartitionOpTranslator::visitRefToRawPointerInst(RefToRawPointerInst *r) { - assert(isLookThroughIfOperandNonSendable(r) && "Out of sync"); + assert(isLookThroughIfOperandAndResultNonSendable(r) && "Out of sync"); // If our source ref is non sendable, perform a look through. + // + // NOTE: From RBI perspective, RawPointer is non-Sendable, so this is really + // just look through if operand and result non-Sendable. if (isNonSendableType(r->getOperand()->getType())) return TranslationSemantics::LookThrough; @@ -3677,13 +4014,13 @@ bool BlockPartitionState::recomputeExitFromEntry( } std::optional getElement(SILValue value) const { - return translator.getValueMap().getTrackableValue(value).getID(); + return translator.getValueMap().getTrackableValue(value).value.getID(); } SILValue getRepresentative(SILValue value) const { return translator.getValueMap() .getTrackableValue(value) - .getRepresentative() + .value.getRepresentative() .maybeGetValue(); } @@ -3898,3 +4235,25 @@ void RegionAnalysis::initialize(SILPassManager *pm) { SILAnalysis *swift::createRegionAnalysis(SILModule *) { return new RegionAnalysis(); } + +//===----------------------------------------------------------------------===// +// MARK: Tests +//===----------------------------------------------------------------------===// + +namespace swift::test { + +// Arguments: +// - SILValue: value to look up isolation for. +// Dumps: +// - The inferred isolation. +static FunctionTest + UnderlyingTrackedValue("sil_regionanalysis_underlying_tracked_value", + [](auto &function, auto &arguments, auto &test) { + RegionAnalysisValueMap valueMap(&function); + auto value = arguments.takeValue(); + auto trackableValue = valueMap.getTrackableValue(value); + trackableValue.print(llvm::outs()); + llvm::outs() << '\n'; + }); + +} // namespace swift::test diff --git a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp index ea70fe42e65..9cfdd4cad9c 100644 --- a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp @@ -2095,6 +2095,23 @@ PullbackCloner::PullbackCloner(VJPCloner &vjpCloner) PullbackCloner::~PullbackCloner() { delete &impl; } +static SILValue getArrayValue(ApplyInst *ai) { + SILValue arrayValue; + for (auto use : ai->getUses()) { + auto *dti = dyn_cast(use->getUser()); + if (!dti) + continue; + DEBUG_ASSERT(!arrayValue && "Array value already found"); + // The first `destructure_tuple` result is the `Array` value. + arrayValue = dti->getResult(0); +#ifndef DEBUG_ASSERT_enabled + break; +#endif + } + ASSERT(arrayValue); + return arrayValue; +} + //--------------------------------------------------------------------------// // Entry point //--------------------------------------------------------------------------// @@ -2439,6 +2456,134 @@ bool PullbackCloner::Implementation::run() { // Visit original blocks in post-order and perform differentiation // in corresponding pullback blocks. If errors occurred, back out. else { + LLVM_DEBUG(getADDebugStream() + << "Begin search for adjoints of loop-local active values\n"); + llvm::DenseMap> + loopLocalActiveValues; + for (auto *bb : originalBlocks) { + const SILLoop *loop = vjpCloner.getLoopInfo()->getLoopFor(bb); + if (loop == nullptr) + continue; + SILBasicBlock *loopHeader = loop->getHeader(); + SILBasicBlock *pbLoopHeader = getPullbackBlock(loopHeader); + LLVM_DEBUG(getADDebugStream() + << "Original bb" << bb->getDebugID() + << " belongs to a loop, original header bb" + << loopHeader->getDebugID() << ", pullback header bb" + << pbLoopHeader->getDebugID() << '\n'); + builder.setInsertionPoint(pbLoopHeader); + auto bbActiveValuesIt = activeValues.find(bb); + if (bbActiveValuesIt == activeValues.end()) + continue; + const auto &bbActiveValues = bbActiveValuesIt->second; + for (SILValue bbActiveValue : bbActiveValues) { + if (vjpCloner.getLoopInfo()->getLoopFor( + bbActiveValue->getParentBlock()) != loop) { + LLVM_DEBUG( + getADDebugStream() + << "The following active value is NOT loop-local, skipping: " + << bbActiveValue); + continue; + } + + auto [_, wasInserted] = + loopLocalActiveValues[loop].insert(bbActiveValue); + LLVM_DEBUG(getADDebugStream() + << "The following active value is loop-local, "); + if (!wasInserted) { + LLVM_DEBUG(llvm::dbgs() << "but it was already processed, skipping: " + << bbActiveValue); + continue; + } + + if (getTangentValueCategory(bbActiveValue) == + SILValueCategory::Object) { + LLVM_DEBUG(llvm::dbgs() + << "zeroing its adjoint value in loop header: " + << bbActiveValue); + setAdjointValue(bb, bbActiveValue, + makeZeroAdjointValue(getRemappedTangentType( + bbActiveValue->getType()))); + continue; + } + + ASSERT(getTangentValueCategory(bbActiveValue) == + SILValueCategory::Address); + + // getAdjointProjection might call materializeAdjointDirect which + // writes to debug output, emit \n. + LLVM_DEBUG(llvm::dbgs() + << "checking if it's adjoint is a projection\n"); + + if (!getAdjointProjection(bb, bbActiveValue)) { + LLVM_DEBUG(getADDebugStream() + << "Adjoint for the following value is NOT a projection, " + "zeroing its adjoint buffer in loop header: " + << bbActiveValue); + + // All adjoint buffers are allocated in the pullback entry and + // deallocated in the pullback exit. So, use IsNotInitialization to + // emit destroy_addr before zeroing the buffer. + ASSERT(bufferMap.contains({bb, bbActiveValue})); + builder.emitZeroIntoBuffer(pbLoc, getAdjointBuffer(bb, bbActiveValue), + IsNotInitialization); + + continue; + } + + LLVM_DEBUG(getADDebugStream() + << "Adjoint for the following value is a projection, "); + + // If Projection::isAddressProjection(v) is true for a value v, it + // is not added to active values list (see recordValueIfActive). + // + // Ensure that only the following value types conforming to + // getAdjointProjection but not conforming to + // Projection::isAddressProjection can go here. + // + // Instructions conforming to Projection::isAddressProjection and + // thus never corresponding to an active value do not need any + // handling, because only active values can have adjoints from + // previous iterations propagated via BB arguments. + do { + // Consider '%X = begin_access [modify] [static] %Y'. + // 1. If %Y is loop-local, it's adjoint buffer will + // be zeroed, and we'll have zero adjoint projection to it. + // 2. Otherwise, we do not need to zero the projection buffer. + // Thus, we can just skip. + if (dyn_cast(bbActiveValue)) { + LLVM_DEBUG(llvm::dbgs() << "skipping: " << bbActiveValue); + break; + } + + // Consider the following sequence: + // %1 = function_ref @allocUninitArray + // %2 = apply %1(%0) + // (%3, %4) = destructure_tuple %2 + // %5 = mark_dependence %4 on %3 + // %6 = pointer_to_address %6 to [strict] $*Float + // Since %6 is active, %3 (which is an array) must also be active. + // Thus, adjoint for %3 will be zeroed if needed. Ensure that expected + // invariants hold and then skip. + if (auto *ai = getAllocateUninitializedArrayIntrinsicElementAddress( + bbActiveValue)) { + ASSERT(isa(bbActiveValue)); + SILValue arrayValue = getArrayValue(ai); + ASSERT(llvm::find(bbActiveValues, arrayValue) != + bbActiveValues.end()); + ASSERT(vjpCloner.getLoopInfo()->getLoopFor( + arrayValue->getParentBlock()) == loop); + LLVM_DEBUG(llvm::dbgs() << "skipping: " << bbActiveValue); + break; + } + + ASSERT(false); + } while (false); + } + } + LLVM_DEBUG(getADDebugStream() + << "End search for adjoints of loop-local active values\n"); + for (auto *bb : originalBlocks) { visitSILBasicBlock(bb); if (errorOccurred) @@ -3339,19 +3484,9 @@ SILValue PullbackCloner::Implementation::getAdjointProjection( eltIndex = ili->getValue().getLimitedValue(); } // Get the array adjoint value. - SILValue arrayAdjoint; - assert(ai && "Expected `array.uninitialized_intrinsic` application"); - for (auto use : ai->getUses()) { - auto *dti = dyn_cast(use->getUser()); - if (!dti) - continue; - assert(!arrayAdjoint && "Array adjoint already found"); - // The first `destructure_tuple` result is the `Array` value. - auto arrayValue = dti->getResult(0); - arrayAdjoint = materializeAdjointDirect( - getAdjointValue(origBB, arrayValue), definingInst->getLoc()); - } - assert(arrayAdjoint && "Array does not have adjoint value"); + SILValue arrayValue = getArrayValue(ai); + SILValue arrayAdjoint = materializeAdjointDirect( + getAdjointValue(origBB, arrayValue), definingInst->getLoc()); // Apply `Array.TangentVector.subscript` to get array element adjoint value. auto *eltAdjBuffer = getArrayAdjointElementBuffer(arrayAdjoint, eltIndex, ai->getLoc()); diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 69cd337c2a6..ca6b3e2fbe8 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -938,16 +938,6 @@ static void substituteConstants(FoldState &foldState) { for (SILValue constantSILValue : foldState.getConstantSILValues()) { SymbolicValue constantSymbolicVal = evaluator.lookupConstValue(constantSILValue).value(); - CanType instType = constantSILValue->getType().getASTType(); - - // If the SymbolicValue is a string but the instruction that is folded is - // not String typed, we are tracking a StaticString which is represented as - // a raw pointer. Skip folding StaticString as they are already efficiently - // represented. - if (constantSymbolicVal.getKind() == SymbolicValue::String && - !instType->isString()) - continue; - // Make sure that the symbolic value tracked in the foldState is a constant. // In the case of ArraySymbolicValue, the array storage could be a non-constant // if some instruction in the array initialization sequence was not evaluated @@ -986,6 +976,7 @@ static void substituteConstants(FoldState &foldState) { SILBuilderWithScope builder(insertionPoint); SILLocation loc = insertionPoint->getLoc(); + CanType instType = constantSILValue->getType().getASTType(); SILValue foldedSILVal = emitCodeForSymbolicValue( constantSymbolicVal, instType, builder, loc, foldState.stringInfo); diff --git a/lib/SILOptimizer/Mandatory/SendNonSendable.cpp b/lib/SILOptimizer/Mandatory/SendNonSendable.cpp index e8ac44c7ca7..9fe400c8f20 100644 --- a/lib/SILOptimizer/Mandatory/SendNonSendable.cpp +++ b/lib/SILOptimizer/Mandatory/SendNonSendable.cpp @@ -1743,7 +1743,7 @@ private: getIsolatedValuePartialApplyIndex(PartialApplyInst *pai, SILValue isolatedValue) { for (auto &paiOp : ApplySite(pai).getArgumentOperands()) { - if (valueMap.getTrackableValue(paiOp.get()).getRepresentative() == + if (valueMap.getTrackableValue(paiOp.get()).value.getRepresentative() == isolatedValue) { return ApplySite(pai).getASTAppliedArgIndex(paiOp); } @@ -1827,13 +1827,13 @@ bool SentNeverSendableDiagnosticInferrer::initForSendingPartialApply( // If our value's rep is task isolated or is the dynamic isolated // value... then we are done. This is a 'correct' error value to emit. auto trackableValue = valueMap.getTrackableValue(sendingPAIOp.get()); - if (trackableValue.isSendable()) + if (trackableValue.value.isSendable()) continue; - auto rep = trackableValue.getRepresentative().maybeGetValue(); + auto rep = trackableValue.value.getRepresentative().maybeGetValue(); nonSendableOps.push_back(&sendingPAIOp); - if (trackableValue.getIsolationRegionInfo().isTaskIsolated() || + if (trackableValue.value.getIsolationRegionInfo().isTaskIsolated() || rep == maybeIsolatedValue) { if (auto capturedValue = findClosureUse(&sendingPAIOp)) { diagnosticEmitter.emitSendingClosureParamDirectlyIsolated( @@ -2692,24 +2692,15 @@ struct DiagnosticEvaluator final void handleLocalUseAfterSend(LocalUseAfterSendError error) const { const auto &partitionOp = *error.op; - - auto &operandState = operandToStateMap.get(error.sendingOp); - // Ignore this if we have a gep like instruction that is returning a - // sendable type and sendingOp was not set with closure - // capture. - if (auto *svi = - dyn_cast(partitionOp.getSourceInst())) { - if (isa(svi) && - !SILIsolationInfo::isNonSendableType(svi->getType(), - svi->getFunction())) { - bool isCapture = operandState.isClosureCaptured; - if (!isCapture) { - return; - } - } - } - REGIONBASEDISOLATION_LOG(error.print(llvm::dbgs(), info->getValueMap())); + + // Ignore this if we are erroring on a mutable base of a Sendable value and + // if when we sent the value's region was not closure captured. + if (error.op->getOptions().containsOnly( + PartitionOp::Flag::RequireOfMutableBaseOfSendableValue) && + !operandToStateMap.get(error.sendingOp).isClosureCaptured) + return; + sendingOpToRequireInstMultiMap.insert( error.sendingOp, RequireInst::forUseAfterSend(partitionOp.getSourceInst())); } @@ -2782,13 +2773,13 @@ struct DiagnosticEvaluator final } std::optional getElement(SILValue value) const { - return info->getValueMap().getTrackableValue(value).getID(); + return info->getValueMap().getTrackableValue(value).value.getID(); } SILValue getRepresentative(SILValue value) const { return info->getValueMap() .getTrackableValue(value) - .getRepresentative() + .value.getRepresentative() .maybeGetValue(); } diff --git a/lib/SILOptimizer/SILCombiner/Simplifications.def b/lib/SILOptimizer/SILCombiner/Simplifications.def index 5186a40053e..0b4444664bf 100644 --- a/lib/SILOptimizer/SILCombiner/Simplifications.def +++ b/lib/SILOptimizer/SILCombiner/Simplifications.def @@ -41,6 +41,7 @@ INSTRUCTION_SIMPLIFICATION(ReleaseValueInst) INSTRUCTION_SIMPLIFICATION(LoadInst) INSTRUCTION_SIMPLIFICATION(LoadBorrowInst) INSTRUCTION_SIMPLIFICATION(CopyValueInst) +INSTRUCTION_SIMPLIFICATION(CopyBlockInst) INSTRUCTION_SIMPLIFICATION(DestroyValueInst) INSTRUCTION_SIMPLIFICATION(DestructureStructInst) INSTRUCTION_SIMPLIFICATION(DestructureTupleInst) diff --git a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp index 3df88969da8..9933a05a319 100644 --- a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp @@ -77,6 +77,28 @@ static bool seemsUseful(SILInstruction *I) { return true; } + // Instructions which end the lifetimes of values which escape can only be + // deleted if compensating lifetime ends are added. Compensating lifetime + // ends are added by OSSACompleteLifetime when the def block of the value + // is different from the parent block of the instruction. But + // OSSACompleteLifetime requires that liveness be complete--that there are no + // pointer escapes. So we can't delete instructions which end the lifetime + // of values which escape to a pointer and whose parent blocks are different. + if (llvm::any_of(I->getAllOperands(), [I](Operand &operand) { + if (!operand.isLifetimeEnding()) + return false; + auto value = operand.get(); + if (isa(value)) + return false; + auto *insertionPoint = value->getDefiningInsertionPoint(); + ASSERT(insertionPoint); + if (insertionPoint->getParent() == I->getParent()) + return false; + return findPointerEscape(value); + })) { + return true; + } + if (auto *BI = dyn_cast(I)) { // Although the onFastPath builtin has no side-effects we don't want to // remove it. @@ -813,7 +835,9 @@ bool DCE::removeDead() { } } - OSSALifetimeCompletion completion(F, DT, *deadEndBlocks); + OSSALifetimeCompletion completion( + F, DT, *deadEndBlocks, OSSALifetimeCompletion::IgnoreTrivialVariable, + /*forceLivenessVerification=*/true); for (auto value : valuesToComplete) { if (!value.has_value()) continue; diff --git a/lib/SILOptimizer/Utils/PartitionUtils.cpp b/lib/SILOptimizer/Utils/PartitionUtils.cpp index d0dd74a1e27..4980743c202 100644 --- a/lib/SILOptimizer/Utils/PartitionUtils.cpp +++ b/lib/SILOptimizer/Utils/PartitionUtils.cpp @@ -113,55 +113,64 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const { os << "assign "; if (extraSpace) os << extraSpaceLiteral; - os << "%%" << opArgs[0] << " = %%" << opArgs[1]; + os << "%%" << getOpArg1() << " = %%" << getOpArg2(); break; } case PartitionOpKind::AssignFresh: - os << "assign_fresh %%" << opArgs[0]; + os << "assign_fresh %%" << getOpArg1(); break; case PartitionOpKind::Send: { os << "send "; if (extraSpace) os << extraSpaceLiteral; - os << "%%" << opArgs[0]; + os << "%%" << getOpArg1(); break; } case PartitionOpKind::UndoSend: { os << "undo_send "; if (extraSpace) os << extraSpaceLiteral; - os << "%%" << opArgs[0]; + os << "%%" << getOpArg1(); break; } case PartitionOpKind::Merge: { os << "merge "; if (extraSpace) os << extraSpaceLiteral; - os << "%%" << opArgs[0] << " with %%" << opArgs[1]; + os << "%%" << getOpArg1() << " with %%" << getOpArg2(); break; } case PartitionOpKind::Require: { os << "require "; + if (getOptions().containsOnly( + PartitionOp::Flag::RequireOfMutableBaseOfSendableValue)) + os << "[mutable_base_of_sendable_val] "; if (extraSpace) os << extraSpaceLiteral; - os << "%%" << opArgs[0]; + os << "%%" << getOpArg1(); break; } case PartitionOpKind::UnknownPatternError: os << "unknown pattern error "; - os << "%%" << opArgs[0]; + os << "%%" << getOpArg1(); break; case PartitionOpKind::InOutSendingAtFunctionExit: os << "inout_sending_at_function_exit "; if (extraSpace) os << extraSpaceLiteral; - os << "%%" << opArgs[0]; + os << "%%" << getOpArg1(); break; case PartitionOpKind::NonSendableIsolationCrossingResult: os << "nonsendable_isolationcrossing_result "; if (extraSpace) os << extraSpaceLiteral; - os << "%%" << opArgs[0]; + os << "%%" << getOpArg1(); + break; + case PartitionOpKind::AssignFreshAssign: + os << "assign_fresh_assign "; + if (extraSpace) + os << extraSpaceLiteral; + os << "%%" << getOpArg1() << " = %%" << getOpArg2(); break; } os << ": " << *getSourceInst(); diff --git a/lib/Sema/AsyncCallerExecutionMigration.cpp b/lib/Sema/AsyncCallerExecutionMigration.cpp index 5385d386cfd..323b2b901e3 100644 --- a/lib/Sema/AsyncCallerExecutionMigration.cpp +++ b/lib/Sema/AsyncCallerExecutionMigration.cpp @@ -50,8 +50,8 @@ public: : ctx(ctx), node(repr), isolation(isolation) {} /// Warns that the behavior of nonisolated async functions will change under - /// `AsyncCallerExecution` and suggests `@execution(concurrent)` to preserve - /// the current behavior. + /// `AsyncCallerExecution` and suggests `@concurrent` to preserve the current + /// behavior. void diagnose() const; }; } // end anonymous namespace @@ -74,7 +74,7 @@ void AsyncCallerExecutionMigrationTarget::diagnose() const { // If the attribute cannot appear on this kind of declaration, we can't // diagnose it. - if (!DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind::Execution, + if (!DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind::Concurrent, decl)) { return; } @@ -121,8 +121,14 @@ void AsyncCallerExecutionMigrationTarget::diagnose() const { attrs = &closure->getAttrs(); } - if (attrs && attrs->hasAttribute()) { - return; + if (attrs) { + if (attrs->hasAttribute()) + return; + + if (auto *nonisolated = attrs->getAttribute()) { + if (nonisolated->isNonSending()) + return; + } } } @@ -142,7 +148,7 @@ void AsyncCallerExecutionMigrationTarget::diagnose() const { } } - const ExecutionAttr attr(ExecutionKind::Concurrent, /*implicit=*/true); + const ConcurrentAttr attr(/*implicit=*/true); const auto featureName = feature.getName(); if (decl) { diff --git a/lib/Sema/AsyncCallerExecutionMigration.h b/lib/Sema/AsyncCallerExecutionMigration.h index 31a3218c18b..9086f90d3de 100644 --- a/lib/Sema/AsyncCallerExecutionMigration.h +++ b/lib/Sema/AsyncCallerExecutionMigration.h @@ -29,21 +29,21 @@ class ValueDecl; class AbstractClosureExpr; /// Warns that the behavior of nonisolated async functions will change under -/// `AsyncCallerExecution` and suggests `@execution(concurrent)` to preserve -/// the current behavior. +/// `AsyncCallerExecution` and suggests `@concurrent` to preserve the current +/// behavior. void warnAboutNewNonisolatedAsyncExecutionBehavior( ASTContext &ctx, FunctionTypeRepr *node, FunctionTypeIsolation isolation); /// Warns that the behavior of nonisolated async functions will change under -/// `AsyncCallerExecution` and suggests `@execution(concurrent)` to preserve -/// the current behavior. +/// `AsyncCallerExecution` and suggests `@concurrent` to preserve the current +/// behavior. void warnAboutNewNonisolatedAsyncExecutionBehavior(ASTContext &ctx, ValueDecl *node, ActorIsolation isolation); /// Warns that the behavior of nonisolated async functions will change under -/// `AsyncCallerExecution` and suggests `@execution(concurrent)` to preserve -/// the current behavior. +/// `AsyncCallerExecution` and suggests `@concurrent` to preserve the current +/// behavior. void warnAboutNewNonisolatedAsyncExecutionBehavior(ASTContext &ctx, AbstractClosureExpr *node, ActorIsolation isolation); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index d88b894dd6e..647f8dc6af7 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1631,6 +1631,29 @@ namespace { // Build a member reference. auto memberRef = resolveConcreteDeclRef(member, memberLocator); + // If our member reference is a value generic, then the resulting + // expression is the type value one to access the underlying parameter's + // value. + // + // This can occur in code that does something like: 'type(of: x).a' where + // 'a' is the static value generic member. + if (auto gp = dyn_cast(member)) { + if (gp->isValue()) { + auto refType = adjustedOpenedType; + auto ref = TypeValueExpr::createForDecl(memberLoc, gp, dc); + cs.setType(ref, refType); + + auto gpTy = gp->getDeclaredInterfaceType(); + auto subs = baseTy->getContextSubstitutionMap(); + ref->setParamType(gpTy.subst(subs)); + + auto result = new (ctx) DotSyntaxBaseIgnoredExpr(base, dotLoc, ref, + refType); + cs.setType(result, refType); + return result; + } + } + // If we're referring to a member type, it's just a type // reference. if (auto *TD = dyn_cast(member)) { @@ -1949,7 +1972,7 @@ namespace { solution.setExprTypes(base); auto capture = new (ctx) VarDecl(/*static*/ false, VarDecl::Introducer::Let, - SourceLoc(), + base->getEndLoc(), ctx.getIdentifier("$base$"), dc); capture->setImplicit(); @@ -3222,8 +3245,20 @@ namespace { Expr *visitTypeValueExpr(TypeValueExpr *expr) { auto toType = simplifyType(cs.getType(expr)); - assert(toType->isEqual(expr->getParamDecl()->getValueType())); + ASSERT(toType->isEqual(expr->getParamDecl()->getValueType())); cs.setType(expr, toType); + + auto declRefRepr = cast(expr->getRepr()); + auto resolvedTy = + TypeResolution::resolveContextualType(declRefRepr, cs.DC, + TypeResolverContext::InExpression, + nullptr, nullptr, nullptr); + + if (!resolvedTy || resolvedTy->hasError()) + return nullptr; + + expr->setParamType(resolvedTy); + return expr; } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index d72f5621a50..61427ffcfdf 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1723,9 +1723,6 @@ namespace { } Type visitTypeValueExpr(TypeValueExpr *E) { - auto ty = E->getParamDecl()->getDeclaredInterfaceType(); - auto paramType = CS.DC->mapTypeIntoContext(ty); - E->setParamType(paramType); return E->getParamDecl()->getValueType(); } @@ -2584,14 +2581,8 @@ namespace { return FunctionTypeIsolation::forGlobalActor(actorType); } - if (auto *execution = - closure->getAttrs().getAttribute()) { - switch (execution->getBehavior()) { - case ExecutionKind::Caller: - return FunctionTypeIsolation::forNonIsolatedCaller(); - case ExecutionKind::Concurrent: - return FunctionTypeIsolation::forNonIsolated(); - } + if (closure->getAttrs().hasAttribute()) { + return FunctionTypeIsolation::forNonIsolated(); } return FunctionTypeIsolation::forNonIsolated(); @@ -4591,8 +4582,8 @@ generateForEachStmtConstraints(ConstraintSystem &cs, DeclContext *dc, // non-`Sendable` state across the isolation boundary. `next()` should // inherit the isolation of the caller, but for now, use the opt out. if (isAsync) { - auto *nonisolated = new (ctx) - NonisolatedAttr(/*unsafe=*/true, /*implicit=*/true); + auto *nonisolated = + NonisolatedAttr::createImplicit(ctx, NonIsolatedModifier::Unsafe); makeIteratorVar->getAttrs().add(nonisolated); } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index ea4632232da..fd5e6b9842a 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3303,14 +3303,14 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, SmallVector func2Params; func2Params.append(func2->getParams().begin(), func2->getParams().end()); - // Support conversion from `@execution(caller)` to a function type + // Support conversion from `nonisolated(nonsending)` to a function type // with an isolated parameter. if (subKind == ConstraintKind::Subtype && func1->getIsolation().isNonIsolatedCaller() && func2->getIsolation().isParameter()) { - // `@execution(caller)` function gets an implicit isolation parameter - // introduced during SILGen and thunk is going to forward an isolation - // from the caller to it. + // `nonisolated(nonsending)` function gets an implicit isolation parameter + // introduced during SILGen and thunk is going to forward an isolation from + // the caller to it. // Let's remove the isolated parameter from consideration, function // types have to match on everything else. llvm::erase_if(func2Params, [](const AnyFunctionType::Param ¶m) { @@ -10565,26 +10565,34 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, if (Context.LangOpts.hasFeature(Feature::InferSendableFromCaptures)) { auto shouldCheckSendabilityOfBase = [&]() { if (!Context.getProtocol(KnownProtocolKind::Sendable)) - return false; + return Type(); - return llvm::any_of(lookup, [&](const auto &result) { + for (const auto &result : lookup) { auto decl = result.getValueDecl(); if (!isa_and_nonnull(decl)) - return false; + continue; - if (!decl->isInstanceMember()) - return false; + if (!decl->isInstanceMember() && + !decl->getDeclContext()->getSelfProtocolDecl()) + continue; auto hasAppliedSelf = decl->hasCurriedSelf() && doesMemberRefApplyCurriedSelf(baseObjTy, decl); auto numApplies = getNumApplications(hasAppliedSelf, functionRefInfo); - return numApplies < decl->getNumCurryLevels(); - }); + if (numApplies >= decl->getNumCurryLevels()) + continue; + + return decl->isInstanceMember() + ? instanceTy + : MetatypeType::get(instanceTy); + } + + return Type(); }; - if (shouldCheckSendabilityOfBase()) { + if (Type baseTyToCheck = shouldCheckSendabilityOfBase()) { auto sendableProtocol = Context.getProtocol(KnownProtocolKind::Sendable); - auto baseConformance = lookupConformance(instanceTy, sendableProtocol); + auto baseConformance = lookupConformance(baseTyToCheck, sendableProtocol); if (llvm::any_of( baseConformance.getConditionalRequirements(), @@ -10593,8 +10601,10 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, return false; return (req.getFirstType()->hasTypeVariable() && - req.getProtocolDecl()->isSpecificProtocol( - KnownProtocolKind::Sendable)); + (req.getProtocolDecl()->isSpecificProtocol( + KnownProtocolKind::Sendable) || + req.getProtocolDecl()->isSpecificProtocol( + KnownProtocolKind::SendableMetatype))); })) { result.OverallResult = MemberLookupResult::Unsolved; return result; diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index b35474b9878..c6f324d6197 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -708,6 +708,9 @@ private: // Disable all of the overload choices which are different from // the one which is currently picked for representative. for (auto *constraint : disjunction->getNestedConstraints()) { + if (constraint->isDisabled()) + continue; + auto choice = constraint->getOverloadChoice(); if (!choice.isDecl() || choice.getDecl() == representative.getDecl()) continue; diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index dc63555217a..42fd3b7ce45 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -1739,8 +1739,7 @@ bool swift::addNonIsolatedToSynthesized(NominalTypeDecl *nominal, return false; ASTContext &ctx = nominal->getASTContext(); - value->getAttrs().add( - new (ctx) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); + value->getAttrs().add(NonisolatedAttr::createImplicit(ctx)); return true; } diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 637a5b1ad31..591e26b5c18 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -110,8 +110,7 @@ static VarDecl *addImplicitDistributedActorIDProperty( nominal); // mark as nonisolated, allowing access to it from everywhere - propDecl->getAttrs().add( - new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); + propDecl->getAttrs().add(NonisolatedAttr::createImplicit(C)); // mark as @_compilerInitialized, since we synthesize the initializing // assignment during SILGen. propDecl->getAttrs().add( @@ -161,8 +160,7 @@ static VarDecl *addImplicitDistributedActorActorSystemProperty( nominal); // mark as nonisolated, allowing access to it from everywhere - propDecl->getAttrs().add( - new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); + propDecl->getAttrs().add(NonisolatedAttr::createImplicit(C)); auto idProperty = nominal->getDistributedActorIDProperty(); // If the id was not yet synthesized, we need to ensure that eventually @@ -739,8 +737,7 @@ static FuncDecl *createSameSignatureDistributedThunkDecl(DeclContext *DC, thunk->setSynthesized(true); thunk->setDistributedThunk(true); - thunk->getAttrs().add( - new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); + thunk->getAttrs().add(NonisolatedAttr::createImplicit(C)); return thunk; } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1a6bb0ee542..a4d1b99dd46 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1414,8 +1414,8 @@ FunctionType::ExtInfo ClosureEffectsRequest::evaluate( bool async = expr->getAsyncLoc().isValid(); bool sendable = expr->getAttrs().hasAttribute(); - // `@execution(...)` attribute is only valid on asynchronous function types. - if (expr->getAttrs().hasAttribute()) { + // `@concurrent` attribute is only valid on asynchronous function types. + if (expr->getAttrs().hasAttribute()) { async = true; } diff --git a/lib/Sema/DerivedConformance/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformance/DerivedConformanceActor.cpp index 7fdcb683096..24066b7ddb8 100644 --- a/lib/Sema/DerivedConformance/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformance/DerivedConformanceActor.cpp @@ -147,8 +147,7 @@ static ValueDecl *deriveActor_unownedExecutor(DerivedConformance &derived) { property->getAttrs().add(new (ctx) SemanticsAttr(SEMANTICS_DEFAULT_ACTOR, SourceLoc(), SourceRange(), /*implicit*/ true)); - property->getAttrs().add( - new (ctx) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); + property->getAttrs().add(NonisolatedAttr::createImplicit(ctx)); // Make the property implicitly final. property->getAttrs().add(new (ctx) FinalAttr(/*IsImplicit=*/true)); diff --git a/lib/Sema/DerivedConformance/DerivedConformanceCaseIterable.cpp b/lib/Sema/DerivedConformance/DerivedConformanceCaseIterable.cpp index dfdb509013b..625032b92cd 100644 --- a/lib/Sema/DerivedConformance/DerivedConformanceCaseIterable.cpp +++ b/lib/Sema/DerivedConformance/DerivedConformanceCaseIterable.cpp @@ -105,8 +105,7 @@ ValueDecl *DerivedConformance::deriveCaseIterable(ValueDecl *requirement) { SynthesizedIntroducer::Var, Context.Id_allCases, returnTy, /*isStatic=*/true, /*isFinal=*/true); - propDecl->getAttrs().add( - new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); + propDecl->getAttrs().add(NonisolatedAttr::createImplicit(C)); // Define the getter. auto *getterDecl = addGetterToReadOnlyDerivedProperty(propDecl); diff --git a/lib/Sema/DerivedConformance/DerivedConformanceDistributedActor.cpp b/lib/Sema/DerivedConformance/DerivedConformanceDistributedActor.cpp index 2dc2831712d..c7b0fc44fd1 100644 --- a/lib/Sema/DerivedConformance/DerivedConformanceDistributedActor.cpp +++ b/lib/Sema/DerivedConformance/DerivedConformanceDistributedActor.cpp @@ -465,8 +465,7 @@ static ValueDecl *deriveDistributedActor_id(DerivedConformance &derived) { /*isStatic=*/false, /*isFinal=*/true); // mark as nonisolated, allowing access to it from everywhere - propDecl->getAttrs().add( - new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); + propDecl->getAttrs().add(NonisolatedAttr::createImplicit(C)); derived.addMemberToConformanceContext(pbDecl, /*insertAtHead=*/true); derived.addMemberToConformanceContext(propDecl, /*insertAtHead=*/true); @@ -496,8 +495,7 @@ static ValueDecl *deriveDistributedActor_actorSystem( propertyType, /*isStatic=*/false, /*isFinal=*/true); // mark as nonisolated, allowing access to it from everywhere - propDecl->getAttrs().add( - new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); + propDecl->getAttrs().add(NonisolatedAttr::createImplicit(C)); // IMPORTANT: `id` MUST be the first field of a distributed actor, and // `actorSystem` MUST be the second field, because for a remote instance @@ -795,8 +793,7 @@ static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &der property->getAttrs().add(new (ctx) SemanticsAttr(SEMANTICS_DEFAULT_ACTOR, SourceLoc(), SourceRange(), /*implicit*/ true)); - property->getAttrs().add( - new (ctx) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); + property->getAttrs().add(NonisolatedAttr::createImplicit(ctx)); // Make the property implicitly final. property->getAttrs().add(new (ctx) FinalAttr(/*IsImplicit=*/true)); diff --git a/lib/Sema/DerivedConformance/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformance/DerivedConformanceEquatableHashable.cpp index 45021cf1f21..d8b0cb8a614 100644 --- a/lib/Sema/DerivedConformance/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformance/DerivedConformanceEquatableHashable.cpp @@ -555,8 +555,7 @@ deriveHashable_hashInto( // The derived hash(into:) for an actor must be non-isolated. if (!addNonIsolatedToSynthesized(derived, hashDecl) && derived.Nominal->isActor()) - hashDecl->getAttrs().add( - new (C) NonisolatedAttr(/*unsafe*/ false, /*implicit*/ true)); + hashDecl->getAttrs().add(NonisolatedAttr::createImplicit(C)); derived.addMembersToConformanceContext({hashDecl}); @@ -912,8 +911,7 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { // The derived hashValue of an actor must be nonisolated. if (!addNonIsolatedToSynthesized(derived, hashValueDecl) && derived.Nominal->isActor()) - hashValueDecl->getAttrs().add( - new (C) NonisolatedAttr(/*unsafe*/ false, /*implicit*/ true)); + hashValueDecl->getAttrs().add(NonisolatedAttr::createImplicit(C)); Pattern *hashValuePat = NamedPattern::createImplicit(C, hashValueDecl, intType); diff --git a/lib/Sema/PreCheckTarget.cpp b/lib/Sema/PreCheckTarget.cpp index 0336f1f0636..f3cbbb2445a 100644 --- a/lib/Sema/PreCheckTarget.cpp +++ b/lib/Sema/PreCheckTarget.cpp @@ -817,8 +817,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, : D->getInterfaceType()); } else { if (makeTypeValue) { - return TypeValueExpr::createForDecl(UDRE->getNameLoc(), - cast(D)); + return TypeValueExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC); } else { return TypeExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC); } @@ -1853,7 +1852,7 @@ TypeExpr *PreCheckTarget::simplifyUnresolvedSpecializeExpr( UnresolvedSpecializeExpr *us) { // If this is a reference type a specialized type, form a TypeExpr. // The base should be a TypeExpr that we already resolved. - if (auto *te = dyn_cast(us->getSubExpr())) { + if (auto *te = dyn_cast_or_null(us->getSubExpr())) { if (auto *declRefTR = dyn_cast_or_null(te->getTypeRepr())) { return TypeExpr::createForSpecializedDecl( diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 542ee845cb0..2142e7d419f 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -198,7 +198,7 @@ public: TypeChecker::checkDeclABIAttribute(D, attr); } - void visitExecutionAttr(ExecutionAttr *attr) { + void checkExecutionBehaviorAttribute(DeclAttribute *attr) { auto *const decl = cast(D); auto *const storage = dyn_cast(decl); @@ -208,7 +208,8 @@ public: } if (!decl->isAsync()) { - diagnoseAndRemoveAttr(attr, diag::attr_execution_only_on_async, decl); + diagnoseAndRemoveAttr(attr, diag::execution_behavior_only_on_async, attr, + decl); return; } @@ -224,8 +225,8 @@ public: // isolated parameters affect isolation of the function itself if (isa(repr)) { diagnoseAndRemoveAttr( - attr, diag::attr_execution_incompatible_isolated_parameter, decl, - P); + attr, diag::execution_behavior_incompatible_isolated_parameter, + attr, decl, P); return; } @@ -233,8 +234,9 @@ public: if (attrType->has(TypeAttrKind::Isolated)) { diagnoseAndRemoveAttr( attr, - diag::attr_execution_incompatible_dynamically_isolated_parameter, - decl, P); + diag:: + execution_behavior_incompatible_dynamically_isolated_parameter, + attr, decl, P); return; } } @@ -256,6 +258,16 @@ public: } } + void visitConcurrentAttr(ConcurrentAttr *attr) { + checkExecutionBehaviorAttribute(attr); + + if (auto *nonisolated = D->getAttrs().getAttribute()) { + if (nonisolated->isNonSending()) + diagnoseAndRemoveAttr(attr, diag::actor_isolation_multiple_attr_2, D, + attr, nonisolated); + } + } + void visitAlignmentAttr(AlignmentAttr *attr) { // Alignment must be a power of two. auto value = attr->getValue(); @@ -4320,7 +4332,7 @@ static void checkGlobalActorAttr( std::pair &globalActorAttr) { auto isolatedAttr = decl->getAttrs().getAttribute(); auto nonisolatedAttr = decl->getAttrs().getAttribute(); - auto executionAttr = decl->getAttrs().getAttribute(); + auto concurrentAttr = decl->getAttrs().getAttribute(); llvm::SmallVector attributes; @@ -4332,10 +4344,9 @@ static void checkGlobalActorAttr( if (nonisolatedAttr) { attributes.push_back(nonisolatedAttr); } - if (executionAttr) { - attributes.push_back(executionAttr); + if (concurrentAttr) { + attributes.push_back(concurrentAttr); } - if (attributes.size() == 1) return; @@ -7536,6 +7547,19 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) { // that do not have storage. auto dc = D->getDeclContext(); + if (attr->isNonSending()) { + // Just like `@concurrent` this form of `nonisolated` is only + // applicable to certain declarations. + if (!DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind::Concurrent, D)) { + diagnoseAndRemoveAttr( + attr, diag::cannot_specify_execution_behavior_for_decl, attr); + return; + } + + checkExecutionBehaviorAttribute(attr); + return; + } + if (auto var = dyn_cast(D)) { // stored properties have limitations as to when they can be nonisolated. auto type = var->getTypeInContext(); @@ -8150,18 +8174,13 @@ public: // Nothing else to check. } - void visitExecutionAttr(ExecutionAttr *attr) { - if (!ctx.LangOpts.hasFeature(Feature::ExecutionAttribute)) { - visitDeclAttribute(attr); - return; - } - - // `@execution(...)` implies `async`. + void checkExecutionBehaviorAttribute(DeclAttribute *attr) { + // execution behavior attribute implies `async`. if (closure->hasExplicitResultType() && closure->getAsyncLoc().isInvalid()) { ctx.Diags .diagnose(attr->getLocation(), - diag::attr_execution_only_on_async_closure) + diag::execution_behavior_only_on_async_closure, attr) .fixItRemove(attr->getRangeWithAt()); attr->setInvalid(); } @@ -8170,8 +8189,8 @@ public: ctx.Diags .diagnose( attr->getLocation(), - diag::attr_execution_type_attr_incompatible_with_global_isolation, - actorType) + diag::execution_behavior_attr_incompatible_with_global_isolation, + attr, actorType) .fixItRemove(attr->getRangeWithAt()); attr->setInvalid(); } @@ -8181,13 +8200,18 @@ public: ctx.Diags .diagnose( attr->getLocation(), - diag::attr_execution_type_attr_incompatible_with_isolated_param) + diag::execution_behavior_attr_incompatible_with_isolated_param, + attr) .fixItRemove(attr->getRangeWithAt()); attr->setInvalid(); } } } + void visitConcurrentAttr(ConcurrentAttr *attr) { + checkExecutionBehaviorAttribute(attr); + } + void visitNonisolatedAttr(NonisolatedAttr *attr) { if (attr->isUnsafe() || !ctx.LangOpts.hasFeature(Feature::ClosureIsolation)) { diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 006c6652f4e..019c98dbc7f 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -878,6 +878,12 @@ SendableCheckContext::preconcurrencyBehavior( return std::nullopt; } +std::optional +SendableCheckContext::preconcurrencyBehavior(Type type) const { + return type->getConcurrencyDiagnosticBehaviorLimit( + const_cast(fromDC)); +} + static bool shouldDiagnosePreconcurrencyImports(SourceFile &sf) { switch (sf.Kind) { case SourceFileKind::Interface: @@ -2712,16 +2718,16 @@ namespace { return; switch (toIsolation.getKind()) { - // Converting to `@execution(caller)` function type + // Converting to `nonisolated(nonsending)` function type case FunctionTypeIsolation::Kind::NonIsolatedCaller: { switch (fromIsolation.getKind()) { case FunctionTypeIsolation::Kind::NonIsolated: { - // nonisolated -> @execution(caller) doesn't cross + // nonisolated -> nonisolated(nonsending) doesn't cross // an isolation boundary. if (!fromFnType->isAsync()) break; - // @execution(concurrent) -> @execution(caller) + // @concurrent -> nonisolated(nonsending) // crosses an isolation boundary. LLVM_FALLTHROUGH; } @@ -2744,9 +2750,9 @@ namespace { break; } - // Converting to nonisolated synchronous or @execution(concurrent) - // asynchronous function type could require crossing an isolation - // boundary. + // Converting to nonisolated synchronous or @concurrent + // asynchronous function type could require crossing an + // isolation boundary. case FunctionTypeIsolation::Kind::NonIsolated: { switch (fromIsolation.getKind()) { case FunctionTypeIsolation::Kind::Parameter: @@ -2762,7 +2768,7 @@ namespace { } case FunctionTypeIsolation::Kind::NonIsolated: { - // nonisolated synchronous <-> @execution(concurrent) + // nonisolated synchronous <-> @concurrent if (fromFnType->isAsync() != toFnType->isAsync()) { diagnoseNonSendableParametersAndResult( toFnType, /*downgradeToWarning=*/true); @@ -2784,10 +2790,10 @@ namespace { break; case FunctionTypeIsolation::Kind::NonIsolated: { - // Since @execution(concurrent) as an asynchronous - // function it would mean that without Sendable - // check it would be possible for non-Sendable state - // to escape from actor isolation. + // Since @concurrent as an asynchronous function it + // would mean that without Sendable check it would + // be possible for non-Sendable state to escape from + // actor isolation. if (fromFnType->isAsync()) { diagnoseNonSendableParametersAndResult( toFnType, /*downgradeToWarning=*/true); @@ -3020,13 +3026,16 @@ namespace { // FIXME: When passing to a sending parameter, should this be handled // by region isolation? Or should it always be handled by region // isolation? - if (ctx.LangOpts.hasFeature(Feature::IsolatedConformances) && - (mayExecuteConcurrentlyWith( + if (mayExecuteConcurrentlyWith( localFunc.getAsDeclContext(), getDeclContext()) || - (explicitClosure && explicitClosure->isPassedToSendingParameter()))) { - GenericSignature genericSig; - if (auto afd = localFunc.getAbstractFunctionDecl()) - genericSig = afd->getGenericSignature(); + (explicitClosure && explicitClosure->isPassedToSendingParameter())) { + auto innermostGenericDC = localFunc.getAsDeclContext(); + while (innermostGenericDC && !innermostGenericDC->isGenericContext()) + innermostGenericDC = innermostGenericDC->getParent(); + + GenericSignature genericSig = innermostGenericDC + ? innermostGenericDC->getGenericSignatureOfContext() + : GenericSignature(); for (const auto &capturedType : localFunc.getCaptureInfo().getCapturedTypes()) { @@ -3037,8 +3046,6 @@ namespace { ->getDepth(); } else if (type->isTypeParameter()) { genericDepth = type->getRootGenericParam()->getDepth(); - - type = localFunc.getAsDeclContext()->mapTypeIntoContext(type); } else { continue; } @@ -3049,6 +3056,9 @@ namespace { genericDepth < genericSig.getNextDepth() - 1) continue; + if (type->isTypeParameter() && innermostGenericDC) + type = innermostGenericDC->mapTypeIntoContext(type); + // Check that the metatype is sendable. SendableCheckContext sendableContext(getDeclContext(), preconcurrency); diagnoseNonSendableTypes(MetatypeType::get(type), @@ -4889,7 +4899,7 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true, auto isolatedAttr = decl->getAttrs().getAttribute(); auto nonisolatedAttr = decl->getAttrs().getAttribute(); auto globalActorAttr = decl->getGlobalActorAttr(); - auto concurrentExecutionAttr = decl->getAttrs().getAttribute(); + auto concurrentAttr = decl->getAttrs().getAttribute(); // Remove implicit attributes if we only care about explicit ones. if (onlyExplicit) { @@ -4899,13 +4909,13 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true, isolatedAttr = nullptr; if (globalActorAttr && globalActorAttr->first->isImplicit()) globalActorAttr = std::nullopt; - if (concurrentExecutionAttr && concurrentExecutionAttr->isImplicit()) - concurrentExecutionAttr = nullptr; + if (concurrentAttr && concurrentAttr->isImplicit()) + concurrentAttr = nullptr; } unsigned numIsolationAttrs = (isolatedAttr ? 1 : 0) + (nonisolatedAttr ? 1 : 0) + - (globalActorAttr ? 1 : 0) + (concurrentExecutionAttr ? 1 : 0); + (globalActorAttr ? 1 : 0) + (concurrentAttr ? 1 : 0); if (numIsolationAttrs == 0) { if (isa(decl) && !decl->isImplicit()) { return ActorIsolation::forNonisolated(false); @@ -4913,28 +4923,18 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true, return std::nullopt; } - // If the declaration is explicitly marked with 'execution', return the - // appropriate isolation. - // - // NOTE: This needs to occur before we handle an explicit nonisolated attr, - // since if @execution and nonisolated are used together, we want to ensure - // that @execution takes priority. This ensures that if we import code from a - // module that was compiled with a different value for AsyncCallerExecution, - // we get the semantics of the source module. - if (concurrentExecutionAttr) { - switch (concurrentExecutionAttr->getBehavior()) { - case ExecutionKind::Concurrent: - return ActorIsolation::forNonisolated(false /*is unsafe*/); - case ExecutionKind::Caller: - return ActorIsolation::forCallerIsolationInheriting(); - } - } + if (concurrentAttr) + return ActorIsolation::forNonisolated(/*is unsafe*/ false); // If the declaration is explicitly marked 'nonisolated', report it as // independent. if (nonisolatedAttr) { - // If the nonisolated async inherits isolation from context is set, return - // caller isolation inheriting. + // 'nonisolated(nonsending)' modifier is set on the decl. + if (nonisolatedAttr->isNonSending()) + return ActorIsolation::forCallerIsolationInheriting(); + + // If the nonisolated async inherits isolation from context, + // return caller isolation inheriting. if (decl->getASTContext().LangOpts.hasFeature( Feature::AsyncCallerExecution)) { if (auto *func = dyn_cast(decl); @@ -5695,13 +5695,16 @@ static void addAttributesForActorIsolation(ValueDecl *value, ASTContext &ctx = value->getASTContext(); switch (isolation) { case ActorIsolation::CallerIsolationInheriting: - value->getAttrs().add(new (ctx) ExecutionAttr(ExecutionKind::Caller, - /*implicit=*/true)); + value->getAttrs().add(new (ctx) NonisolatedAttr( + /*atLoc=*/{}, /*range=*/{}, NonIsolatedModifier::NonSending, + /*implicit=*/true)); break; case ActorIsolation::Nonisolated: case ActorIsolation::NonisolatedUnsafe: { - value->getAttrs().add(new (ctx) NonisolatedAttr( - isolation == ActorIsolation::NonisolatedUnsafe, /*implicit=*/true)); + value->getAttrs().add(NonisolatedAttr::createImplicit( + ctx, isolation == ActorIsolation::NonisolatedUnsafe + ? NonIsolatedModifier::Unsafe + : NonIsolatedModifier::None)); break; } case ActorIsolation::GlobalActor: { @@ -5889,10 +5892,12 @@ static InferredActorIsolation computeActorIsolation(Evaluator &evaluator, // as nonisolated but since AsyncCallerExecution is enabled, we return // CallerIsolationInheriting. if (isolationFromAttr && isolationFromAttr->getKind() == - ActorIsolation::CallerIsolationInheriting && - !value->getAttrs().hasAttribute()) { - value->getAttrs().add(new (ctx) ExecutionAttr(ExecutionKind::Caller, - /*implicit=*/true)); + ActorIsolation::CallerIsolationInheriting) { + auto nonisolated = value->getAttrs().getAttribute(); + if (!nonisolated || !nonisolated->isNonSending()) + value->getAttrs().add(new (ctx) NonisolatedAttr( + /*atLoc*/ {}, /*range=*/{}, NonIsolatedModifier::NonSending, + /*implicit=*/true)); } if (auto *fd = dyn_cast(value)) { @@ -6672,8 +6677,7 @@ static bool checkSendableInstanceStorage( propertyType, context, /*inDerivedConformance*/Type(), property->getLoc(), [&](Type type, DiagnosticBehavior behavior) { - auto preconcurrency = - context.preconcurrencyBehavior(type->getAnyNominal()); + auto preconcurrency = context.preconcurrencyBehavior(type); if (isImplicitSendableCheck(check)) { // If this is for an externally-visible conformance, fail. if (check == SendableCheck::ImplicitForExternallyVisible) { @@ -6717,8 +6721,7 @@ static bool checkSendableInstanceStorage( elementType, context, /*inDerivedConformance*/Type(), element->getLoc(), [&](Type type, DiagnosticBehavior behavior) { - auto preconcurrency = - context.preconcurrencyBehavior(type->getAnyNominal()); + auto preconcurrency = context.preconcurrencyBehavior(type); if (isImplicitSendableCheck(check)) { // If this is for an externally-visible conformance, fail. if (check == SendableCheck::ImplicitForExternallyVisible) { @@ -7922,8 +7925,6 @@ ConformanceIsolationRequest::evaluate(Evaluator &evaluator, ProtocolConformance auto dc = rootNormal->getDeclContext(); ASTContext &ctx = dc->getASTContext(); - if (!ctx.LangOpts.hasFeature(Feature::IsolatedConformances)) - return ActorIsolation::forNonisolated(false); // If the protocol itself is isolated, don't infer isolation for the // conformance. @@ -7931,7 +7932,7 @@ ConformanceIsolationRequest::evaluate(Evaluator &evaluator, ProtocolConformance return ActorIsolation::forNonisolated(false); // If we are inferring isolated conformances and the conforming type is - // isolated to a global actor, + // isolated to a global actor, use the conforming type's isolation. auto nominal = dc->getSelfNominalTypeDecl(); if (ctx.LangOpts.hasFeature(Feature::InferIsolatedConformances) && nominal) { @@ -8000,7 +8001,8 @@ namespace { firstConformance->getIsolation(), firstConformance->getType(), firstConformance->getProtocol()->getName(), - getContextIsolation()); + getContextIsolation()) + .warnUntilSwiftVersion(6); return true; } }; diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 6e74ecbf1ff..a109db1b65a 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -407,10 +407,18 @@ struct SendableCheckContext { /// type in this context. DiagnosticBehavior diagnosticBehavior(NominalTypeDecl *nominal) const; + /// Determine the preconcurrency behavior when referencing the given + /// declaration from a type. This only has an effect when the declaration + /// is a nominal type. std::optional preconcurrencyBehavior( Decl *decl, bool ignoreExplicitConformance = false) const; + /// Determine the preconcurrency behavior when referencing the given + /// non-Sendable type. This only has an effect when the declaration + /// is a nominal or metatype type. + std::optional preconcurrencyBehavior(Type type) const; + /// Whether to warn about a Sendable violation even in minimal checking. bool warnInMinimalChecking() const; }; @@ -460,8 +468,7 @@ bool diagnoseNonSendableTypes( [&](Type specificType, DiagnosticBehavior behavior) { // FIXME: Reconcile preconcurrency declaration vs preconcurrency // import behavior. - auto preconcurrency = - fromContext.preconcurrencyBehavior(specificType->getAnyNominal()); + auto preconcurrency = fromContext.preconcurrencyBehavior(specificType); ctx.Diags.diagnose(diagnoseLoc, diag, type, diagArgs...) .limitBehaviorWithPreconcurrency(behavior, @@ -497,8 +504,7 @@ bool diagnoseIfAnyNonSendableTypes( diagnoseNonSendableTypes( type, fromContext, derivedConformance, typeLoc, [&](Type specificType, DiagnosticBehavior behavior) { - auto preconcurrency = - fromContext.preconcurrencyBehavior(specificType->getAnyNominal()); + auto preconcurrency = fromContext.preconcurrencyBehavior(specificType); if (behavior == DiagnosticBehavior::Ignore || preconcurrency == DiagnosticBehavior::Ignore) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 4aeeeec1f98..0089ccc9da1 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2272,6 +2272,10 @@ ParamSpecifierRequest::evaluate(Evaluator &evaluator, nestedRepr = lifetime->getBase(); } + if (auto callerIsolated = dyn_cast(nestedRepr)) { + nestedRepr = callerIsolated->getBase(); + } + if (auto sending = dyn_cast(nestedRepr)) { // If we do not have an Ownership Repr and do not have a no escape type, // return implicit copyable consuming. @@ -2294,7 +2298,7 @@ ParamSpecifierRequest::evaluate(Evaluator &evaluator, } return ownershipRepr->getSpecifier(); } - + return ParamSpecifier::Default; } diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 77395c53a19..6b18ec4826c 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -19,6 +19,7 @@ #include "TypeCheckProtocol.h" #include "TypeChecker.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/AvailabilityInference.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" @@ -4053,18 +4054,46 @@ private: public: void diagnoseUnmatchedRequirements() { + auto ext = dyn_cast(decl); + if (!ext) + return; + + llvm::SmallString<128> stubs; + llvm::raw_svector_ostream stubStream(stubs); + + unsigned numEmitted = 0; + for (auto req : unmatchedRequirements) { // Ignore `@optional` protocol requirements. if (isOptionalObjCProtocolRequirement(req)) continue; - auto ext = cast(req->getDeclContext()->getAsDecl()) - ->getImplementationContext(); + if (numEmitted == 0) { + // Emit overall diagnostic for all the notes to attach to. + diagnose(ext, diag::objc_implementation_missing_impls, + getCategoryName(req->getDeclContext())); + } - diagnose(ext->getDecl(), diag::objc_implementation_missing_impl, - getCategoryName(req->getDeclContext()), req); + numEmitted += 1; - // FIXME: Should give fix-it to add stub implementation + // Emit different diagnostic if there's an async alternative. + if (auto asyncAlternative = getAsyncAlternative(req)) { + diagnose(ext, diag::objc_implementation_missing_impl_either, + asyncAlternative, req); + req = asyncAlternative; + } else { + diagnose(ext, diag::objc_implementation_missing_impl, req); + } + + // Append stub for this requirement into eventual fix-it. + swift::printRequirementStub(req, ext, ext->getSelfInterfaceType(), + ext->getStartLoc(), stubStream, + /*objCAttr=*/true); + } + + if (!stubs.empty()) { + diagnose(ext, diag::objc_implementation_missing_impls_fixit, numEmitted) + .fixItInsertAfter(ext->getBraces().Start, stubs); } } diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 7218b3c0aee..62a7ea1797b 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1588,13 +1588,13 @@ namespace { UNINTERESTING_ATTR(Borrowed) UNINTERESTING_ATTR(Borrowing) UNINTERESTING_ATTR(CDecl) + UNINTERESTING_ATTR(Concurrent) UNINTERESTING_ATTR(Consuming) UNINTERESTING_ATTR(Documentation) UNINTERESTING_ATTR(Dynamic) UNINTERESTING_ATTR(DynamicCallable) UNINTERESTING_ATTR(DynamicMemberLookup) UNINTERESTING_ATTR(SILGenName) - UNINTERESTING_ATTR(Execution) UNINTERESTING_ATTR(Exported) UNINTERESTING_ATTR(ForbidSerializingReference) UNINTERESTING_ATTR(GKInspectable) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index e184ff4ed0a..c27bb4e0b30 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -728,6 +728,20 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current, auto found = nominal->lookupDirect(current->getBaseName(), SourceLoc(), flags); otherDefinitions.append(found.begin(), found.end()); + + // Look into the generics of the type. Value generic parameters can appear + // as static members of the type. + if (auto genericDC = static_cast(nominal)->getAsGenericContext()) { + auto gpList = genericDC->getGenericParams(); + + if (gpList && !current->getBaseName().isSpecial()) { + auto gp = gpList->lookUpGenericParam(current->getBaseIdentifier()); + + if (gp && gp->isValue()) { + otherDefinitions.push_back(gp); + } + } + } } } else if (currentDC->isLocalContext()) { if (!current->isImplicit()) { @@ -1135,6 +1149,7 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current, break; } } + return std::make_tuple<>(); } diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 2f5a84d9737..779e6bdf4e6 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -1064,17 +1064,19 @@ CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics( break; } - if (!isolatedConformances.empty()) { + if (!isolatedConformances.empty() && signature) { // Dig out the original type parameter for the requirement. // FIXME: req might not be the right pre-substituted requirement, // if this came from a conditional requirement. - if (auto failedProtocol = - typeParameterProhibitsIsolatedConformance(req.getFirstType(), - signature)) { - return CheckGenericArgumentsResult::createIsolatedConformanceFailure( - req, substReq, - TinyPtrVector(isolatedConformances), - *failedProtocol); + for (const auto &isolatedConformance : isolatedConformances) { + (void)isolatedConformance; + if (auto failed = + signature->prohibitsIsolatedConformance(req.getFirstType())) { + return CheckGenericArgumentsResult::createIsolatedConformanceFailure( + req, substReq, + TinyPtrVector(isolatedConformances), + failed->second); + } } } } @@ -1181,28 +1183,3 @@ Type StructuralTypeRequest::evaluate(Evaluator &evaluator, return TypeAliasType::get(typeAlias, parent, genericArgs, result); } - -std::optional swift::typeParameterProhibitsIsolatedConformance( - Type type, GenericSignature signature) { - if (!type->isTypeParameter()) - return std::nullopt; - - // An isolated conformance cannot be used in a context where the type - // parameter can escape the isolation domain in which the conformance - // was formed. To establish this, we look for Sendable or SendableMetatype - // requirements on the type parameter itself. - ASTContext &ctx = type->getASTContext(); - auto sendableProto = ctx.getProtocol(KnownProtocolKind::Sendable); - auto sendableMetatypeProto = - ctx.getProtocol(KnownProtocolKind::SendableMetatype); - - if (sendableProto && - signature->requiresProtocol(type, sendableProto)) - return sendableProto; - - if (sendableMetatypeProto && - signature->requiresProtocol(type, sendableMetatypeProto)) - return sendableMetatypeProto; - - return std::nullopt; -} diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index a7083b37980..7d32bc32ecd 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -90,7 +90,21 @@ filterForEnumElement(DeclContext *DC, SourceLoc UseLoc, ValueDecl *e = result.getValueDecl(); assert(e); - // Skip if the enum element was referenced as an instance member + // We only care about enum members, and must either have an EnumElementDecl, + // or a VarDecl which could be wrapping an underlying enum element. + // FIXME: We check this up-front to avoid kicking InterfaceTypeRequest + // below to help workaround https://github.com/swiftlang/swift/issues/80657 + // for non-enum cases. The proper fix is to move this filtering logic + // into the constraint system. + if (!e->getDeclContext()->getSelfEnumDecl()) + continue; + + auto *EED = dyn_cast(e); + auto *VD = dyn_cast(e); + if (!EED && !VD) + continue; + + // Skip if referenced as an instance member if (unqualifiedLookup) { if (!result.getBaseDecl() || !result.getBaseDecl()->getInterfaceType()->is()) { @@ -98,17 +112,17 @@ filterForEnumElement(DeclContext *DC, SourceLoc UseLoc, } } - if (auto *oe = dyn_cast(e)) { + if (EED) { // Note that there could be multiple elements with the same // name, such results in a re-declaration error, so let's // just always pick the last element, just like in `foundConstant` // case. - foundElement = oe; + foundElement = EED; continue; } - if (auto *var = dyn_cast(e)) { - foundConstant = var; + if (VD) { + foundConstant = VD; continue; } } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index d9cf87e43f0..22062516163 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -2567,13 +2567,6 @@ checkIndividualConformance(NormalProtocolConformance *conformance) { ComplainLoc, diag::unchecked_conformance_not_special, ProtoType); } - // Complain if the global-actor-isolated conformances are not enabled. - if (conformance->isIsolated() && - !Context.LangOpts.hasFeature(Feature::IsolatedConformances)) { - Context.Diags.diagnose( - ComplainLoc, diag::isolated_conformance_experimental_feature); - } - bool allowImpliedConditionalConformance = false; if (Proto->isSpecificProtocol(KnownProtocolKind::Sendable)) { // In -swift-version 5 mode, a conditional conformance to a protocol can imply @@ -3704,8 +3697,12 @@ static Type getTupleConformanceTypeWitness(DeclContext *dc, bool swift:: printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, - Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS) { - if (isa(Requirement)) { + Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS, + bool withExplicitObjCAttr) { + // We sometimes use this for @implementation extensions too. + bool forProtocol = isa(Requirement->getDeclContext()); + + if (isa(Requirement) && forProtocol) { if (auto CD = Adopter->getSelfClassDecl()) { if (!CD->isSemanticallyFinal() && isa(Adopter)) { // In this case, user should mark class as 'final' or define @@ -3731,15 +3728,35 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, ExtraIndentStreamPrinter Printer(OS, StubIndent); Printer.printNewline(); + PrintOptions Options = PrintOptions::printForDiagnostics( + AccessLevel::Private, Ctx.TypeCheckerOpts.PrintFullConvention); + Options.PrintDocumentationComments = false; + Options.PrintAccess = false; + Options.SkipAttributes = true; + Options.FunctionDefinitions = true; + Options.PrintAccessorBodiesInProtocols = true; + Options.PrintExplicitAccessorParameters = false; + Options.FullyQualifiedTypesIfAmbiguous = true; + + if (withExplicitObjCAttr) { + if (auto runtimeName = Requirement->getObjCRuntimeName()) { + llvm::SmallString<32> scratch; + Printer.printAttrName("@objc"); + Printer << "(" << runtimeName->getString(scratch) << ")"; + Printer.printNewline(); + Options.ExcludeAttrList.push_back(DeclAttrKind::ObjC); + } + } + AccessLevel Access = std::min( /* Access of the context */ Adopter->getSelfNominalTypeDecl()->getFormalAccess(), /* Access of the protocol */ - Requirement->getDeclContext()->getSelfProtocolDecl()-> - getFormalAccess()); - if (Access == AccessLevel::Public) - Printer << "public "; + Requirement->getDeclContext()->getSelfNominalTypeDecl() + ->getFormalAccess()); + if (Access > AccessLevel::Internal) + Printer.printKeyword(getAccessLevelSpelling(Access), Options, " "); if (auto MissingTypeWitness = dyn_cast(Requirement)) { Printer << "typealias " << MissingTypeWitness->getName() << " = "; @@ -3753,7 +3770,7 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, Printer << "\n"; } else { - if (isa(Requirement)) { + if (isa(Requirement) && forProtocol) { if (auto CD = Adopter->getSelfClassDecl()) { if (!CD->isFinal()) { Printer << "required "; @@ -3763,15 +3780,6 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, } } - PrintOptions Options = PrintOptions::printForDiagnostics( - AccessLevel::Private, Ctx.TypeCheckerOpts.PrintFullConvention); - Options.PrintDocumentationComments = false; - Options.PrintAccess = false; - Options.SkipAttributes = true; - Options.FunctionDefinitions = true; - Options.PrintAccessorBodiesInProtocols = true; - Options.FullyQualifiedTypesIfAmbiguous = true; - bool AdopterIsClass = Adopter->getSelfClassDecl() != nullptr; // Skip 'mutating' only inside classes: mutating methods usually // don't have a sensible non-mutating implementation. @@ -3797,9 +3805,12 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, }; Options.setBaseType(AdopterTy); Options.CurrentModule = Adopter->getParentModule(); - if (isa(Adopter)) { - // Create a variable declaration instead of a computed property in - // nominal types... + + // Can the conforming declaration declare a stored property? + auto ImplementedAdopter = Adopter->getImplementedObjCContext(); + if (isa(ImplementedAdopter) && + (!isa(ImplementedAdopter) || Requirement->isStatic())) { + // Create a variable declaration instead of a computed property... Options.PrintPropertyAccessors = false; // ...but a non-mutating setter requirement will force us into a @@ -3810,6 +3821,11 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, if (const auto Set = VD->getOpaqueAccessor(AccessorKind::Set)) if (Set->getAttrs().hasAttribute()) Options.PrintPropertyAccessors = true; + + // If we're not printing the accessors, make them affect the introducer + // instead. + Options.InferPropertyIntroducerFromAccessors = + !Options.PrintPropertyAccessors; } Requirement->print(Printer, Options); Printer << "\n"; @@ -4940,6 +4956,9 @@ static void diagnoseConformanceIsolationErrors( hasIsolatedConformances = true; } + // Take the least-restrictive behavior. + behavior = behavior.merge(assocConformanceError.behavior); + anyNonDistributedIssues = true; } @@ -4968,8 +4987,7 @@ static void diagnoseConformanceIsolationErrors( } // Suggest isolating the conformance, if possible. - if (ctx.LangOpts.hasFeature(Feature::IsolatedConformances) && - potentialIsolation && potentialIsolation->isGlobalActor() && + if (potentialIsolation && potentialIsolation->isGlobalActor() && !conformance->isIsolated()) { bool isMainActor = false; Type globalActorIsolation = potentialIsolation->getGlobalActor(); @@ -5396,9 +5414,19 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx, // If the isolation doesn't match, record an error. if (!outerIsolation.isGlobalActor() || outerIsolation != innerIsolation) { + DiagnosticBehavior behavior = DiagnosticBehavior::Unspecified; + // If we're working with requirements imported from Clang, or with + // global actor isolation in general, use the default diagnostic + // behavior based on the conformance context. + if (proto->hasClangNode() || + outerIsolation.isGlobalActor() || + innerIsolation.isGlobalActor()) + behavior = SendableCheckContext(dc).defaultDiagnosticBehavior(); + ctx.getGlobalCache().conformanceIsolationErrors[conformance] .push_back( - AssociatedConformanceIsolationError{isolatedConformance}); + AssociatedConformanceIsolationError{ + isolatedConformance, behavior}); return true; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index d1432534b11..65cb04d4c13 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2324,6 +2324,8 @@ namespace { TypeResolutionOptions options); NeverNullType resolveSendingTypeRepr(SendingTypeRepr *repr, TypeResolutionOptions options); + NeverNullType resolveCallerIsolatedTypeRepr(CallerIsolatedTypeRepr *repr, + TypeResolutionOptions options); NeverNullType resolveCompileTimeLiteralTypeRepr(CompileTimeLiteralTypeRepr *repr, TypeResolutionOptions options); @@ -2730,7 +2732,8 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, !isa(repr) && !isa(repr) && !isa(repr) && !isa(repr) && !isa(repr) && !isa(repr) && - !isa(repr)) { + !isa(repr) && + !isa(repr)) { options.setContext(std::nullopt); } @@ -2753,6 +2756,9 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr, return resolveIsolatedTypeRepr(cast(repr), options); case TypeReprKind::Sending: return resolveSendingTypeRepr(cast(repr), options); + case TypeReprKind::CallerIsolated: + return resolveCallerIsolatedTypeRepr(cast(repr), + options); case TypeReprKind::CompileTimeLiteral: return resolveCompileTimeLiteralTypeRepr(cast(repr), options); @@ -4195,10 +4201,11 @@ NeverNullType TypeResolver::resolveASTFunctionType( } } - if (auto executionAttr = claim(attrs)) { + auto checkExecutionBehaviorAttribute = [&](TypeAttribute *attr) { if (!repr->isAsync()) { - diagnoseInvalid(repr, executionAttr->getAtLoc(), - diag::attr_execution_type_attr_only_on_async); + diagnoseInvalid(repr, attr->getAttrLoc(), + diag::execution_behavior_type_attr_only_on_async, + attr->getAttrName()); } switch (isolation.getKind()) { @@ -4207,38 +4214,36 @@ NeverNullType TypeResolver::resolveASTFunctionType( case FunctionTypeIsolation::Kind::GlobalActor: diagnoseInvalid( - repr, executionAttr->getAtLoc(), - diag::attr_execution_type_attr_incompatible_with_global_isolation, - isolation.getGlobalActorType()); + repr, attr->getAttrLoc(), + diag::execution_behavior_type_attr_incompatible_with_global_isolation, + attr->getAttrName(), isolation.getGlobalActorType()); break; case FunctionTypeIsolation::Kind::Parameter: diagnoseInvalid( - repr, executionAttr->getAtLoc(), - diag::attr_execution_type_attr_incompatible_with_isolated_param); + repr, attr->getAttrLoc(), + diag::execution_behavior_type_attr_incompatible_with_isolated_param, + attr->getAttrName()); break; case FunctionTypeIsolation::Kind::Erased: diagnoseInvalid( - repr, executionAttr->getAtLoc(), - diag::attr_execution_type_attr_incompatible_with_isolated_any); + repr, attr->getAttrLoc(), + diag::execution_behavior_type_attr_incompatible_with_isolated_any, + attr->getAttrName()); break; case FunctionTypeIsolation::Kind::NonIsolatedCaller: - llvm_unreachable("cannot happen because multiple @execution attributes " - "aren't allowed."); + llvm_unreachable( + "cannot happen because multiple execution behavior attributes " + "aren't allowed."); } + }; - if (!repr->isInvalid()) { - switch (executionAttr->getBehavior()) { - case ExecutionKind::Concurrent: - isolation = FunctionTypeIsolation::forNonIsolated(); - break; - case ExecutionKind::Caller: - isolation = FunctionTypeIsolation::forNonIsolatedCaller(); - break; - } - } + if (auto concurrentAttr = claim(attrs)) { + checkExecutionBehaviorAttribute(concurrentAttr); + if (!repr->isInvalid()) + isolation = FunctionTypeIsolation::forNonIsolated(); } else { if (ctx.LangOpts.getFeatureState(Feature::AsyncCallerExecution) .isEnabledForAdoption()) { @@ -5267,6 +5272,69 @@ TypeResolver::resolveSendingTypeRepr(SendingTypeRepr *repr, return resolveType(repr->getBase(), options); } +NeverNullType +TypeResolver::resolveCallerIsolatedTypeRepr(CallerIsolatedTypeRepr *repr, + TypeResolutionOptions options) { + Type type = resolveType(repr->getBase(), options); + if (type->hasError()) + return ErrorType::get(getASTContext()); + + auto *fnType = dyn_cast(type.getPointer()); + if (!fnType) { + diagnoseInvalid(repr, repr->getStartLoc(), + diag::nonisolated_nonsending_only_on_function_types, repr); + return ErrorType::get(getASTContext()); + } + + if (!fnType->isAsync()) { + diagnoseInvalid(repr, repr->getStartLoc(), + diag::nonisolated_nonsending_only_on_async, repr); + } + + if (auto *ATR = dyn_cast(repr->getBase())) { + if (ATR->get(TypeAttrKind::Concurrent)) { + diagnoseInvalid( + repr, repr->getStartLoc(), + diag::cannot_use_nonisolated_nonsending_together_with_concurrent, + repr); + } + } + + switch (fnType->getIsolation().getKind()) { + case FunctionTypeIsolation::Kind::NonIsolated: + break; + + case FunctionTypeIsolation::Kind::GlobalActor: + diagnoseInvalid( + repr, repr->getStartLoc(), + diag::nonisolated_nonsending_incompatible_with_global_isolation, repr, + fnType->getIsolation().getGlobalActorType()); + break; + + case FunctionTypeIsolation::Kind::Parameter: + diagnoseInvalid( + repr, repr->getStartLoc(), + diag::nonisolated_nonsending_incompatible_with_isolated_param, repr); + break; + + case FunctionTypeIsolation::Kind::Erased: + diagnoseInvalid(repr, repr->getStartLoc(), + diag::nonisolated_nonsending_incompatible_with_isolated_any, + repr); + break; + + case FunctionTypeIsolation::Kind::NonIsolatedCaller: + llvm_unreachable( + "cannot happen because multiple nonisolated(nonsending) attributes " + "aren't allowed."); + } + + if (repr->isInvalid()) + return ErrorType::get(getASTContext()); + + return fnType->withIsolation(FunctionTypeIsolation::forNonIsolatedCaller()); +} + NeverNullType TypeResolver::resolveCompileTimeLiteralTypeRepr(CompileTimeLiteralTypeRepr *repr, TypeResolutionOptions options) { @@ -6267,6 +6335,18 @@ Type TypeChecker::substMemberTypeWithBase(TypeDecl *member, resultType = TypeAliasType::get(aliasDecl, sugaredBaseTy, {}, resultType); } + // However, if overload resolution finds a value generic decl from name + // lookup, replace the returned member type to be the underlying value type + // of the generic. + // + // This can occur in code that does something like: 'type(of: x).a' where + // 'a' is the static value generic member. + if (auto gp = dyn_cast(member)) { + if (gp->isValue()) { + resultType = gp->getValueType(); + } + } + return resultType; } @@ -6387,6 +6467,7 @@ private: case TypeReprKind::PackElement: case TypeReprKind::LifetimeDependent: case TypeReprKind::Integer: + case TypeReprKind::CallerIsolated: return false; } } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 967a81133d1..e87f692218a 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1535,14 +1535,6 @@ bool maybeDiagnoseMissingImportForMember(const ValueDecl *decl, /// source file. void diagnoseMissingImports(SourceFile &sf); -/// Determine whether the type parameter has requirements that would prohibit -/// it from using any isolated conformances. -/// -/// Returns the protocol to which the type conforms that causes the conflict, -/// which can be either Sendable or SendableMetatype. -std::optional typeParameterProhibitsIsolatedConformance( - Type type, GenericSignature signature); - } // end namespace swift #endif diff --git a/lib/Sema/TypeOfReference.cpp b/lib/Sema/TypeOfReference.cpp index 791f493a084..ef2606e8b93 100644 --- a/lib/Sema/TypeOfReference.cpp +++ b/lib/Sema/TypeOfReference.cpp @@ -849,19 +849,27 @@ FunctionType *ConstraintSystem::adjustFunctionTypeForConcurrency( } else if (numApplies < decl->getNumCurryLevels() && decl->hasCurriedSelf() ) { auto shouldMarkMemberTypeSendable = [&]() { - // Static member types are @Sendable on both levels because - // they only capture a metatype "base" that is always Sendable. - // For example, `(S.Type) -> () -> Void`. - if (!decl->isInstanceMember()) - return true; + Type capturedBaseType = baseType; - // For instance members we need to check whether instance type - // is Sendable because @Sendable function values cannot capture - // non-Sendable values (base instance type in this case). - // For example, `(C) -> () -> Void` where `C` should be Sendable - // for the inner function type to be Sendable as well. - return baseType && - baseType->getMetatypeInstanceType()->isSendableType(); + if (!decl->isInstanceMember()) { + // Static member types are Sendable when the metatype of their + // base type is Sendable, because they capture that metatype. + // For example, `(S.Type) -> () -> Void`. + if (!capturedBaseType) + capturedBaseType = decl->getDeclContext()->getSelfTypeInContext(); + + if (!capturedBaseType->is()) + capturedBaseType = MetatypeType::get(capturedBaseType); + } else if (capturedBaseType) { + // For instance members we need to check whether instance type + // is Sendable because @Sendable function values cannot capture + // non-Sendable values (base instance type in this case). + // For example, `(C) -> () -> Void` where `C` should be Sendable + // for the inner function type to be Sendable as well. + capturedBaseType = capturedBaseType->getMetatypeInstanceType(); + } + + return capturedBaseType && capturedBaseType->isSendableType(); }; auto referenceTy = adjustedTy->getResult()->castTo(); @@ -1230,8 +1238,8 @@ void ConstraintSystem::openGenericRequirement( // Check whether the given type parameter has requirements that // prohibit it from using an isolated conformance. - if (typeParameterProhibitsIsolatedConformance(req.getFirstType(), - signature)) + if (signature && + signature->prohibitsIsolatedConformance(req.getFirstType())) prohibitIsolatedConformance = true; openedReq = Requirement(kind, openedFirst, req.getSecondType()); @@ -1549,6 +1557,13 @@ DeclReferenceType ConstraintSystem::getTypeOfMemberReference( // Wrap it in a metatype. memberTy = MetatypeType::get(memberTy); + // If this is a value generic, undo the wrapping. 'substMemberTypeWithBase' + // returns the underlying value type of the value generic (e.g. 'Int'). + if (isa(value) && + cast(value)->isValue()) { + memberTy = memberTy->castTo()->getInstanceType(); + } + auto openedType = FunctionType::get({baseObjParam}, memberTy); return { openedType, openedType, memberTy, memberTy, Type() }; } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index d09f4c0d026..65fb03a81e3 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -1058,8 +1058,11 @@ ProtocolConformanceDeserializer::readNormalProtocolConformance( auto globalActorType = globalActorTypeOrError.get(); TypeExpr *globalActorTypeExpr = nullptr; - if (globalActorType) + if (globalActorType) { globalActorTypeExpr = TypeExpr::createImplicit(globalActorType, ctx); + rawOptions |= + static_cast(ProtocolConformanceFlags::GlobalActorIsolated); + } auto conformance = ctx.getNormalConformance( conformingType, proto, SourceLoc(), dc, @@ -4107,6 +4110,7 @@ public: bool isIsolated; bool isCompileTimeLiteral, isConstValue; bool isSending; + bool isCallerIsolated; uint8_t rawDefaultArg; TypeID defaultExprType; uint8_t rawDefaultArgIsolation; @@ -4119,6 +4123,7 @@ public: isCompileTimeLiteral, isConstValue, isSending, + isCallerIsolated, rawDefaultArg, defaultExprType, rawDefaultArgIsolation, @@ -4164,6 +4169,7 @@ public: param->setCompileTimeLiteral(isCompileTimeLiteral); param->setConstValue(isConstValue); param->setSending(isSending); + param->setCallerIsolated(isCallerIsolated); // Decode the default argument kind. // FIXME: Default argument expression, if available. @@ -5922,14 +5928,6 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { DeclAttribute *Attr = nullptr; bool skipAttr = false; switch (recordID) { - case decls_block::Execution_DECL_ATTR: { - unsigned behavior; - serialization::decls_block::ExecutionDeclAttrLayout::readRecord( - scratch, behavior); - Attr = new (ctx) ExecutionAttr(static_cast(behavior), - /*Implicit=*/false); - break; - } case decls_block::ABI_DECL_ATTR: { bool isImplicit; DeclID abiDeclID; @@ -6514,11 +6512,12 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { } case decls_block::Nonisolated_DECL_ATTR: { - bool isUnsafe{}; + unsigned modifier; bool isImplicit{}; serialization::decls_block::NonisolatedDeclAttrLayout::readRecord( - scratch, isUnsafe, isImplicit); - Attr = new (ctx) NonisolatedAttr(isUnsafe, isImplicit); + scratch, modifier, isImplicit); + Attr = new (ctx) NonisolatedAttr( + {}, {}, static_cast(modifier), isImplicit); break; } diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index c05eaa04e96..b57802b4728 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 939; // cdecl isUnderscored +const uint16_t SWIFTMODULE_VERSION_MINOR = 943; // cdecl isUnderscored /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1728,6 +1728,7 @@ namespace decls_block { BCFixed<1>, // isCompileTimeLiteral? BCFixed<1>, // isConst? BCFixed<1>, // isSending? + BCFixed<1>, // isCallerIsolated? DefaultArgumentField, // default argument kind TypeIDField, // default argument type ActorIsolationField, // default argument isolation @@ -2389,11 +2390,6 @@ namespace decls_block { BCFixed<2> // exclusivity mode >; - using ExecutionDeclAttrLayout = BCRecordLayout< - Execution_DECL_ATTR, - BCFixed<1> // execution behavior kind - >; - using ABIDeclAttrLayout = BCRecordLayout< ABI_DECL_ATTR, BCFixed<1>, // implicit flag @@ -2552,7 +2548,7 @@ namespace decls_block { using NonisolatedDeclAttrLayout = BCRecordLayout, // is the argument (unsafe) + BCFixed<2>, // the modifier (unsafe, nonsending) BCFixed<1> // implicit flag >; diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 52f4da3c602..afacf625fe6 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2938,15 +2938,6 @@ class Serializer::DeclSerializer : public DeclVisitor { } #include "swift/AST/DeclAttr.def" - case DeclAttrKind::Execution: { - auto *theAttr = cast(DA); - auto abbrCode = S.DeclTypeAbbrCodes[ExecutionDeclAttrLayout::Code]; - ExecutionDeclAttrLayout::emitRecord( - S.Out, S.ScratchRecord, abbrCode, - static_cast(theAttr->getBehavior())); - return; - } - case DeclAttrKind::ABI: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[ABIDeclAttrLayout::Code]; @@ -3472,9 +3463,9 @@ class Serializer::DeclSerializer : public DeclVisitor { case DeclAttrKind::Nonisolated: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[NonisolatedDeclAttrLayout::Code]; - NonisolatedDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, - theAttr->isUnsafe(), - theAttr->isImplicit()); + NonisolatedDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, + static_cast(theAttr->getModifier()), theAttr->isImplicit()); return; } @@ -4755,6 +4746,7 @@ public: param->isCompileTimeLiteral(), param->isConstVal(), param->isSending(), + param->isCallerIsolated(), getRawStableDefaultArgumentKind(argKind), S.addTypeRef(defaultExprType), getRawStableActorIsolationKind(isolation.getKind()), diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index 993c47bb8af..10adbb6de68 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -697,6 +697,26 @@ void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { llvm::StringMap Availabilities; getInheritedAvailabilities(D, Availabilities); + // If we were asked to filter the availability platforms for the output graph, + // perform that filtering here. + if (Graph->Walker.Options.AvailabilityPlatforms) { + auto AvailabilityPlatforms = + Graph->Walker.Options.AvailabilityPlatforms.value(); + if (Graph->Walker.Options.AvailabilityIsBlockList) { + for (const auto Availability : Availabilities.keys()) { + if (Availability != "*" && AvailabilityPlatforms.contains(Availability)) { + Availabilities.erase(Availability); + } + } + } else { + for (const auto Availability : Availabilities.keys()) { + if (Availability != "*" && !AvailabilityPlatforms.contains(Availability)) { + Availabilities.erase(Availability); + } + } + } + } + if (Availabilities.empty()) { return; } diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index b149ee96951..28829186bc4 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -932,11 +932,14 @@ function(add_swift_target_library_single target name) endif() endif() - # FIXME: swiftDarwin currently trips an assertion in SymbolGraphGen - if (SWIFTLIB_IS_STDLIB AND SWIFT_STDLIB_BUILD_SYMBOL_GRAPHS AND NOT ${name} STREQUAL "swiftDarwin") + # FIXME: swiftDarwin and swiftDifferentiationUnittest currently trip an assertion in SymbolGraphGen + if (SWIFTLIB_IS_STDLIB AND SWIFT_STDLIB_BUILD_SYMBOL_GRAPHS AND NOT ${name} STREQUAL "swiftDarwin" + AND NOT ${name} STREQUAL "swiftDifferentiationUnittest") list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS "-Xfrontend;-emit-symbol-graph") list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS "-Xfrontend;-emit-symbol-graph-dir;-Xfrontend;${out_lib_dir}/symbol-graph/${VARIANT_NAME}") + list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS + "-Xfrontend;-symbol-graph-allow-availability-platforms;-Xfrontend;Swift") endif() if(MODULE) diff --git a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def index 40cd713e4f6..270349ffc01 100644 --- a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def +++ b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def @@ -441,7 +441,8 @@ OVERRIDE_TASK(task_startOnMainActor, void, // In ACTOR since we need ExecutorTracking info OVERRIDE_ACTOR(task_startSynchronously, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, (AsyncTask *task), (task)) + swift::, (AsyncTask *task, SerialExecutorRef targetExecutor), + (task, targetExecutor)) #undef OVERRIDE #undef OVERRIDE_ACTOR diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index 99f9dda0066..ed4b39fd72c 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -506,7 +506,13 @@ extern "C" SWIFT_CC(swift) void _swift_task_enqueueOnExecutor( static swift_task_is_current_executor_flag _getIsolationCheckingOptionsFromExecutorWitnessTable(const SerialExecutorWitnessTable *_wtable) { const WitnessTable* wtable = reinterpret_cast(_wtable); +#if SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES + auto description = lookThroughOptionalConditionalWitnessTable( + reinterpret_cast(wtable)) + ->getDescription(); +#else auto description = wtable->getDescription(); +#endif if (!description) { return swift_task_is_current_executor_flag::None; } @@ -2531,22 +2537,31 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex } SWIFT_CC(swift) -static void swift_task_startSynchronouslyImpl(AsyncTask* task) { +static void +swift_task_startSynchronouslyImpl(AsyncTask *task, + SerialExecutorRef targetExecutor) { swift_retain(task); + if (targetExecutor.isGeneric()) { + // If the target is generic, it means that the closure did not specify + // an isolation explicitly. According to the "start synchronously" rules, + // we should therefore ignore the global and just start running on the + // caller immediately. + SerialExecutorRef executor = SerialExecutorRef::forSynchronousStart(); - auto currentTracking = ExecutorTrackingInfo::current(); - if (currentTracking) { - auto currentExecutor = currentTracking->getActiveExecutor(); - AsyncTask * originalTask = _swift_task_clearCurrent(); - - swift_job_run(task, currentExecutor); - _swift_task_setCurrent(originalTask); + auto originalTask = ActiveTask::swap(task); + swift_job_run(task, executor); + _swift_task_setCurrent(originalTask); } else { - auto originalTask = ActiveTask::swap(task); - assert(!originalTask); + assert(swift_task_isCurrentExecutor(targetExecutor) && + "startSynchronously must only be invoked when it is correctly in " + "the same isolation already, but wasn't!"); - SerialExecutorRef executor = SerialExecutorRef::forSynchronousStart(); - swift_job_run(task, executor); + // We can run synchronously, we're on the expected executor so running in + // the caller context is going to be in the same context as the requested + // "caller" context. + AsyncTask *originalTask = _swift_task_clearCurrent(); + + swift_job_run(task, targetExecutor); _swift_task_setCurrent(originalTask); } } diff --git a/stdlib/public/Concurrency/AsyncLet.cpp b/stdlib/public/Concurrency/AsyncLet.cpp index 7764eb17df4..b95a2756de5 100644 --- a/stdlib/public/Concurrency/AsyncLet.cpp +++ b/stdlib/public/Concurrency/AsyncLet.cpp @@ -24,9 +24,9 @@ #include "swift/ABI/Metadata.h" #include "swift/ABI/Task.h" #include "swift/ABI/TaskOptions.h" +#include "swift/Basic/Casting.h" #include "swift/Runtime/Heap.h" #include "swift/Runtime/HeapObject.h" -#include "swift/Runtime/STLCompatibility.h" #include "swift/Threading/Mutex.h" #include "llvm/ADT/PointerIntPair.h" @@ -289,7 +289,7 @@ static void _asyncLet_get_throwing_continuation( // Continue the caller's execution. auto throwingResume = - std::bit_cast(callContext->ResumeParent); + function_cast(callContext->ResumeParent); return throwingResume(callContext->Parent, error); } @@ -307,7 +307,7 @@ static void swift_asyncLet_get_throwingImpl( auto aletContext = static_cast(callContext); aletContext->ResumeParent = - std::bit_cast(resumeFunction); + function_cast(resumeFunction); aletContext->Parent = callerContext; aletContext->alet = alet; auto futureContext = asImpl(alet)->getFutureContext(); @@ -377,7 +377,7 @@ static void asyncLet_finish_after_task_completion(SWIFT_ASYNC_CONTEXT AsyncConte swift_task_dealloc(task); } - return std::bit_cast(resumeFunction) + return function_cast(resumeFunction) (callerContext, error); } @@ -529,14 +529,14 @@ static void swift_asyncLet_consume_throwingImpl( if (asImpl(alet)->hasResultInBuffer()) { return asyncLet_finish_after_task_completion(callerContext, alet, - std::bit_cast(resumeFunction), + function_cast(resumeFunction), callContext, nullptr); } auto aletContext = static_cast(callContext); aletContext->ResumeParent = - std::bit_cast(resumeFunction); + function_cast(resumeFunction); aletContext->Parent = callerContext; aletContext->alet = alet; auto futureContext = asImpl(alet)->getFutureContext(); diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 9a58dd11238..5b2b022930b 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -272,31 +272,23 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC list(GET list 1 mod) list(GET list 2 triple) + set(extra_c_compile_flags) + set(extra_swift_compile_flags) + if (SWIFT_HOST_VARIANT STREQUAL "linux") if(NOT "${mod}" MATCHES "-linux-gnu$") continue() endif() - set(extra_c_compile_flags) - set(extra_swift_compile_flags) elseif (SWIFT_HOST_VARIANT STREQUAL "macosx") - if(NOT "${mod}" MATCHES "-macos$") - continue() - endif() - if("${mod}" MATCHES "riscv") - continue() - endif() - if("${mod}" MATCHES "armv6m") + if(NOT "${mod}" MATCHES "x86_64|arm64|arm64e|armv7|armv7m|armv7em") continue() endif() - if("${mod}" MATCHES "-macos$") - set(extra_c_compile_flags -ffreestanding -stdlib=libc++) - set(extra_swift_compile_flags -Xcc -ffreestanding) + if(NOT "${mod}" MATCHES "-apple-" OR "${mod}" MATCHES "-none-macho") + # Host is macOS with a macOS SDK. To be able to build the C++ Concurrency runtime for non-Darwin targets using the macOS SDK, + # we need to pass some extra flags and search paths. + set(extra_c_compile_flags -stdlib=libc++ -isystem${SWIFT_SDK_OSX_PATH}/usr/include/c++/v1 -isystem${SWIFT_SDK_OSX_PATH}/usr/include -D__APPLE__) endif() - - elseif (SWIFT_HOST_VARIANT STREQUAL "wasi") - set(extra_c_compile_flags) - set(extra_swift_compile_flags) endif() set(SWIFT_SDK_embedded_THREADING_PACKAGE none) diff --git a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp index b5face3c0fb..82e5d6c3586 100644 --- a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp +++ b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp @@ -30,9 +30,9 @@ #include +#include "swift/Basic/Casting.h" #include "swift/Runtime/Concurrency.h" #include "swift/Runtime/EnvironmentVariables.h" -#include "swift/Runtime/STLCompatibility.h" #if SWIFT_CONCURRENCY_ENABLE_DISPATCH #include "swift/Runtime/HeapObject.h" @@ -99,11 +99,11 @@ static void initializeDispatchEnqueueFunc(dispatch_queue_t queue, void *obj, if (SWIFT_RUNTIME_WEAK_CHECK(dispatch_async_swift_job)) func = SWIFT_RUNTIME_WEAK_USE(dispatch_async_swift_job); #elif defined(_WIN32) - func = std::bit_cast( + func = function_cast( GetProcAddress(LoadLibraryW(L"dispatch.dll"), "dispatch_async_swift_job")); #else - func = std::bit_cast( + func = function_cast( dlsym(RTLD_NEXT, "dispatch_async_swift_job")); #endif #endif diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index af6991eda55..49de9ead325 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -534,6 +534,9 @@ public protocol ExecutorFactory { static var defaultExecutor: any TaskExecutor { get } } +@available(SwiftStdlib 6.2, *) +typealias DefaultExecutorFactory = PlatformExecutorFactory + @available(SwiftStdlib 6.2, *) @_silgen_name("swift_createExecutors") public func _createExecutors(factory: F.Type) { @@ -556,7 +559,7 @@ extension MainActor { @available(SwiftStdlib 6.2, *) public static var executor: any MainExecutor { if _executor == nil { - _executor = PlatformExecutorFactory.mainExecutor + _executor = DefaultExecutorFactory.mainExecutor } return _executor! } @@ -575,7 +578,7 @@ extension Task where Success == Never, Failure == Never { @available(SwiftStdlib 6.2, *) public static var defaultExecutor: any TaskExecutor { if _defaultExecutor == nil { - _defaultExecutor = PlatformExecutorFactory.defaultExecutor + _defaultExecutor = DefaultExecutorFactory.defaultExecutor } return _defaultExecutor! } diff --git a/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb b/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb index 30c55da989f..c2331fdf6c8 100644 --- a/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb +++ b/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb @@ -59,14 +59,30 @@ extension Task where Failure == ${FAILURE_TYPE} { public static func startSynchronously( name: String? = nil, priority: TaskPriority? = nil, - @_inheritActorContext @_implicitSelfCapture _ operation: __owned sending @escaping () async throws -> Success + % # NOTE: This closure cannot be 'sending' because we'll trigger ' pattern that the region based isolation checker does not understand how to check' + % # In this case: `func syncOnMyGlobalActor() { Task.startSynchronously { @MyGlobalActor in } }` + @_implicitSelfCapture _ operation: __owned @isolated(any) @escaping () async throws -> Success ) -> Task { + + let builtinSerialExecutor = + unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + // Determine if we're switching isolation dynamically. + // If not, we can run the task synchronously and therefore MUST NOT "enqueue" it. + let flagsMustNotCrash: UInt64 = 0 + let canRunSynchronously: Bool = + if let builtinSerialExecutor { + _taskIsCurrentExecutor(executor: builtinSerialExecutor, flags: flagsMustNotCrash) + } else { + true // if there is not target executor, we can run synchronously + } + let flags = taskCreateFlags( priority: priority, isChildTask: false, copyTaskLocals: true, inheritContext: true, - enqueueJob: false, // don't enqueue, we'll run it manually + enqueueJob: !canRunSynchronously, addPendingGroupTaskUnconditionally: false, isDiscardingTask: false, isSynchronousStart: true @@ -79,6 +95,7 @@ extension Task where Failure == ${FAILURE_TYPE} { unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in Builtin.createTask( flags: flags, + initialSerialExecutor: builtinSerialExecutor, taskName: nameBytes.baseAddress!._rawValue, operation: operation).0 } @@ -91,7 +108,9 @@ extension Task where Failure == ${FAILURE_TYPE} { operation: operation).0 } - _startTaskSynchronously(task!) + if canRunSynchronously { + _startTaskSynchronously(task!, targetExecutor: builtinSerialExecutor) + } return Task(task!) } } @@ -161,7 +180,7 @@ extension ${GROUP_TYPE} { public func ${METHOD_NAME}( // in ${GROUP_TYPE} name: String? = nil, priority: TaskPriority? = nil, - operation: sending @escaping () async ${THROWS}-> ${RESULT_TYPE} + @_inheritActorContext @_implicitSelfCapture operation: sending @isolated(any) @escaping () async ${THROWS}-> ${RESULT_TYPE} ) { let flags = taskCreateFlags( priority: priority, @@ -174,13 +193,18 @@ extension ${GROUP_TYPE} { isSynchronousStart: true ) + // Create the asynchronous task. + let builtinSerialExecutor = + unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + // Create the task in this group. let (task, _) = Builtin.createTask( flags: flags, + initialSerialExecutor: builtinSerialExecutor, taskGroup: self._group, operation: operation ) - _startTaskSynchronously(task) + _startTaskSynchronously(task, targetExecutor: builtinSerialExecutor) } } % end # METHOD_NAMES @@ -241,4 +265,4 @@ extension Task where Failure == ${FAILURE_TYPE} { internal func _startTaskOnMainActor(_ task: Builtin.NativeObject) @_silgen_name("swift_task_startSynchronously") -internal func _startTaskSynchronously(_ task: Builtin.NativeObject) +internal func _startTaskSynchronously(_ task: Builtin.NativeObject, targetExecutor: Builtin.Executor?) diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index bf81095d846..8fcf079de8c 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -30,6 +30,7 @@ #include "swift/ABI/Metadata.h" #include "swift/ABI/Task.h" #include "swift/ABI/TaskOptions.h" +#include "swift/Basic/Casting.h" #include "swift/Basic/Lazy.h" #include "swift/Runtime/Concurrency.h" #include "swift/Runtime/EnvironmentVariables.h" @@ -1075,19 +1076,19 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags, // The final funclet shouldn't release the task or the task function. } else if (asyncLet) { initialContext->ResumeParent = - reinterpret_cast(&completeTask); + function_cast(&completeTask); // If we have a non-null closure context and the task function is not // consumed by calling it, use a final funclet that releases both the // task and the closure context. } else if (closureContext && !taskCreateFlags.isTaskFunctionConsumed()) { initialContext->ResumeParent = - reinterpret_cast(&completeTaskWithClosure); + function_cast(&completeTaskWithClosure); // Otherwise, just release the task. } else { initialContext->ResumeParent = - reinterpret_cast(&completeTaskAndRelease); + function_cast(&completeTaskAndRelease); } #pragma clang diagnostic pop @@ -1799,8 +1800,7 @@ static void swift_task_removeCancellationHandlerImpl( auto task = swift_task_getCurrent(); assert(task->_private()._status().load(std::memory_order_relaxed).getInnermostRecord() == record && "We expect that the popped record will be exactly first as well as that it is of the expected type"); - if (auto poppedRecord = - popStatusRecordOfType(task)) { + if (popStatusRecordOfType(task)) { swift_task_dealloc(record); } } diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 044571a82a7..2709e66ba6d 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -807,8 +807,7 @@ extension Task where Failure == Error { unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor let (task, _) = Builtin.createTask(flags: flags, - initialSerialExecutor: - builtinSerialExecutor, + initialSerialExecutor: builtinSerialExecutor, operation: operation) self._task = task diff --git a/stdlib/public/Concurrency/TaskGroup.cpp b/stdlib/public/Concurrency/TaskGroup.cpp index 6de5d06c8e3..9cc8f4ea071 100644 --- a/stdlib/public/Concurrency/TaskGroup.cpp +++ b/stdlib/public/Concurrency/TaskGroup.cpp @@ -27,13 +27,13 @@ #include "swift/ABI/Task.h" #include "swift/ABI/TaskGroup.h" #include "swift/ABI/TaskOptions.h" +#include "swift/Basic/Casting.h" #include "swift/Basic/RelativePointer.h" #include "swift/Basic/STLExtras.h" #include "swift/Runtime/Concurrency.h" #include "swift/Runtime/Config.h" #include "swift/Runtime/Heap.h" #include "swift/Runtime/HeapObject.h" -#include "swift/Runtime/STLCompatibility.h" #include "swift/Threading/Mutex.h" #include #include @@ -1166,6 +1166,10 @@ bool TaskGroup::isCancelled() { return asBaseImpl(this)->isCancelled(); } +bool TaskGroup::statusCancel() { + return asBaseImpl(this)->statusCancel(); +} + // ============================================================================= // ==== offer ------------------------------------------------------------------ @@ -1661,7 +1665,7 @@ task_group_wait_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *_context) { auto context = static_cast(_context); auto resumeWithError = - std::bit_cast(context->ResumeParent); + function_cast(context->ResumeParent); return resumeWithError(context->Parent, context->errorResult); } @@ -1713,7 +1717,7 @@ static void swift_taskGroup_wait_next_throwingImpl( auto context = static_cast(rawContext); context->ResumeParent = - std::bit_cast(resumeFunction); + function_cast(resumeFunction); context->Parent = callerContext; context->errorResult = nullptr; context->successResultPointer = resultPointer; @@ -1945,7 +1949,7 @@ void TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask, auto context = static_cast(rawContext); context->ResumeParent = - std::bit_cast(resumeFunction); + function_cast(resumeFunction); context->Parent = callerContext; context->errorResult = nullptr; context->successResultPointer = resultPointer; @@ -2116,8 +2120,8 @@ bool TaskGroupBase::cancelAll(AsyncTask *owningTask) { // Cancel all the child tasks. TaskGroup is not a Sendable type, // so cancelAll() can only be called from the owning task. This - // satisfies the precondition on cancelAllChildren_unlocked(). - _swift_taskGroup_cancelAllChildren_unlocked(asAbstract(this), owningTask); + // satisfies the precondition on cancel_unlocked(). + _swift_taskGroup_cancel_unlocked(asAbstract(this), owningTask); return true; } @@ -2126,8 +2130,8 @@ SWIFT_CC(swift) static void swift_task_cancel_group_child_tasksImpl(TaskGroup *group) { // TaskGroup is not a Sendable type, and so this operation (which is not // currently exposed in the API) can only be called from the owning - // task. This satisfies the precondition on cancelAllChildren_unlocked(). - _swift_taskGroup_cancelAllChildren_unlocked(group, swift_task_getCurrent()); + // task. This satisfies the precondition on cancel_unlocked(). + _swift_taskGroup_cancel_unlocked(group, swift_task_getCurrent()); } // ============================================================================= diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h index 2235e7a687b..b33be59fa54 100644 --- a/stdlib/public/Concurrency/TaskPrivate.h +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -89,16 +89,16 @@ AsyncTask *_swift_task_clearCurrent(); /// Set the active task reference for the current thread. AsyncTask *_swift_task_setCurrent(AsyncTask *newTask); -/// Cancel all the child tasks that belong to `group`. +/// Cancel the task group and all the child tasks that belong to `group`. /// /// The caller must guarantee that this is called while holding the owning /// task's status record lock. -void _swift_taskGroup_cancelAllChildren(TaskGroup *group); +void _swift_taskGroup_cancel(TaskGroup *group); -/// Cancel all the child tasks that belong to `group`. +/// Cancel the task group and all the child tasks that belong to `group`. /// /// The caller must guarantee that this is called from the owning task. -void _swift_taskGroup_cancelAllChildren_unlocked(TaskGroup *group, +void _swift_taskGroup_cancel_unlocked(TaskGroup *group, AsyncTask *owningTask); /// Remove the given task from the given task group. @@ -766,9 +766,11 @@ struct AsyncTask::PrivateStorage { alignas(ActiveTaskStatus) char StatusStorage[sizeof(ActiveTaskStatus)]; /// The allocator for the task stack. - /// Currently 2 words + 8 bytes. + /// Currently 2 words + 4 bytes. TaskAllocator Allocator; + // Four bytes of padding here (on 64-bit) + /// Storage for task-local values. /// Currently one word. TaskLocal::Storage Local; @@ -776,6 +778,8 @@ struct AsyncTask::PrivateStorage { /// The top 32 bits of the task ID. The bottom 32 bits are in Job::Id. uint32_t Id; + // Another four bytes of padding here too (on 64-bit) + /// Base priority of Task - set only at creation time of task. /// Current max priority of task is ActiveTaskStatus. /// diff --git a/stdlib/public/Concurrency/TaskStatus.cpp b/stdlib/public/Concurrency/TaskStatus.cpp index 38cc28b4553..12e5b918705 100644 --- a/stdlib/public/Concurrency/TaskStatus.cpp +++ b/stdlib/public/Concurrency/TaskStatus.cpp @@ -776,11 +776,13 @@ void swift::_swift_taskGroup_detachChild(TaskGroup *group, }); } -/// Cancel all the child tasks that belong to `group`. +/// Cancel the task group and all the child tasks that belong to `group`. /// /// The caller must guarantee that this is called while holding the owning /// task's status record lock. -void swift::_swift_taskGroup_cancelAllChildren(TaskGroup *group) { +void swift::_swift_taskGroup_cancel(TaskGroup *group) { + (void) group->statusCancel(); + // Because only the owning task of the task group can modify the // child list of a task group status record, and it can only do so // while holding the owning task's status record lock, we do not need @@ -789,10 +791,10 @@ void swift::_swift_taskGroup_cancelAllChildren(TaskGroup *group) { swift_task_cancel(childTask); } -/// Cancel all the child tasks that belong to `group`. +/// Cancel the task group and all the child tasks that belong to `group`. /// /// The caller must guarantee that this is called from the owning task. -void swift::_swift_taskGroup_cancelAllChildren_unlocked(TaskGroup *group, +void swift::_swift_taskGroup_cancel_unlocked(TaskGroup *group, AsyncTask *owningTask) { // Early out. If there are no children, there's nothing to do. We can safely // check this without locking, since this can only be concurrently mutated @@ -801,7 +803,7 @@ void swift::_swift_taskGroup_cancelAllChildren_unlocked(TaskGroup *group, return; withStatusRecordLock(owningTask, [&group](ActiveTaskStatus status) { - _swift_taskGroup_cancelAllChildren(group); + _swift_taskGroup_cancel(group); }); } @@ -825,7 +827,7 @@ static void performCancellationAction(TaskStatusRecord *record) { // under the synchronous control of the task that owns the group. case TaskStatusRecordKind::TaskGroup: { auto groupRecord = cast(record); - _swift_taskGroup_cancelAllChildren(groupRecord->getGroup()); + _swift_taskGroup_cancel(groupRecord->getGroup()); return; } diff --git a/stdlib/public/Distributed/DistributedActor.cpp b/stdlib/public/Distributed/DistributedActor.cpp index 83a30738c76..8596288a714 100644 --- a/stdlib/public/Distributed/DistributedActor.cpp +++ b/stdlib/public/Distributed/DistributedActor.cpp @@ -17,9 +17,9 @@ #include "swift/ABI/Actor.h" #include "swift/ABI/Metadata.h" #include "swift/ABI/Task.h" +#include "swift/Basic/Casting.h" #include "swift/Runtime/AccessibleFunction.h" #include "swift/Runtime/Concurrency.h" -#include "swift/Runtime/STLCompatibility.h" using namespace swift; @@ -103,7 +103,7 @@ static void swift_distributed_execute_target_resume( SWIFT_CONTEXT SwiftError *error) { auto parentCtx = context->Parent; auto resumeInParent = - std::bit_cast( + function_cast( parentCtx->ResumeParent); swift_task_dealloc(context); // See `swift_distributed_execute_target` - `parentCtx` in this case @@ -132,7 +132,7 @@ void swift_distributed_execute_target( SwiftError *error = swift_distributed_makeDistributedTargetAccessorNotFoundError(); auto resumeInParent = - std::bit_cast( + function_cast( callerContext->ResumeParent); resumeInParent(callerContext, error); return; @@ -150,7 +150,7 @@ void swift_distributed_execute_target( swift_task_alloc(asyncFnPtr->ExpectedContextSize)); calleeContext->Parent = callerContext; - calleeContext->ResumeParent = std::bit_cast( + calleeContext->ResumeParent = function_cast( &swift_distributed_execute_target_resume); accessorEntry(calleeContext, argumentDecoder, argumentTypes, resultBuffer, diff --git a/stdlib/public/Synchronization/Cell.swift b/stdlib/public/Synchronization/Cell.swift index 75dde59cdcf..c169ac7b5fa 100644 --- a/stdlib/public/Synchronization/Cell.swift +++ b/stdlib/public/Synchronization/Cell.swift @@ -14,13 +14,12 @@ import Builtin @available(SwiftStdlib 6.0, *) @frozen -@usableFromInline @_rawLayout(like: Value, movesAsLike) -internal struct _Cell: ~Copyable { +public struct _Cell: ~Copyable { @available(SwiftStdlib 6.0, *) @_alwaysEmitIntoClient @_transparent - internal var _address: UnsafeMutablePointer { + public var _address: UnsafeMutablePointer { unsafe UnsafeMutablePointer(_rawAddress) } @@ -34,7 +33,7 @@ internal struct _Cell: ~Copyable { @available(SwiftStdlib 6.0, *) @_alwaysEmitIntoClient @_transparent - internal init(_ initialValue: consuming Value) { + public init(_ initialValue: consuming Value) { unsafe _address.initialize(to: initialValue) } diff --git a/stdlib/public/Synchronization/Mutex/WasmImpl.swift b/stdlib/public/Synchronization/Mutex/WasmImpl.swift index 6c58628c40e..49d89c4fc22 100644 --- a/stdlib/public/Synchronization/Mutex/WasmImpl.swift +++ b/stdlib/public/Synchronization/Mutex/WasmImpl.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -// Note: All atomic accesses on WASM are sequentially consistent regardless of +// Note: All atomic accesses on Wasm are sequentially consistent regardless of // what ordering we tell LLVM to use. @_extern(c, "llvm.wasm.memory.atomic.wait32") diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 94a3b28efb4..76c6eeea6e4 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -214,6 +214,13 @@ split_embedded_sources( EMBEDDED UnsafeRawPointer.swift EMBEDDED UTFEncoding.swift EMBEDDED UTF8.swift + EMBEDDED UTF8EncodingError.swift + EMBEDDED UTF8Span.swift + EMBEDDED UTF8SpanBits.swift + EMBEDDED UTF8SpanComparisons.swift + EMBEDDED UTF8SpanFundamentals.swift + EMBEDDED UTF8SpanInternalHelpers.swift + EMBEDDED UTF8SpanIterators.swift EMBEDDED UTF16.swift EMBEDDED UTF32.swift EMBEDDED Unicode.swift # ORDER DEPENDENCY: must follow new unicode support diff --git a/stdlib/public/core/CollectionOfOne.swift b/stdlib/public/core/CollectionOfOne.swift index 702f2631326..4096bd80e4c 100644 --- a/stdlib/public/core/CollectionOfOne.swift +++ b/stdlib/public/core/CollectionOfOne.swift @@ -166,7 +166,22 @@ extension CollectionOfOne { @lifetime(borrow self) @_alwaysEmitIntoClient get { - fatalError("Span over CollectionOfOne is not supported yet.") + let pointer = unsafe UnsafePointer(Builtin.addressOfBorrow(self)) + let span = unsafe Span(_unsafeStart: pointer, count: 1) + return unsafe _overrideLifetime(span, borrowing: self) + } + } + + @available(SwiftStdlib 6.2, *) + public var mutableSpan: MutableSpan { + @lifetime(&self) + @_alwaysEmitIntoClient + mutating get { + let pointer = unsafe UnsafeMutablePointer( + Builtin.addressOfBorrow(self) + ) + let span = unsafe MutableSpan(_unsafeStart: pointer, count: 1) + return unsafe _overrideLifetime(span, mutating: &self) } } } diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index 655d20c95b0..f606571e3d0 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -205,6 +205,15 @@ "RawSpan.swift", "Span.swift" ], + "UTF8Span": [ + "UTF8EncodingError.swift", + "UTF8Span.swift", + "UTF8SpanBits.swift", + "UTF8SpanComparisons.swift", + "UTF8SpanFundamentals.swift", + "UTF8SpanInternalHelpers.swift", + "UTF8SpanIterators.swift" + ], "Protocols": [ "CompilerProtocols.swift", "ShadowProtocols.swift" diff --git a/stdlib/public/core/InlineArray.swift b/stdlib/public/core/InlineArray.swift index bfa15d4f853..15058a2c73e 100644 --- a/stdlib/public/core/InlineArray.swift +++ b/stdlib/public/core/InlineArray.swift @@ -272,14 +272,6 @@ extension InlineArray where Element: ~Copyable { @available(SwiftStdlib 6.2, *) public typealias Index = Int - // FIXME: Remove when SE-0452 "Integer Generic Parameters" is implemented. - @available(SwiftStdlib 6.2, *) - @_alwaysEmitIntoClient - @_transparent - public static var count: Int { - count - } - /// The number of elements in the array. /// /// - Complexity: O(1) @@ -468,7 +460,20 @@ extension InlineArray where Element: ~Copyable { @lifetime(borrow self) @_alwaysEmitIntoClient borrowing get { - fatalError("Span over InlineArray is not supported yet.") + let pointer = unsafe _address + let span = unsafe Span(_unsafeStart: pointer, count: count) + return unsafe _overrideLifetime(span, borrowing: self) + } + } + + @available(SwiftStdlib 6.2, *) + public var mutableSpan: MutableSpan { + @lifetime(&self) + @_alwaysEmitIntoClient + mutating get { + let pointer = unsafe _mutableAddress + let span = unsafe MutableSpan(_unsafeStart: pointer, count: count) + return unsafe _overrideLifetime(span, mutating: &self) } } } diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index 8f5653262fc..870d69023b8 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -3041,7 +3041,8 @@ extension UnsignedInteger where Self: FixedWidthInteger { /// - Parameter source: A value to convert to this type of integer. The value /// passed as `source` must be representable in this type. @_semantics("optimize.sil.specialize.generic.partial.never") - @_transparent + @inlinable // FIXME(inline-always) + @inline(__always) public init(_ source: T) { // This check is potentially removable by the optimizer if T.isSigned { @@ -3056,7 +3057,8 @@ extension UnsignedInteger where Self: FixedWidthInteger { } @_semantics("optimize.sil.specialize.generic.partial.never") - @_transparent + @inlinable // FIXME(inline-always) + @inline(__always) public init?(exactly source: T) { // This check is potentially removable by the optimizer if T.isSigned && source < (0 as T) { @@ -3254,7 +3256,8 @@ extension SignedInteger where Self: FixedWidthInteger { /// - Parameter source: A value to convert to this type of integer. The value /// passed as `source` must be representable in this type. @_semantics("optimize.sil.specialize.generic.partial.never") - @_transparent + @inlinable // FIXME(inline-always) + @inline(__always) public init(_ source: T) { // This check is potentially removable by the optimizer if T.isSigned && source.bitWidth > Self.bitWidth { @@ -3271,7 +3274,8 @@ extension SignedInteger where Self: FixedWidthInteger { } @_semantics("optimize.sil.specialize.generic.partial.never") - @_transparent + @inlinable // FIXME(inline-always) + @inline(__always) public init?(exactly source: T) { // This check is potentially removable by the optimizer if T.isSigned && source.bitWidth > Self.bitWidth && source < Self.min { diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index a46e786cb53..f32c461e8dd 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -18,7 +18,7 @@ import Swift // contains initialized `Element` instances. @safe @frozen -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) public struct MutableRawSpan: ~Copyable & ~Escapable { @usableFromInline @@ -45,11 +45,11 @@ public struct MutableRawSpan: ~Copyable & ~Escapable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableRawSpan: @unchecked Sendable {} -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableRawSpan { @@ -109,20 +109,21 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient - @lifetime(copy elements) + @lifetime(&elements) public init( - _elements elements: consuming MutableSpan + _elements elements: inout MutableSpan ) { - let bytes = unsafe UnsafeMutableRawBufferPointer( - start: elements._pointer, - count: elements.count &* MemoryLayout.stride + let (start, count) = unsafe (elements._pointer, elements._count) + let span = unsafe MutableRawSpan( + _unchecked: start, + byteCount: count == 1 ? MemoryLayout.size + : count &* MemoryLayout.stride ) - let span = unsafe MutableRawSpan(_unsafeBytes: bytes) - self = unsafe _overrideLifetime(span, copying: elements) + self = unsafe _overrideLifetime(span, mutating: &elements) } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableRawSpan { @_alwaysEmitIntoClient @@ -137,7 +138,7 @@ extension MutableRawSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableRawSpan { @@ -163,7 +164,7 @@ extension MutableRawSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { @@ -176,7 +177,7 @@ extension RawSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableRawSpan { @@ -213,7 +214,7 @@ extension MutableRawSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableRawSpan { @@ -355,7 +356,7 @@ extension MutableRawSpan { #if !SPAN_COMPATIBILITY_STUB //MARK: copyMemory -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableRawSpan { @@ -449,7 +450,7 @@ extension MutableRawSpan { #endif // MARK: sub-spans -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableRawSpan { @@ -565,7 +566,7 @@ extension MutableRawSpan { } // MARK: prefixes and suffixes -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableRawSpan { diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index 51c0a895463..a9e40ca8167 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -18,7 +18,7 @@ import Swift // contains initialized `Element` instances. @safe @frozen -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) public struct MutableSpan : ~Copyable, ~Escapable { @@ -46,11 +46,11 @@ public struct MutableSpan } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan: @unchecked Sendable where Element: Sendable {} -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { @@ -94,7 +94,7 @@ extension MutableSpan where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan { @@ -110,7 +110,7 @@ extension MutableSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: BitwiseCopyable { @@ -163,7 +163,7 @@ extension MutableSpan where Element: BitwiseCopyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: ~Copyable { @@ -180,7 +180,7 @@ extension Span where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { @@ -193,7 +193,7 @@ extension MutableSpan where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { @@ -210,7 +210,7 @@ extension RawSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { @@ -224,7 +224,7 @@ extension MutableSpan where Element: ~Copyable { } //MARK: Collection, RandomAccessCollection -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { @@ -242,7 +242,7 @@ extension MutableSpan where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: BitwiseCopyable { @@ -256,9 +256,20 @@ extension MutableSpan where Element: BitwiseCopyable { RawSpan(_mutableSpan: self) } } + + /// Construct a MutableRawSpan over the memory represented by this span + /// + /// - Returns: a MutableRawSpan over the memory represented by this span + @_alwaysEmitIntoClient + public var mutableBytes: MutableRawSpan { + @lifetime(&self) + mutating get { + MutableRawSpan(_elements: &self) + } + } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { @@ -312,7 +323,7 @@ extension MutableSpan where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { @@ -336,7 +347,7 @@ extension MutableSpan where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: BitwiseCopyable { @@ -386,7 +397,7 @@ extension MutableSpan where Element: BitwiseCopyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { @@ -418,7 +429,7 @@ extension MutableSpan where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: BitwiseCopyable { @@ -445,7 +456,7 @@ extension MutableSpan where Element: BitwiseCopyable { } //MARK: bulk-update functions -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan { @@ -531,7 +542,7 @@ extension MutableSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { @@ -565,7 +576,7 @@ extension MutableSpan where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan { @@ -578,7 +589,7 @@ extension MutableSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: BitwiseCopyable { @@ -673,7 +684,7 @@ extension MutableSpan where Element: BitwiseCopyable { } // MARK: sub-spans -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { @@ -792,7 +803,7 @@ extension MutableSpan where Element: ~Copyable { } // MARK: prefixes and suffixes -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension MutableSpan where Element: ~Copyable { diff --git a/stdlib/public/core/Span/RawSpan.swift b/stdlib/public/core/Span/RawSpan.swift index b9e549aefb9..3856faeb744 100644 --- a/stdlib/public/core/Span/RawSpan.swift +++ b/stdlib/public/core/Span/RawSpan.swift @@ -22,7 +22,7 @@ import Swift /// owning the contiguous memory, ensuring temporal safety and avoiding /// use-after-free errors. Operations on `RawSpan` are bounds-checked, /// ensuring spcial safety and avoiding buffer overflow errors. -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) @frozen @safe @@ -85,11 +85,11 @@ public struct RawSpan: ~Escapable, Copyable, BitwiseCopyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan: @unchecked Sendable {} -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { @@ -329,7 +329,7 @@ extension RawSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { @@ -359,7 +359,7 @@ extension RawSpan { } // MARK: extracting sub-spans -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { @@ -473,7 +473,7 @@ extension RawSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { @@ -504,7 +504,7 @@ extension RawSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { @@ -537,7 +537,7 @@ extension RawSpan { } // MARK: load -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { @@ -654,7 +654,7 @@ extension RawSpan { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { /// Returns a Boolean value indicating whether two `RawSpan` instances @@ -687,7 +687,7 @@ extension RawSpan { } // MARK: prefixes and suffixes -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension RawSpan { diff --git a/stdlib/public/core/Span/Span.swift b/stdlib/public/core/Span/Span.swift index 35d946804e9..e27b7ac4f07 100644 --- a/stdlib/public/core/Span/Span.swift +++ b/stdlib/public/core/Span/Span.swift @@ -24,7 +24,7 @@ import Swift /// ensuring spcial safety and avoiding buffer overflow errors. @frozen @safe -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) public struct Span: ~Escapable, Copyable, BitwiseCopyable { @@ -86,11 +86,11 @@ public struct Span: ~Escapable, Copyable, BitwiseCopyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span: @unchecked Sendable where Element: Sendable & ~Copyable {} -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: ~Copyable { @@ -168,7 +168,7 @@ extension Span where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span /*where Element: Copyable*/ { @@ -215,7 +215,7 @@ extension Span /*where Element: Copyable*/ { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: BitwiseCopyable { @@ -379,7 +379,7 @@ extension Span where Element: BitwiseCopyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: ~Copyable { @@ -412,7 +412,7 @@ extension Span where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: ~Copyable { @_semantics("fixed_storage.check_index") @@ -466,7 +466,7 @@ extension Span where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: BitwiseCopyable { /// Accesses the element at the specified position in the `Span`. @@ -517,7 +517,7 @@ extension Span where Element: BitwiseCopyable { } // MARK: sub-spans -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: ~Copyable { @@ -637,7 +637,7 @@ extension Span where Element: ~Copyable { } // MARK: UnsafeBufferPointer access hatch -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: ~Copyable { @@ -671,7 +671,7 @@ extension Span where Element: ~Copyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: BitwiseCopyable { @@ -704,7 +704,7 @@ extension Span where Element: BitwiseCopyable { } } -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: ~Copyable { /// Returns a Boolean value indicating whether two `Span` instances @@ -740,7 +740,7 @@ extension Span where Element: ~Copyable { } // MARK: prefixes and suffixes -@available(SwiftStdlib 5.0, *) +@available(SwiftCompatibilitySpan 5.0, *) @_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2) extension Span where Element: ~Copyable { diff --git a/stdlib/public/core/String.swift b/stdlib/public/core/String.swift index b4132341e89..e6715e91cc6 100644 --- a/stdlib/public/core/String.swift +++ b/stdlib/public/core/String.swift @@ -1112,108 +1112,4 @@ extension String { } } -extension _StringGutsSlice { - internal func _isScalarNFCQC( - _ scalar: Unicode.Scalar, - _ prevCCC: inout UInt8 - ) -> Bool { - let normData = Unicode._NormData(scalar, fastUpperbound: 0x300) - if prevCCC > normData.ccc, normData.ccc != 0 { - return false - } - - if !normData.isNFCQC { - return false - } - - prevCCC = normData.ccc - return true - } - - internal func _withNFCCodeUnits(_ f: (UInt8) throws -> Void) rethrows { - let substring = String(_guts)[range] - // Fast path: If we're already NFC (or ASCII), then we don't need to do - // anything at all. - if _fastPath(_guts.isNFC) { - try substring.utf8.forEach(f) - return - } - - var isNFCQC = true - var prevCCC: UInt8 = 0 - - if _guts.isFastUTF8 { - _fastNFCCheck(&isNFCQC, &prevCCC) - - // Because we have access to the fastUTF8, we can go through that instead - // of accessing the UTF8 view on String. - if isNFCQC { - try unsafe withFastUTF8 { - for unsafe byte in unsafe $0 { - try f(byte) - } - } - - return - } - } else { - for scalar in substring.unicodeScalars { - if !_isScalarNFCQC(scalar, &prevCCC) { - isNFCQC = false - break - } - } - - if isNFCQC { - for byte in substring.utf8 { - try f(byte) - } - - return - } - } - - for scalar in substring.unicodeScalars._internalNFC { - try scalar.withUTF8CodeUnits { - for unsafe byte in unsafe $0 { - try f(byte) - } - } - } - } - - internal func _fastNFCCheck(_ isNFCQC: inout Bool, _ prevCCC: inout UInt8) { - unsafe withFastUTF8 { utf8 in - var position = 0 - - while position < utf8.count { - // If our first byte is less than 0xCC, then it means we're under the - // 0x300 scalar value and everything up to 0x300 is NFC already. - if unsafe utf8[position] < 0xCC { - // If our first byte is less than 0xC0, then it means it is ASCII - // and only takes up a single byte. - if unsafe utf8[position] < 0xC0 { - position &+= 1 - } else { - // Otherwise, this is a 2 byte < 0x300 sequence. - position &+= 2 - } - // ASCII always has ccc of 0. - prevCCC = 0 - - continue - } - - let (scalar, len) = unsafe _decodeScalar(utf8, startingAt: position) - - if !_isScalarNFCQC(scalar, &prevCCC) { - isNFCQC = false - return - } - - position &+= len - } - } - } -} diff --git a/stdlib/public/core/StringComparison.swift b/stdlib/public/core/StringComparison.swift index e5a21c8208a..59f856662f4 100644 --- a/stdlib/public/core/StringComparison.swift +++ b/stdlib/public/core/StringComparison.swift @@ -97,7 +97,7 @@ internal func _stringCompareInternal( } @_effects(readonly) -private func _stringCompareFastUTF8( +internal func _stringCompareFastUTF8( _ utf8Left: UnsafeBufferPointer, _ utf8Right: UnsafeBufferPointer, expecting: _StringComparisonResult, diff --git a/stdlib/public/core/StringCreate.swift b/stdlib/public/core/StringCreate.swift index b11cdcb199d..2bdbfb48410 100644 --- a/stdlib/public/core/StringCreate.swift +++ b/stdlib/public/core/StringCreate.swift @@ -117,7 +117,7 @@ extension String { return unsafe (String._uncheckedFromUTF8( input, asciiPreScanResult: extraInfo.isASCII ), false) - case .error(let initialRange): + case .error(_, let initialRange): return unsafe (repairUTF8(input, firstKnownBrokenRange: initialRange), true) } } @@ -139,7 +139,7 @@ extension String { newIsASCII: info.isASCII ) return result.asString - case .error(let initialRange): + case .error(_, let initialRange): defer { _fixLifetime(result) } //This could be optimized to use excess tail capacity return unsafe repairUTF8(result.codeUnits, firstKnownBrokenRange: initialRange) diff --git a/stdlib/public/core/StringGraphemeBreaking.swift b/stdlib/public/core/StringGraphemeBreaking.swift index ddbadb24273..0574eb355be 100644 --- a/stdlib/public/core/StringGraphemeBreaking.swift +++ b/stdlib/public/core/StringGraphemeBreaking.swift @@ -13,14 +13,18 @@ import SwiftShims /// CR and LF are common special cases in grapheme breaking logic -private var _CR: UInt8 { return 0x0d } -private var _LF: UInt8 { return 0x0a } +private var _CR: UInt8 { return 0x0D } +private var _LF: UInt8 { return 0x0A } -internal func _hasGraphemeBreakBetween( +/// Perform a quick-check to determine if there's a grapheme-break between two +/// scalars, without consulting the data tables. Returns true if there +/// definitely is a break, false if there definitely is none, and nil if a +/// break couldn't be determined +internal func _quickHasGraphemeBreakBetween( _ lhs: Unicode.Scalar, _ rhs: Unicode.Scalar -) -> Bool { - - // CR-LF is a special case: no break between these +) -> Bool? { + // GB3: + // CR-LF is a special case: no break between these if lhs == Unicode.Scalar(_CR) && rhs == Unicode.Scalar(_LF) { return false } @@ -80,7 +84,10 @@ internal func _hasGraphemeBreakBetween( default: return false } } - return hasBreakWhenPaired(lhs) && hasBreakWhenPaired(rhs) + if hasBreakWhenPaired(lhs) && hasBreakWhenPaired(rhs) { + return true + } + return nil } extension _StringGuts { @@ -513,6 +520,8 @@ extension Unicode { internal var _previous: Unicode.Scalar internal var _state: _GraphemeBreakingState + /// Refactoring TODO: should we use a quick check result? + /// /// Returns a non-nil value if it can be determined whether there is a /// grapheme break between `scalar1` and `scalar2` without knowing anything /// about the scalars that precede `scalar1`. This can optionally be used as @@ -523,13 +532,7 @@ extension Unicode { between scalar1: Unicode.Scalar, and scalar2: Unicode.Scalar ) -> Bool? { - if scalar1.value == 0xD, scalar2.value == 0xA { - return false - } - if _hasGraphemeBreakBetween(scalar1, scalar2) { - return true - } - return nil + _quickHasGraphemeBreakBetween(scalar1, scalar2) } /// Initialize a new character recognizer at the _start of text_ (sot) @@ -637,59 +640,76 @@ extension _StringGuts { nextScalar: (Int) -> (scalar: Unicode.Scalar, end: Int)? ) -> Int { _internalInvariant(index < endIndex._encodedOffset) + return _nextGraphemeClusterBoundary(startingAt: index, nextScalar: nextScalar) + } +} - // Note: If `index` in't already on a boundary, then starting with an empty - // state here sometimes leads to this method returning results that diverge - // from the true breaks in the string. - var state = _GraphemeBreakingState() - var (scalar, index) = nextScalar(index)! +internal func _nextGraphemeClusterBoundary( + startingAt index: Int, + nextScalar: (Int) -> (scalar: Unicode.Scalar, end: Int)? +) -> Int { - while true { - guard let (scalar2, nextIndex) = nextScalar(index) else { break } - if state.shouldBreak(between: scalar, and: scalar2) { - break - } - index = nextIndex - scalar = scalar2 + // Note: If `index` isn't already on a boundary, then starting with an empty + // state here sometimes leads to this method returning results that diverge + // from the true breaks in the string. + var state = _GraphemeBreakingState() + var (scalar, index) = nextScalar(index)! + + while true { + guard let (scalar2, nextIndex) = nextScalar(index) else { break } + if state.shouldBreak(between: scalar, and: scalar2) { + break } - - return index + index = nextIndex + scalar = scalar2 } - // Returns the stride of the grapheme cluster ending at offset `index`. - // - // This method uses `previousScalar` to looks back in the string as far as - // necessary to find a correct grapheme cluster boundary, whether or not - // `index` happens to be on a boundary itself. - internal func previousBoundary( + return index +} + +extension _StringGuts { + fileprivate func previousBoundary( endingAt index: Int, previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? ) -> Int { - // FIXME: This requires potentially arbitrary lookback in each iteration, - // leading to quadratic behavior in some edge cases. Ideally lookback should - // only be done once per cluster (or in the case of RI sequences, once per - // flag sequence). One way to avoid most quadratic behavior is to replace - // this implementation with a scheme that first searches backwards for a - // safe point then iterates forward using the regular `shouldBreak` until we - // reach `index`, as recommended in section 6.4 of TR#29. - // - // https://www.unicode.org/reports/tr29/#Random_Access - - var (scalar2, index) = previousScalar(index)! - - while true { - guard let (scalar1, previousIndex) = previousScalar(index) else { break } - if shouldBreakWithLookback( - between: scalar1, and: scalar2, at: index, with: previousScalar - ) { - break - } - index = previousIndex - scalar2 = scalar1 - } - - return index + _previousGraphemeClusterBoundary(endingAt: index, previousScalar: previousScalar) } + +} + +// Returns the stride of the grapheme cluster ending at offset `index`. +// +// This method uses `previousScalar` to looks back in the string as far as +// necessary to find a correct grapheme cluster boundary, whether or not +// `index` happens to be on a boundary itself. +internal func _previousGraphemeClusterBoundary( + endingAt index: Int, + previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? +) -> Int { + // FIXME: This requires potentially arbitrary lookback in each iteration, + // leading to quadratic behavior in some edge cases. Ideally lookback should + // only be done once per cluster (or in the case of RI sequences, once per + // flag sequence). One way to avoid most quadratic behavior is to replace + // this implementation with a scheme that first searches backwards for a + // safe point then iterates forward using the regular `shouldBreak` until we + // reach `index`, as recommended in section 6.4 of TR#29. + // + // https://www.unicode.org/reports/tr29/#Random_Access + + var (scalar2, index) = previousScalar(index)! + + while true { + guard let (scalar1, previousIndex) = previousScalar(index) else { break } + if _shouldBreakWithLookback( + between: scalar1, and: scalar2, at: index, with: previousScalar + ) { + break + } + index = previousIndex + scalar2 = scalar1 + } + + return index } extension _GraphemeBreakingState { @@ -708,13 +728,8 @@ extension _GraphemeBreakingState { between scalar1: Unicode.Scalar, and scalar2: Unicode.Scalar ) -> Bool { - // GB3 - if scalar1.value == 0xD, scalar2.value == 0xA { - return false - } - - if _hasGraphemeBreakBetween(scalar1, scalar2) { - return true + if let result = _quickHasGraphemeBreakBetween(scalar1, scalar2) { + return result } let x = Unicode._GraphemeBreakProperty(from: scalar1) @@ -868,289 +883,282 @@ extension _GraphemeBreakingState { } } -extension _StringGuts { - // Return true if there is an extended grapheme cluster boundary between two - // scalars, with no previous knowledge about preceding scalars. - // - // This method looks back as far as it needs to determine the correct - // placement of boundaries. - // - // This is based off of the Unicode Annex #29 for [Grapheme Cluster Boundary - // Rules](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules). - internal func shouldBreakWithLookback( - between scalar1: Unicode.Scalar, - and scalar2: Unicode.Scalar, - at index: Int, - with previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? - ) -> Bool { - // GB3 - if scalar1.value == 0xD, scalar2.value == 0xA { - return false - } - - if _hasGraphemeBreakBetween(scalar1, scalar2) { - return true - } - - let x = Unicode._GraphemeBreakProperty(from: scalar1) - let y = Unicode._GraphemeBreakProperty(from: scalar2) - - switch (x, y) { - - // Fast path: If we know our scalars have no properties the decision is - // trivial and we don't need to crawl to the default statement. - case (.any, .any): - return true - - // GB4 - case (.control, _): - return true - - // GB5 - case (_, .control): - return true - - // GB6 - case (.l, .l), - (.l, .v), - (.l, .lv), - (.l, .lvt): - return false - - // GB7 - case (.lv, .v), - (.v, .v), - (.lv, .t), - (.v, .t): - return false - - // GB8 - case (.lvt, .t), - (.t, .t): - return false - - // GB9 - case (_, .extend), - (_, .zwj): - return false - - // GB9a - case (_, .spacingMark): - return false - - // GB9b - case (.prepend, _): - return false - - // GB11 - case (.zwj, .extendedPictographic): - return !checkIfInEmojiSequence(at: index, with: previousScalar) - - // GB12 & GB13 - case (.regionalIndicator, .regionalIndicator): - return countRIs(at: index, with: previousScalar) - - // GB999 - default: - // GB9c - // - // Check if our rhs is an InCB=Consonant first because we can more easily - // exit out of this branch in most cases. Otherwise, this is a consonant. - // Check that the lhs is an InCB=Extend or InCB=Linker (we have to check - // if it's an .extend or .zwj first because _isInCBExtend assumes that it - // is true). - if scalar2._isInCBConsonant, - (x == .extend || x == .zwj), - (scalar1._isInCBExtend || scalar1._isInCBLinker) { - return !checkIfInIndicSequence(at: index, with: previousScalar) - } - - return true - } +// Return true if there is an extended grapheme cluster boundary between two +// scalars, with no previous knowledge about preceding scalars. +// +// This method looks back as far as it needs to determine the correct +// placement of boundaries. +// +// This is based off of the Unicode Annex #29 for [Grapheme Cluster Boundary +// Rules](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules). +fileprivate func _shouldBreakWithLookback( + between scalar1: Unicode.Scalar, + and scalar2: Unicode.Scalar, + at index: Int, + with previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? +) -> Bool { + if let result = _quickHasGraphemeBreakBetween(scalar1, scalar2) { + return result } - // When walking backwards, it's impossible to know whether we were in an emoji - // sequence without walking further backwards. This walks the string backwards - // enough until we figure out whether or not to break our - // (.zwj, .extendedPictographic) question. For example: - // - // Scalar view #1: - // - // [.control, .zwj, .extendedPictographic] - // ^ - // | = To determine whether or not we break here, we need - // to see the previous scalar's grapheme property. - // ^ - // | = This is neither .extendedPictographic nor .extend, thus we - // were never in an emoji sequence, so break between the .zwj - // and .extendedPictographic. - // - // Scalar view #2: - // - // [.extendedPictographic, .zwj, .extendedPictographic] - // ^ - // | = Same as above, move backwards one to - // view the previous scalar's property. - // ^ - // | = This is an .extendedPictographic, so this indicates that - // we are in an emoji sequence, so we should NOT break - // between the .zwj and .extendedPictographic. - // - // Scalar view #3: - // - // [.extendedPictographic, .extend, .extend, .zwj, .extendedPictographic] - // ^ - // | = Same as above - // ^ - // | = This is an .extend which means - // there is a potential emoji - // sequence, walk further backwards - // to find an .extendedPictographic. - // - // <-- = Another extend, go backwards more. - // ^ - // | = We found our starting .extendedPictographic letting us - // know that we are in an emoji sequence so our initial - // break question is answered as NO. - internal func checkIfInEmojiSequence( - at index: Int, - with previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? - ) -> Bool { - guard var i = previousScalar(index)?.start else { return false } - while let prev = previousScalar(i) { - i = prev.start - let gbp = Unicode._GraphemeBreakProperty(from: prev.scalar) + let x = Unicode._GraphemeBreakProperty(from: scalar1) + let y = Unicode._GraphemeBreakProperty(from: scalar2) - switch gbp { - case .extend: - continue - case .extendedPictographic: - return true - default: - return false - } - } + switch (x, y) { + + // Fast path: If we know our scalars have no properties the decision is + // trivial and we don't need to crawl to the default statement. + case (.any, .any): + return true + + // GB4 + case (.control, _): + return true + + // GB5 + case (_, .control): + return true + + // GB6 + case (.l, .l), + (.l, .v), + (.l, .lv), + (.l, .lvt): return false - } - - // When walking backwards, it's impossible to know whether we break when we - // see our first (InCB=Extend, InCB=Consonant) or (InCB=Linker, InCB=Consonant) - // without walking further backwards. This walks the string backwards enough - // until we figure out whether or not to break this indic sequence. For example: - // - // Scalar view #1: - // - // [InCB=Linker, InCB=Extend, InCB=Consonant] - // ^ - // | = To be able to know whether or not to - // break these two, we need to walk - // backwards to determine if this is a - // legitimate indic sequence. - // ^ - // | = The scalar sequence ends without a starting InCB=Consonant, - // so this is in fact not an indic sequence, so we can break the two. - // - // Scalar view #2: - // - // [InCB=Consonant, InCB=Linker, InCB=Extend, InCB=Consonant] - // ^ - // | = Same as above - // ^ - // | = This is a Linker, so we at least have seen - // 1 to be able to return true if we see a - // consonant later. - // ^ - // | = Is a consonant and we've seen a linker, so this is a - // legitimate indic sequence, so do NOT break the initial question. - internal func checkIfInIndicSequence( - at index: Int, - with previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? - ) -> Bool { - guard let p = previousScalar(index) else { return false } - - var hasSeenInCBLinker = p.scalar._isInCBLinker - var i = p.start - - while let (scalar, prev) = previousScalar(i) { - i = prev - - if scalar._isInCBConsonant { - return hasSeenInCBLinker - } - - let gbp = Unicode._GraphemeBreakProperty(from: scalar) - - guard gbp == .extend || gbp == .zwj else { - return false - } - - switch (scalar._isInCBExtend, scalar._isInCBLinker) { - case (false, false): - return false - - case (false, true): - hasSeenInCBLinker = true - - case (true, false): - continue - - case (true, true): - // This case should never happen, but if it does then just be cautious - // and say this is invalid. - return false - } - } + // GB7 + case (.lv, .v), + (.v, .v), + (.lv, .t), + (.v, .t): return false - } - // When walking backwards, it's impossible to know whether we break when we - // see our first (.regionalIndicator, .regionalIndicator) without walking - // further backwards. This walks the string backwards enough until we figure - // out whether or not to break these RIs. For example: - // - // Scalar view #1: - // - // [.control, .regionalIndicator, .regionalIndicator] - // ^ - // | = To be able to know whether or not to - // break these two, we need to walk - // backwards to determine if there were - // any previous .regionalIndicators in - // a row. - // ^ - // | = Not a .regionalIndicator, so our total riCount is 0 and 0 is - // even thus we do not break. - // - // Scalar view #2: - // - // [.control, .regionalIndicator, .regionalIndicator, .regionalIndicator] - // ^ - // | = Same as above - // ^ - // | = This is a .regionalIndicator, so continue - // walking backwards for more of them. riCount is - // now equal to 1. - // ^ - // | = Not a .regionalIndicator. riCount = 1 which is odd, so break - // the last two .regionalIndicators. - internal func countRIs( - at index: Int, - with previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? - ) -> Bool { - guard let p = previousScalar(index) else { return false } - var i = p.start - var riCount = 0 - while let p = previousScalar(i) { - i = p.start + // GB8 + case (.lvt, .t), + (.t, .t): + return false - let gbp = Unicode._GraphemeBreakProperty(from: p.scalar) - guard gbp == .regionalIndicator else { - break - } + // GB9 + case (_, .extend), + (_, .zwj): + return false - riCount += 1 + // GB9a + case (_, .spacingMark): + return false + + // GB9b + case (.prepend, _): + return false + + // GB11 + case (.zwj, .extendedPictographic): + return !_checkIfInEmojiSequence(at: index, with: previousScalar) + + // GB12 & GB13 + case (.regionalIndicator, .regionalIndicator): + return _countRIs(at: index, with: previousScalar) + + // GB999 + default: + // GB9c + // + // Check if our rhs is an InCB=Consonant first because we can more easily + // exit out of this branch in most cases. Otherwise, this is a consonant. + // Check that the lhs is an InCB=Extend or InCB=Linker (we have to check + // if it's an .extend or .zwj first because _isInCBExtend assumes that it + // is true). + if scalar2._isInCBConsonant, + (x == .extend || x == .zwj), + (scalar1._isInCBExtend || scalar1._isInCBLinker) { + return !_checkIfInIndicSequence(at: index, with: previousScalar) } - return riCount & 1 != 0 + + return true } } + +// When walking backwards, it's impossible to know whether we were in an emoji +// sequence without walking further backwards. This walks the string backwards +// enough until we figure out whether or not to break our +// (.zwj, .extendedPictographic) question. For example: +// +// Scalar view #1: +// +// [.control, .zwj, .extendedPictographic] +// ^ +// | = To determine whether or not we break here, we need +// to see the previous scalar's grapheme property. +// ^ +// | = This is neither .extendedPictographic nor .extend, thus we +// were never in an emoji sequence, so break between the .zwj +// and .extendedPictographic. +// +// Scalar view #2: +// +// [.extendedPictographic, .zwj, .extendedPictographic] +// ^ +// | = Same as above, move backwards one to +// view the previous scalar's property. +// ^ +// | = This is an .extendedPictographic, so this indicates that +// we are in an emoji sequence, so we should NOT break +// between the .zwj and .extendedPictographic. +// +// Scalar view #3: +// +// [.extendedPictographic, .extend, .extend, .zwj, .extendedPictographic] +// ^ +// | = Same as above +// ^ +// | = This is an .extend which means +// there is a potential emoji +// sequence, walk further backwards +// to find an .extendedPictographic. +// +// <-- = Another extend, go backwards more. +// ^ +// | = We found our starting .extendedPictographic letting us +// know that we are in an emoji sequence so our initial +// break question is answered as NO. +fileprivate func _checkIfInEmojiSequence( + at index: Int, + with previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? +) -> Bool { + guard var i = previousScalar(index)?.start else { return false } + while let prev = previousScalar(i) { + i = prev.start + let gbp = Unicode._GraphemeBreakProperty(from: prev.scalar) + + switch gbp { + case .extend: + continue + case .extendedPictographic: + return true + default: + return false + } + } + return false +} + +// When walking backwards, it's impossible to know whether we break when we +// see our first (InCB=Extend, InCB=Consonant) or (InCB=Linker, InCB=Consonant) +// without walking further backwards. This walks the string backwards enough +// until we figure out whether or not to break this indic sequence. For example: +// +// Scalar view #1: +// +// [InCB=Linker, InCB=Extend, InCB=Consonant] +// ^ +// | = To be able to know whether or not to +// break these two, we need to walk +// backwards to determine if this is a +// legitimate indic sequence. +// ^ +// | = The scalar sequence ends without a starting InCB=Consonant, +// so this is in fact not an indic sequence, so we can break the two. +// +// Scalar view #2: +// +// [InCB=Consonant, InCB=Linker, InCB=Extend, InCB=Consonant] +// ^ +// | = Same as above +// ^ +// | = This is a Linker, so we at least have seen +// 1 to be able to return true if we see a +// consonant later. +// ^ +// | = Is a consonant and we've seen a linker, so this is a +// legitimate indic sequence, so do NOT break the initial question. +fileprivate func _checkIfInIndicSequence( + at index: Int, + with previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? +) -> Bool { + guard let p = previousScalar(index) else { return false } + + var hasSeenInCBLinker = p.scalar._isInCBLinker + var i = p.start + + while let (scalar, prev) = previousScalar(i) { + i = prev + + if scalar._isInCBConsonant { + return hasSeenInCBLinker + } + + let gbp = Unicode._GraphemeBreakProperty(from: scalar) + + guard gbp == .extend || gbp == .zwj else { + return false + } + + switch (scalar._isInCBExtend, scalar._isInCBLinker) { + case (false, false): + return false + + case (false, true): + hasSeenInCBLinker = true + + case (true, false): + continue + + case (true, true): + // This case should never happen, but if it does then just be cautious + // and say this is invalid. + return false + } + } + + return false +} + +// When walking backwards, it's impossible to know whether we break when we +// see our first (.regionalIndicator, .regionalIndicator) without walking +// further backwards. This walks the string backwards enough until we figure +// out whether or not to break these RIs. For example: +// +// Scalar view #1: +// +// [.control, .regionalIndicator, .regionalIndicator] +// ^ +// | = To be able to know whether or not to +// break these two, we need to walk +// backwards to determine if there were +// any previous .regionalIndicators in +// a row. +// ^ +// | = Not a .regionalIndicator, so our total riCount is 0 and 0 is +// even thus we do not break. +// +// Scalar view #2: +// +// [.control, .regionalIndicator, .regionalIndicator, .regionalIndicator] +// ^ +// | = Same as above +// ^ +// | = This is a .regionalIndicator, so continue +// walking backwards for more of them. riCount is +// now equal to 1. +// ^ +// | = Not a .regionalIndicator. riCount = 1 which is odd, so break +// the last two .regionalIndicators. +fileprivate func _countRIs( + at index: Int, + with previousScalar: (Int) -> (scalar: Unicode.Scalar, start: Int)? +) -> Bool { + guard let p = previousScalar(index) else { return false } + var i = p.start + var riCount = 0 + while let p = previousScalar(i) { + i = p.start + + let gbp = Unicode._GraphemeBreakProperty(from: p.scalar) + guard gbp == .regionalIndicator else { + break + } + + riCount += 1 + } + return riCount & 1 != 0 +} diff --git a/stdlib/public/core/StringNormalization.swift b/stdlib/public/core/StringNormalization.swift index a8ee470de41..e14e4325afa 100644 --- a/stdlib/public/core/StringNormalization.swift +++ b/stdlib/public/core/StringNormalization.swift @@ -52,3 +52,119 @@ extension UnsafeBufferPointer where Element == UInt8 { return unsafe !UTF8.isContinuation(self[offset]) } } + +internal func _isScalarNFCQC( + _ scalar: Unicode.Scalar, + _ prevCCC: inout UInt8 +) -> Bool { + let normData = Unicode._NormData(scalar, fastUpperbound: 0x300) + + if prevCCC > normData.ccc, normData.ccc != 0 { + return false + } + + if !normData.isNFCQC { + return false + } + + prevCCC = normData.ccc + return true +} + +extension _StringGutsSlice { + internal func _withNFCCodeUnits(_ f: (UInt8) throws -> Void) rethrows { + let substring = String(_guts)[range] + // Fast path: If we're already NFC (or ASCII), then we don't need to do + // anything at all. + if _fastPath(_guts.isNFC) { + try substring.utf8.forEach(f) + return + } + + var isNFCQC = true + var prevCCC: UInt8 = 0 + + if _guts.isFastUTF8 { + _fastNFCCheck(&isNFCQC, &prevCCC) + + // Because we have access to the fastUTF8, we can go through that instead + // of accessing the UTF8 view on String. + if isNFCQC { + try unsafe withFastUTF8 { + for unsafe byte in unsafe $0 { + try f(byte) + } + } + + return + } + } else { + for scalar in substring.unicodeScalars { + if !_isScalarNFCQC(scalar, &prevCCC) { + isNFCQC = false + break + } + } + + if isNFCQC { + for byte in substring.utf8 { + try f(byte) + } + + return + } + } + + for scalar in substring.unicodeScalars._internalNFC { + try scalar.withUTF8CodeUnits { + for unsafe byte in unsafe $0 { + try f(byte) + } + } + } + } + + internal func _fastNFCCheck(_ isNFCQC: inout Bool, _ prevCCC: inout UInt8) { + unsafe withFastUTF8 { utf8 in + isNFCQC = unsafe _nfcQuickCheck(utf8, prevCCC: &prevCCC) + } + } +} + +/// Run the Unicode NFC quick check algorithm, returns +internal func _nfcQuickCheck( + _ utf8: UnsafeBufferPointer, + prevCCC: inout UInt8 +) -> Bool { + var position = 0 + + while position < utf8.count { + // If our first byte is less than 0xCC, then it means we're under the + // 0x300 scalar value and everything up to 0x300 is NFC already. + if unsafe utf8[position] < 0xCC { + // If our first byte is less than 0xC0, then it means it is ASCII + // and only takes up a single byte. + if unsafe utf8[position] < 0xC0 { + position &+= 1 + } else { + // Otherwise, this is a 2 byte < 0x300 sequence. + position &+= 2 + } + // ASCII always has ccc of 0. + prevCCC = 0 + + continue + } + + let (scalar, len) = unsafe _decodeScalar(utf8, startingAt: position) + + guard _isScalarNFCQC(scalar, &prevCCC) else { + return false + } + + position &+= len + } + + return true +} + diff --git a/stdlib/public/core/StringUTF8Validation.swift b/stdlib/public/core/StringUTF8Validation.swift index cd7003e0a08..70449c9cf98 100644 --- a/stdlib/public/core/StringUTF8Validation.swift +++ b/stdlib/public/core/StringUTF8Validation.swift @@ -18,7 +18,7 @@ private func _isNotOverlong_F0(_ x: UInt8) -> Bool { return (0x90...0xBF).contains(x) } -private func _isNotOverlong_F4(_ x: UInt8) -> Bool { +private func _isNotInvalid_F4(_ x: UInt8) -> Bool { return UTF8.isContinuation(x) && x <= 0x8F } @@ -26,7 +26,7 @@ private func _isNotOverlong_E0(_ x: UInt8) -> Bool { return (0xA0...0xBF).contains(x) } -private func _isNotOverlong_ED(_ x: UInt8) -> Bool { +private func _isNotInvalid_ED(_ x: UInt8) -> Bool { return UTF8.isContinuation(x) && x <= 0x9F } @@ -34,15 +34,82 @@ internal struct UTF8ExtraInfo: Equatable { public var isASCII: Bool } +@inline(never) // slow-path +private func _diagnoseInvalidUTF8MultiByteLeading( + _ x: UInt8 +) -> _UTF8EncodingErrorKind { + _internalInvariant(x >= 0x80) + _internalInvariant(!_isUTF8MultiByteLeading(x)) + switch x { + case 0x80...0xBF: + return .unexpectedContinuationByte + case 0xC0..<0xC2: + return .overlongEncodingByte + default: + _internalInvariant(x > 0xF4) + return .invalidNonSurrogateCodePointByte + } +} + internal enum UTF8ValidationResult { case success(UTF8ExtraInfo) - case error(toBeReplaced: Range) + case error( + kind: _UTF8EncodingErrorKind, toBeReplaced: Range + ) +} + +// FIXME: refactor other parts of stdlib to avoid this dumb mirror enum +// +// Mirror of UTF8.ValidationError.Kind, available on 6.1 +internal struct _UTF8EncodingErrorKind: Error, Sendable, Hashable +// TODO: embedded?, Codable + , RawRepresentable { + internal var rawValue: UInt8 + + @available(SwiftStdlib 6.2, *) + internal var _publicKind: UTF8.ValidationError.Kind { + .init(rawValue: self.rawValue)! + } + + @inlinable + internal init(rawValue: UInt8) { + self.rawValue = rawValue + } + + /// A continuation byte (`10xxxxxx`) outside of a multi-byte sequence + @_alwaysEmitIntoClient + internal static var unexpectedContinuationByte: Self { + .init(rawValue: 0) + } + + /// A byte in a surrogate code point (`U+D800..U+DFFF`) sequence + @_alwaysEmitIntoClient + internal static var surrogateCodePointByte: Self { + .init(rawValue: 1) + } + + /// A byte in an invalid, non-surrogate code point (`>U+10FFFF`) sequence + @_alwaysEmitIntoClient + internal static var invalidNonSurrogateCodePointByte: Self { + .init(rawValue: 2) + } + + /// A byte in an overlong encoding sequence + @_alwaysEmitIntoClient + internal static var overlongEncodingByte: Self { + .init(rawValue: 3) + } + + /// A multi-byte sequence that is the start of a valid multi-byte scalar + /// but is cut off before ending correctly + @_alwaysEmitIntoClient + internal static var truncatedScalar: Self { + .init(rawValue: 4) + } } extension UTF8ValidationResult: Equatable {} -private struct UTF8ValidationError: Error {} - internal func validateUTF8(_ buf: UnsafeBufferPointer) -> UTF8ValidationResult { if unsafe _allASCII(buf) { return .success(UTF8ExtraInfo(isASCII: true)) @@ -51,12 +118,20 @@ internal func validateUTF8(_ buf: UnsafeBufferPointer) -> UTF8ValidationR var iter = unsafe buf.makeIterator() var lastValidIndex = buf.startIndex - @inline(__always) func guaranteeIn(_ f: (UInt8) -> Bool) throws(UTF8ValidationError) { - guard let cu = unsafe iter.next() else { throw UTF8ValidationError() } - guard f(cu) else { throw UTF8ValidationError() } + @inline(__always) func guarantee( + _ f: (UInt8) -> Bool, + _ err: _UTF8EncodingErrorKind + ) throws(_UTF8EncodingErrorKind) { + guard let cu = unsafe iter.next() else { + throw .truncatedScalar + } + guard f(cu) else { + throw err + } } - @inline(__always) func guaranteeContinuation() throws(UTF8ValidationError) { - try guaranteeIn(UTF8.isContinuation) + @inline(__always) func guaranteeContinuation( + ) throws(_UTF8EncodingErrorKind) { + try guarantee(UTF8.isContinuation, .truncatedScalar) } func _legacyInvalidLengthCalculation(_ _buffer: (_storage: UInt32, ())) -> Int { @@ -117,21 +192,40 @@ internal func validateUTF8(_ buf: UnsafeBufferPointer) -> UTF8ValidationR return unsafe _legacyNarrowIllegalRange(buf: buf[illegalRange]) } - do { + do throws(_UTF8EncodingErrorKind) { + + /* + The table of valid UTF-8 is: + + ╔════════════════════╦════════╦════════╦════════╦════════╗ + ║ Scalar value ║ Byte 0 ║ Byte 1 ║ Byte 2 ║ Byte 3 ║ + ╠════════════════════╬════════╬════════╬════════╬════════╣ + ║ U+0000..U+007F ║ 00..7F ║ ║ ║ ║ + ║ U+0080..U+07FF ║ C2..DF ║ Contin ║ ║ ║ + ║ U+0800..U+0FFF ║ E0 ║ A0..BF ║ Contin ║ ║ + ║ U+1000..U+CFFF ║ E1..EC ║ Contin ║ Contin ║ ║ + ║ U+D000..U+D7FF ║ ED ║ 80..9F ║ Contin ║ ║ + ║ U+E000..U+FFFF ║ EE..EF ║ Contin ║ Contin ║ ║ + ║ U+10000..U+3FFFF ║ F0 ║ 90..BF ║ Contin ║ Contin ║ + ║ U+40000..U+FFFFF ║ F1..F3 ║ Contin ║ Contin ║ Contin ║ + ║ U+100000..U+10FFFF ║ F4 ║ 80..8F ║ Contin ║ Contin ║ + ╚════════════════════╩════════╩════════╩════════╩════════╝ + + "Contin" is any continuation byte, i.e. 80..BF or 10xxxxxx + */ var isASCII = true while let cu = unsafe iter.next() { if UTF8.isASCII(cu) { lastValidIndex &+= 1; continue } isASCII = false if _slowPath(!_isUTF8MultiByteLeading(cu)) { - func fail() throws(UTF8ValidationError) { throw UTF8ValidationError() } - try fail() + throw _diagnoseInvalidUTF8MultiByteLeading(cu) } switch cu { case 0xC2...0xDF: try guaranteeContinuation() lastValidIndex &+= 2 case 0xE0: - try guaranteeIn(_isNotOverlong_E0) + try guarantee(_isNotOverlong_E0, .overlongEncodingByte) try guaranteeContinuation() lastValidIndex &+= 3 case 0xE1...0xEC: @@ -139,7 +233,7 @@ internal func validateUTF8(_ buf: UnsafeBufferPointer) -> UTF8ValidationR try guaranteeContinuation() lastValidIndex &+= 3 case 0xED: - try guaranteeIn(_isNotOverlong_ED) + try guarantee(_isNotInvalid_ED, .surrogateCodePointByte) try guaranteeContinuation() lastValidIndex &+= 3 case 0xEE...0xEF: @@ -147,7 +241,7 @@ internal func validateUTF8(_ buf: UnsafeBufferPointer) -> UTF8ValidationR try guaranteeContinuation() lastValidIndex &+= 3 case 0xF0: - try guaranteeIn(_isNotOverlong_F0) + try guarantee(_isNotOverlong_F0, .overlongEncodingByte) try guaranteeContinuation() try guaranteeContinuation() lastValidIndex &+= 4 @@ -157,7 +251,8 @@ internal func validateUTF8(_ buf: UnsafeBufferPointer) -> UTF8ValidationR try guaranteeContinuation() lastValidIndex &+= 4 case 0xF4: - try guaranteeIn(_isNotOverlong_F4) + try guarantee( + _isNotInvalid_F4, .invalidNonSurrogateCodePointByte) try guaranteeContinuation() try guaranteeContinuation() lastValidIndex &+= 4 @@ -167,7 +262,9 @@ internal func validateUTF8(_ buf: UnsafeBufferPointer) -> UTF8ValidationR } return .success(UTF8ExtraInfo(isASCII: isASCII)) } catch { - return unsafe .error(toBeReplaced: findInvalidRange(buf[lastValidIndex...])) + return unsafe .error( + kind: error, + toBeReplaced: findInvalidRange(buf[lastValidIndex...])) } } @@ -214,7 +311,7 @@ internal func repairUTF8(_ input: UnsafeBufferPointer, firstKnownBrokenRa case .success: unsafe result.appendInPlace(remainingInput, isASCII: false) return String(result) - case .error(let newBrokenRange): + case .error(_, let newBrokenRange): brokenRange = newBrokenRange } } while !remainingInput.isEmpty diff --git a/stdlib/public/core/UTF8EncodingError.swift b/stdlib/public/core/UTF8EncodingError.swift new file mode 100644 index 00000000000..9956b5ec357 --- /dev/null +++ b/stdlib/public/core/UTF8EncodingError.swift @@ -0,0 +1,261 @@ +extension Unicode.UTF8 { + /** + + The kind and location of a UTF-8 encoding error. + + Valid UTF-8 is represented by this table: + + ``` + ╔════════════════════╦════════╦════════╦════════╦════════╗ + ║ Scalar value ║ Byte 0 ║ Byte 1 ║ Byte 2 ║ Byte 3 ║ + ╠════════════════════╬════════╬════════╬════════╬════════╣ + ║ U+0000..U+007F ║ 00..7F ║ ║ ║ ║ + ║ U+0080..U+07FF ║ C2..DF ║ 80..BF ║ ║ ║ + ║ U+0800..U+0FFF ║ E0 ║ A0..BF ║ 80..BF ║ ║ + ║ U+1000..U+CFFF ║ E1..EC ║ 80..BF ║ 80..BF ║ ║ + ║ U+D000..U+D7FF ║ ED ║ 80..9F ║ 80..BF ║ ║ + ║ U+E000..U+FFFF ║ EE..EF ║ 80..BF ║ 80..BF ║ ║ + ║ U+10000..U+3FFFF ║ F0 ║ 90..BF ║ 80..BF ║ 80..BF ║ + ║ U+40000..U+FFFFF ║ F1..F3 ║ 80..BF ║ 80..BF ║ 80..BF ║ + ║ U+100000..U+10FFFF ║ F4 ║ 80..8F ║ 80..BF ║ 80..BF ║ + ╚════════════════════╩════════╩════════╩════════╩════════╝ + ``` + + ### Classifying errors + + An *unexpected continuation* is when a continuation byte (`10xxxxxx`) occurs + in a position that should be the start of a new scalar value. Unexpected + continuations can often occur when the input contains arbitrary data + instead of textual content. An unexpected continuation at the start of + input might mean that the input was not correctly sliced along scalar + boundaries or that it does not contain UTF-8. + + A *truncated scalar* is a multi-byte sequence that is the start of a valid + multi-byte scalar but is cut off before ending correctly. A truncated + scalar at the end of the input might mean that only part of the entire + input was received. + + A *surrogate code point* (`U+D800..U+DFFF`) is invalid UTF-8. Surrogate + code points are used by UTF-16 to encode scalars in the supplementary + planes. Their presence may mean the input was encoded in a different 8-bit + encoding, such as CESU-8, WTF-8, or Java's Modified UTF-8. + + An *invalid non-surrogate code point* is any code point higher than + `U+10FFFF`. This can often occur when the input is arbitrary data instead + of textual content. + + An *overlong encoding* occurs when a scalar value that could have been + encoded using fewer bytes is encoded in a longer byte sequence. Overlong + encodings are invalid UTF-8 and can lead to security issues if not + correctly detected: + + - https://nvd.nist.gov/vuln/detail/CVE-2008-2938 + - https://nvd.nist.gov/vuln/detail/CVE-2000-0884 + + An overlong encoding of `NUL`, `0xC0 0x80`, is used in Java's Modified + UTF-8 but is invalid UTF-8. Overlong encoding errors often catch attempts + to bypass security measures. + + ### Reporting the range of the error + + The range of the error reported follows the *Maximal subpart of an + ill-formed subsequence* algorithm in which each error is either one byte + long or ends before the first byte that is disallowed. See "U+FFFD + Substitution of Maximal Subparts" in the Unicode Standard. Unicode started + recommending this algorithm in version 6 and is adopted by the W3C. + + The maximal subpart algorithm will produce a single multi-byte range for a + truncated scalar (a multi-byte sequence that is the start of a valid + multi-byte scalar but is cut off before ending correctly). For all other + errors (including overlong encodings, surrogates, and invalid code + points), it will produce an error per byte. + + // FIXME: without a checkAllErrors, we don't have these classification distinctions, should we drop it, ensure we will do it, or what? + + Since overlong encodings, surrogates, and invalid code points are erroneous + by the second byte (at the latest), the above definition produces the same + ranges as defining such a sequence as a truncated scalar error followed by + unexpected continuation byte errors. The more semantically-rich + classification is reported. + + For example, a surrogate count point sequence `ED A0 80` will be reported + as three `.surrogateCodePointByte` errors rather than a `.truncatedScalar` + followed by two `.unexpectedContinuationByte` errors. + + Other commonly reported error ranges can be constructed from this result. + For example, PEP 383's error-per-byte can be constructed by mapping over + the reported range. Similarly, constructing a single error for the longest + invalid byte range can be constructed by joining adjacent error ranges. + + ``` + ╔═════════════════╦══════╦═════╦═════╦═════╦═════╦═════╦═════╦══════╗ + ║ ║ 61 ║ F1 ║ 80 ║ 80 ║ E1 ║ 80 ║ C2 ║ 62 ║ + ╠═════════════════╬══════╬═════╬═════╬═════╬═════╬═════╬═════╬══════╣ + ║ Longest range ║ U+61 ║ err ║ ║ ║ ║ ║ ║ U+62 ║ + ║ Maximal subpart ║ U+61 ║ err ║ ║ ║ err ║ ║ err ║ U+62 ║ + ║ Error per byte ║ U+61 ║ err ║ err ║ err ║ err ║ err ║ err ║ U+62 ║ + ╚═════════════════╩══════╩═════╩═════╩═════╩═════╩═════╩═════╩══════╝ + ``` + + */ + @available(SwiftStdlib 6.2, *) + @frozen + public struct ValidationError: Error, Sendable, Hashable + { + /// The kind of encoding error + public var kind: Unicode.UTF8.ValidationError.Kind + + /// The range of offsets into our input containing the error + public var byteOffsets: Range + + @_alwaysEmitIntoClient + public init( + _ kind: Unicode.UTF8.ValidationError.Kind, + _ byteOffsets: Range + ) { + _precondition(byteOffsets.lowerBound >= 0) + if kind == .truncatedScalar { + _precondition(!byteOffsets.isEmpty) + _precondition(byteOffsets.count < 4) + } else { + _precondition(byteOffsets.count == 1) + } + + self.kind = kind + self.byteOffsets = byteOffsets + } + + @_alwaysEmitIntoClient + public init( + _ kind: Unicode.UTF8.ValidationError.Kind, at byteOffset: Int + ) { + self.init(kind, byteOffset..<(byteOffset+1)) + } + } +} + + +@available(SwiftStdlib 6.2, *) +extension UTF8.ValidationError { + /// The kind of encoding error encountered during validation + @frozen + public struct Kind: Error, Sendable, Hashable, RawRepresentable + { + public var rawValue: UInt8 + + @inlinable + public init?(rawValue: UInt8) { + guard rawValue <= 4 else { return nil } + self.rawValue = rawValue + } + + /// A continuation byte (`10xxxxxx`) outside of a multi-byte sequence + @_alwaysEmitIntoClient + public static var unexpectedContinuationByte: Self { + .init(rawValue: 0)! + } + + /// A byte in a surrogate code point (`U+D800..U+DFFF`) sequence + @_alwaysEmitIntoClient + public static var surrogateCodePointByte: Self { + .init(rawValue: 1)! + } + + /// A byte in an invalid, non-surrogate code point (`>U+10FFFF`) sequence + @_alwaysEmitIntoClient + public static var invalidNonSurrogateCodePointByte: Self { + .init(rawValue: 2)! + } + + /// A byte in an overlong encoding sequence + @_alwaysEmitIntoClient + public static var overlongEncodingByte: Self { + .init(rawValue: 3)! + } + + /// A multi-byte sequence that is the start of a valid multi-byte scalar + /// but is cut off before ending correctly + @_alwaysEmitIntoClient + public static var truncatedScalar: Self { + .init(rawValue: 4)! + } + } +} + +@_unavailableInEmbedded +@available(SwiftStdlib 6.2, *) +extension UTF8.ValidationError.Kind: CustomStringConvertible { + public var description: String { + switch self { + case .invalidNonSurrogateCodePointByte: + ".invalidNonSurrogateCodePointByte" + case .overlongEncodingByte: + ".overlongEncodingByte" + case .surrogateCodePointByte: + ".surrogateCodePointByte" + case .truncatedScalar: + ".truncatedScalar" + case .unexpectedContinuationByte: + ".unexpectedContinuationByte" + default: + fatalError("unreachable") + } + } +} + +@_unavailableInEmbedded +@available(SwiftStdlib 6.2, *) +extension UTF8.ValidationError: CustomStringConvertible { + public var description: String { + "UTF8.ValidationError(\(kind), \(byteOffsets))" + } +} + +extension UTF8 { + @available(SwiftStdlib 6.2, *) + @usableFromInline // for testing purposes + internal static func _checkAllErrors( + _ s: some Sequence + ) -> Array { + // TODO: Span fast path + // TODO: Fixed size buffer for non-contig inputs + // TODO: Lifetime-dependent result variant + let cus = Array(s) + return unsafe cus.withUnsafeBytes { + var bufPtr = unsafe $0 + var start = 0 + var errors: Array = [] + + // Remember the previous error, so that we can + // apply it to subsequent bytes instead of reporting + // just `.unexpectedContinuation`. + var priorError: UTF8.ValidationError? = nil + while true { + do throws(UTF8.ValidationError) { + _ = unsafe try bufPtr.baseAddress!._validateUTF8(limitedBy: bufPtr.count) + return errors + } catch { + let adjustedRange = + error.byteOffsets.lowerBound + start ..< error.byteOffsets.upperBound + start + + let kind: UTF8.ValidationError.Kind + if let prior = priorError, + prior.byteOffsets.upperBound == adjustedRange.lowerBound, + error.kind == .unexpectedContinuationByte + { + kind = prior.kind + } else { + kind = error.kind + } + let adjustedErr = UTF8.ValidationError(kind, adjustedRange) + priorError = adjustedErr + + let errEnd = error.byteOffsets.upperBound + start += errEnd + unsafe bufPtr = .init(rebasing: bufPtr[errEnd...]) + errors.append(adjustedErr) + } + } + } + } +} diff --git a/stdlib/public/core/UTF8Span.swift b/stdlib/public/core/UTF8Span.swift new file mode 100644 index 00000000000..733cb7b0e2b --- /dev/null +++ b/stdlib/public/core/UTF8Span.swift @@ -0,0 +1,235 @@ +// TODO: comment header + + +/// TODO: docs +@frozen +@safe +@available(SwiftStdlib 6.2, *) +public struct UTF8Span: Copyable, ~Escapable, BitwiseCopyable { + @usableFromInline + internal var _unsafeBaseAddress: UnsafeRawPointer? + + /* + A bit-packed count and flags (such as isASCII) + + ╔═══════╦═════╦══════════╦═══════╗ + ║ b63 ║ b62 ║ b61:56 ║ b56:0 ║ + ╠═══════╬═════╬══════════╬═══════╣ + ║ ASCII ║ NFC ║ reserved ║ count ║ + ╚═══════╩═════╩══════════╩═══════╝ + + ASCII means the contents are known to be all-ASCII (<0x7F). + NFC means contents are known to be in normal form C for fast comparisons. + */ + @usableFromInline + internal var _countAndFlags: UInt64 + + // @_alwaysEmitIntoClient + @inline(__always) + @lifetime(borrow start) // TODO: borrow or copy? + internal init( + _unsafeAssumingValidUTF8 start: borrowing UnsafeRawPointer, + _countAndFlags: UInt64 + ) { + unsafe self._unsafeBaseAddress = copy start + self._countAndFlags = _countAndFlags + + _invariantCheck() + } + + /// Creates a UTF8Span, bypassing safety and security checks. The caller + /// must guarantee that `codeUnits` contains validly-encoded UTF-8, or else + /// undefined behavior may result upon use. If `isKnownASCII: true is + /// passed`, the contents must be ASCII, or else undefined behavior may + /// result upon use. + @unsafe + @lifetime(copy codeUnits) + public init( + unchecked codeUnits: Span, + isKnownASCII: Bool = false + ) { + self.init( + _uncheckedAssumingValidUTF8: codeUnits, + isKnownASCII: isKnownASCII, + isKnownNFC: false + ) + } + + // FIXME: we need to make sure ALL API are nil safe, that is they + // at least check the count first + @_alwaysEmitIntoClient + internal func _start() -> UnsafeRawPointer { + unsafe _unsafeBaseAddress._unsafelyUnwrappedUnchecked + } +} + +// TODO: try to convert code to be ran on Span instead of URP + +@available(SwiftStdlib 6.2, *) +extension UTF8Span { + /// Creates a UTF8Span containing `codeUnits`. Validates that the input is + /// valid UTF-8, otherwise throws an error. + /// + /// The resulting UTF8Span has the same lifetime constraints as `codeUnits`. + @lifetime(copy codeUnits) + public init( + validating codeUnits: consuming Span + ) throws(UTF8.ValidationError) { + try self.init(_validating: codeUnits) + } + + // TODO: this doesn't need to be underscored, I don't think + @lifetime(copy codeUnits) + internal init( + _validating codeUnits: consuming Span + ) throws(UTF8.ValidationError) { + guard let basePtr = unsafe codeUnits._pointer else { + unsafe self._unsafeBaseAddress = nil + self._countAndFlags = 0 + return + } + + let count = codeUnits._count + let isASCII = unsafe try basePtr._validateUTF8(limitedBy: count) + + unsafe self._unsafeBaseAddress = .init(basePtr) + self._countAndFlags = UInt64(truncatingIfNeeded: count) + if isASCII { + _setIsASCII() + } + _internalInvariant(self.count == codeUnits.count) + } + + // TODO: SPI? + @lifetime(copy codeUnits) + internal init( + _uncheckedAssumingValidUTF8 codeUnits: consuming Span, + isKnownASCII: Bool, + isKnownNFC: Bool + ) { + guard let ptr = unsafe codeUnits._pointer else { + unsafe self._unsafeBaseAddress = nil + self._countAndFlags = 0 + return + } + + unsafe self._unsafeBaseAddress = ptr + self._countAndFlags = UInt64(truncatingIfNeeded: codeUnits.count) + if isKnownASCII { + _setIsASCII() + } + if isKnownNFC { + _setIsNFC() + } + _internalInvariant(self.count == codeUnits.count) + } + + // HACK: working around lack of internal plumbing work + internal var _str: String { unsafe _start()._str(0..( + _ body: (_ buffer: /*borrowing*/ UnsafeBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + try unsafe body(_start()._ubp(0.. { + @lifetime(copy self) + get { + unsafe Span(_unchecked: _unsafeBaseAddress, count: self.count) + } + } + + +} + +// TODO(toolchain): decide if we rebase on top of Guillaume's work +extension String { + + @available(SwiftStdlib 6.2, *) + public init(copying codeUnits: UTF8Span) { + let isASCII = codeUnits.isKnownASCII + self = unsafe codeUnits._withUnsafeBufferPointer { bufPtr in + unsafe String._uncheckedFromUTF8(bufPtr, isASCII: isASCII) + } + } + + @available(SwiftStdlib 6.2, *) + public var utf8Span: UTF8Span { + @lifetime(borrow self) + borrowing get { + let isKnownASCII = _guts.isASCII + let utf8 = self.utf8 + let span = utf8.span + let result = unsafe UTF8Span( + unchecked: span, + isKnownASCII: isKnownASCII) + return unsafe _overrideLifetime(result, borrowing: self) + } + } +} + +extension Substring { + @available(SwiftStdlib 6.2, *) + public var utf8Span: UTF8Span { + @lifetime(borrow self) + borrowing get { + let isKnownASCII = base._guts.isASCII + let utf8 = self.utf8 + let span = utf8.span + let result = unsafe UTF8Span( + unchecked: span, + isKnownASCII: isKnownASCII) + return unsafe _overrideLifetime(result, borrowing: self) + } + } +} + + + + diff --git a/stdlib/public/core/UTF8SpanBits.swift b/stdlib/public/core/UTF8SpanBits.swift new file mode 100644 index 00000000000..cfccfa81f17 --- /dev/null +++ b/stdlib/public/core/UTF8SpanBits.swift @@ -0,0 +1,126 @@ +@available(SwiftStdlib 6.2, *) +extension UTF8Span { + /// Returns whether contents are known to be all-ASCII. A return value of + /// `true` means that all code units are ASCII. A return value of `false` + /// means there _may_ be non-ASCII content. + /// + /// ASCII-ness is checked and remembered during UTF-8 validation, so this + /// is often equivalent to is-ASCII, but there are some situations where + /// we might return `false` even when the content happens to be all-ASCII. + /// + /// For example, a UTF-8 span generated from a `String` that at some point + /// contained non-ASCII content would report false for `isKnownASCII`, even + /// if that String had subsequent mutation operations that removed any + /// non-ASCII content. + @_alwaysEmitIntoClient + public var isKnownASCII: Bool { + 0 != _countAndFlags & Self._asciiBit + } + + /// Do a scan checking for whether the contents are all-ASCII. + /// + /// Updates the `isKnownASCII` bit if contents are all-ASCII. + @lifetime(self: copy self) + public mutating func checkForASCII() -> Bool { + if isKnownASCII { return true } + + let result = unsafe _withUnsafeBufferPointer { + unsafe _allASCII($0) + } + if result { + _setIsASCII() + } + return result + } + + /// Returns whether the contents are known to be NFC. This is not + /// always checked at initialization time and is set by `checkForNFC`. + // TODO: should this be @_unavailableInEmbedded + @_alwaysEmitIntoClient + public var isKnownNFC: Bool { + 0 != _countAndFlags & Self._nfcBit + } + + // Set the isKnownASCII bit to true (also isNFC) + @_alwaysEmitIntoClient + @lifetime(self: copy self) + internal mutating func _setIsASCII() { + self._countAndFlags |= Self._asciiBit | Self._nfcBit + } + + // Set the isKnownNFC bit to true (also isNFC) + @_alwaysEmitIntoClient + @lifetime(self: copy self) + internal mutating func _setIsNFC() { + self._countAndFlags |= Self._nfcBit + } + + /// Do a scan checking for whether the contents are in Normal Form C. + /// When the contents are in NFC, canonical equivalence checks are much + /// faster. + /// + /// `quickCheck` will check for a subset of NFC contents using the + /// NFCQuickCheck algorithm, which is faster than the full normalization + /// algorithm. However, it cannot detect all NFC contents. + /// + /// Updates the `isKnownNFC` bit. + @_unavailableInEmbedded + @lifetime(self: copy self) + public mutating func checkForNFC( + quickCheck: Bool + ) -> Bool { + if isKnownNFC { return true } + + if quickCheck { + let result = unsafe _withUnsafeBufferPointer { utf8 in + var prevCCC: UInt8 = 0 + return unsafe _nfcQuickCheck(utf8, prevCCC: &prevCCC) + } + if result { + self._countAndFlags |= Self._nfcBit + } + return result + } + + // TODO: use faster internal algorithm + let normalized = _str._nfcCodeUnits + guard unsafe _start()._urbp( + 0..) -> Bool { + unsafe _withUnsafeBufferPointer { unsafe $0.elementsEqual(other) } + } + + /// Whether this span has the same `Unicode.Scalar`s as `other`. + @_alwaysEmitIntoClient + public func unicodeScalarsEqual( + to other: some Sequence + ) -> Bool { + // TODO: We don't need to decode our code units, we can just match + // against their scalars' encoded bytes + + var scalars = makeUnicodeScalarIterator() + var otherScalars = other.makeIterator() + while let s = scalars.next() { + guard let otherS = otherScalars.next(), s == otherS else { + return false + } + } + guard scalars.next() == nil else { + return false + } + return true + } + + /// Whether this span has the same `Character`s as `other`. + @_unavailableInEmbedded + @_alwaysEmitIntoClient + public func charactersEqual( + to other: some Sequence + ) -> Bool { + var chars = makeCharacterIterator() + var otherChars = other.makeIterator() + while let c = chars.next() { + guard let otherC = otherChars.next(), c == otherC else { + return false + } + } + guard chars.next() == nil else { + return false + } + return true + } +} + +@available(SwiftStdlib 6.2, *) +extension UTF8Span { + /// Whether `self` is equivalent to `other` under Unicode Canonical + /// Equivalence. + public func isCanonicallyEquivalent( + to other: UTF8Span + ) -> Bool { + unsafe self._withUnsafeBufferPointer { selfBufPtr in + unsafe other._withUnsafeBufferPointer { otherBufPtr in + unsafe _stringCompareFastUTF8( + selfBufPtr, + otherBufPtr, + expecting: .equal, + bothNFC: self.isKnownNFC && other.isKnownNFC) + } + } + } + + /// Whether `self` orders less than `other` under Unicode Canonical + /// Equivalence using normalized code-unit order (in NFC). + public func isCanonicallyLessThan( + _ other: UTF8Span + ) -> Bool { + unsafe self._withUnsafeBufferPointer { selfBufPtr in + unsafe other._withUnsafeBufferPointer { otherBufPtr in + unsafe _stringCompareFastUTF8( + selfBufPtr, + otherBufPtr, + expecting: .less, + bothNFC: self.isKnownNFC && other.isKnownNFC) + } + } + } +} + +// // FIXME: remove +// @available(SwiftStdlib 6.2, *) +// extension UTF8Span { +// public static func ~=(_ lhs: StaticString, _ rhs: UTF8Span) -> Bool { +// return lhs.withUTF8Buffer { str in +// rhs._withUnsafeBufferPointer { span in +// str.elementsEqual(span) +// } +// } +// } +// } + + diff --git a/stdlib/public/core/UTF8SpanFundamentals.swift b/stdlib/public/core/UTF8SpanFundamentals.swift new file mode 100644 index 00000000000..1f2dccb3e66 --- /dev/null +++ b/stdlib/public/core/UTF8SpanFundamentals.swift @@ -0,0 +1,360 @@ +// Core Scalar API +@available(SwiftStdlib 6.2, *) +extension UTF8Span { + /// Whether `i` is on a boundary between Unicode scalar values. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + internal func _isScalarAligned(unchecked i: Int) -> Bool { + if i == count || i == 0 { return true } + _internalInvariant(_boundsCheck(i)) + return unsafe _start()._isScalarAligned(i) + } + + /// Returns the start of the `Unicode.Scalar` ending at `i`, i.e. the scalar + /// before the one starting at `i` or the last scalar if `i` is the end of + /// the span. + /// + /// `i` must be scalar-aligned. + internal func _previousScalarStart(_ i: Int) -> Int { + precondition(_boundsCheck(i&-1)) + return _previousScalarStart(unchecked: i) + } + + /// Returns the start of the `Unicode.Scalar` ending at `i`, i.e. the scalar + /// before the one starting at `i` or the last scalar if `i` is the end of + /// the span. + /// + /// `i` must be scalar-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + internal func _previousScalarStart(unchecked i: Int) -> Int { + _internalInvariant(_boundsCheck(i&-1)) + precondition(_isScalarAligned(unchecked: i)) + return _previousScalarStart(uncheckedAssumingAligned: i) + } + + /// Returns the start of the `Unicode.Scalar` ending at `i`, i.e. the scalar + /// before the one starting at `i` or the last scalar if `i` is the end of + /// the span. + /// + /// `i` must be scalar-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + /// + /// + /// This function does not validate that `i` is scalar-aligned; this is an + /// unsafe operation if `i` isn't. + internal func _previousScalarStart( + uncheckedAssumingAligned i: Int + ) -> Int { + _internalInvariant(_boundsCheck(i&-1)) + _internalInvariant(_isScalarAligned(unchecked: i)) + return unsafe _start()._previousScalarStart(i) + } + + /// Decode the `Unicode.Scalar` starting at `i`. Return it and the start of + /// the next scalar. + /// + /// `i` must be scalar-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + /// + /// + /// This function does not validate that `i` is scalar-aligned; this is an + /// unsafe operation if `i` isn't. + internal func _decodeNextScalar( + uncheckedAssumingAligned i: Int + ) -> (Unicode.Scalar, nextScalarStart: Int) { + _internalInvariant(_boundsCheck(i)) + _internalInvariant(_isScalarAligned(unchecked: i)) + return unsafe _start()._decodeScalar(startingAt: i) + } + + /// Decode the `Unicode.Scalar` ending at `i`, i.e. the previous scalar. + /// Return it and the start of that scalar. + /// + /// `i` must be scalar-aligned. + internal func _decodePreviousScalar( + _ i: Int + ) -> (Unicode.Scalar, previousScalarStart: Int) { + precondition(_boundsCheck(i &- 1)) + return _decodePreviousScalar(unchecked: i) + } + + /// Decode the `Unicode.Scalar` ending at `i`, i.e. the previous scalar. + /// Return it and the start of that scalar. + /// + /// `i` must be scalar-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + internal func _decodePreviousScalar( + unchecked i: Int + ) -> (Unicode.Scalar, previousScalarStart: Int) { + _internalInvariant(_boundsCheck(i &- 1)) + precondition(_isScalarAligned(unchecked: i)) + return _decodePreviousScalar(uncheckedAssumingAligned: i) + } + + /// Decode the `Unicode.Scalar` ending at `i`, i.e. the previous scalar. + /// Return it and the start of that scalar. + /// + /// `i` must be scalar-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + /// + /// + /// This function does not validate that `i` is scalar-aligned; this is an + /// unsafe operation if `i` isn't. + internal func _decodePreviousScalar( + uncheckedAssumingAligned i: Int + ) -> (Unicode.Scalar, previousScalarStart: Int) { + _internalInvariant(_boundsCheck(i &- 1)) + _internalInvariant(_isScalarAligned(unchecked: i)) + return unsafe _start()._decodeScalar(endingAt: i) + } +} + +// Derived Scalar API +@available(SwiftStdlib 6.2, *) +extension UTF8Span { + /// Find the nearest scalar-aligned position `<= i`. + internal func _scalarAlignBackwards(_ i: Int) -> Int { + if i == count || i == 0 { return i } + + precondition(_boundsCheck(i)) + return unsafe _start()._scalarAlign(i) + } + + /// Find the nearest scalar-aligned position `>= i`. + internal func _scalarAlignForwards(_ i: Int) -> Int { + // FIXME: do the bounds check + // FIXME: stop at end of code units + // - this should be an invariant, but checking it lets us avoid ever + // reading off the end + // FIXME: implement directly + var i = i + while _slowPath(!_isScalarAligned(unchecked: i)) { + i &+= 1 + } + return i + } + + /// Find the nearest scalar-aligned position `>= i`. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + internal func _scalarAlignForwards(unchecked i: Int) -> Int { + if i == count || i == 0 { return i } + + var i = i + while _slowPath(!_isScalarAligned(unchecked: i)) { + i &+= 1 + } + return i + } +} + +// Core Character API +@available(SwiftStdlib 6.2, *) +extension UTF8Span { + /// Returns the start of the next `Character` (i.e. grapheme cluster) after + /// the one starting at `i`, or the end of the span if `i` denotes the final + /// `Character`. + /// + /// `i` must be `Character`-aligned. + internal func _nextCharacterStart(_ i: Int) -> Int { + precondition(_boundsCheck(i)) + return _nextCharacterStart(unchecked: i) + } + + /// Returns the start of the next `Character` (i.e. grapheme cluster) after + /// the one starting at `i`, or the end of the span if `i` denotes the final + /// `Character`. + /// + /// `i` must be `Character`-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + internal func _nextCharacterStart(unchecked i: Int) -> Int { + _internalInvariant(_boundsCheck(i)) + precondition(_isScalarAligned(unchecked: i)) + return _nextCharacterStart(uncheckedAssumingAligned: i) + } + + /// Returns the start of the next `Character` (i.e. grapheme cluster) after + /// the one starting at `i`, or the end of the span if `i` denotes the final + /// `Character`. + /// + /// `i` must be `Character`-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + /// + /// This function does not validate that `i` is `Character`-aligned; this is + /// an unsafe operation if `i` isn't. + internal func _nextCharacterStart( + uncheckedAssumingAligned i: Int + ) -> Int { + _internalInvariant(_boundsCheck(i)) + _internalInvariant(_isScalarAligned(unchecked: i)) + return unsafe _start()._nextCharacterStart(i, limitedBy: count) + } + + /// Returns the start of the `Character` (i.e. grapheme cluster) ending at + /// `i`, i.e. the `Character` before the one starting at `i` or the last + /// `Character` if `i` is the end of the span. + /// + /// `i` must be `Character`-aligned. + internal func _previousCharacterStart(_ i: Int) -> Int { + precondition(_boundsCheck(i&-1)) + return _previousCharacterStart(unchecked: i) + } + + /// Returns the start of the `Character` (i.e. grapheme cluster) ending at + /// `i`, i.e. the `Character` before the one starting at `i` or the last + /// `Character` if `i` is the end of the span. + /// + /// `i` must be `Character`-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + internal func _previousCharacterStart(unchecked i: Int) -> Int { + _internalInvariant(_boundsCheck(i&-1)) + precondition(_isScalarAligned(unchecked: i)) + return _previousCharacterStart(uncheckedAssumingAligned: i) + } + + /// Returns the start of the `Character` (i.e. grapheme cluster) ending at + /// `i`, i.e. the `Character` before the one starting at `i` or the last + /// `Character` if `i` is the end of the span. + /// + /// `i` must be `Character`-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + /// + /// This function does not validate that `i` is `Character`-aligned; this is + /// an unsafe operation if `i` isn't. + internal func _previousCharacterStart( + uncheckedAssumingAligned i: Int + ) -> Int { + _internalInvariant(_boundsCheck(i&-1)) + _internalInvariant(_isScalarAligned(unchecked: i)) + return unsafe _start()._previousCharacterStart(i, limitedBy: count) + } + + /// Decode the `Character` starting at `i` Return it and the start of the + /// next `Character`. + /// + /// `i` must be `Character`-aligned. + internal func _decodeNextCharacter( + _ i: Int + ) -> (Character, nextCharacterStart: Int) { + precondition(_boundsCheck(i)) + return _decodeNextCharacter(unchecked: i) + } + + /// Decode the `Character` starting at `i` Return it and the start of the + /// next `Character`. + /// + /// `i` must be `Character`-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + internal func _decodeNextCharacter( + unchecked i: Int + ) -> (Character, nextCharacterStart: Int) { + _internalInvariant(_boundsCheck(i)) + precondition(_isScalarAligned(unchecked: i)) + return _decodeNextCharacter(uncheckedAssumingAligned: i) + } + + /// Decode the `Character` starting at `i` Return it and the start of the + /// next `Character`. + /// + /// `i` must be `Character`-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + /// + /// This function does not validate that `i` is `Character`-aligned; this is + /// an unsafe operation if `i` isn't. + internal func _decodeNextCharacter( + uncheckedAssumingAligned i: Int + ) -> (Character, nextCharacterStart: Int) { + _internalInvariant(_boundsCheck(i)) + _internalInvariant(_isScalarAligned(unchecked: i)) + return unsafe _start()._decodeCharacter( + startingAt: i, limitedBy: count) + } + + /// Decode the `Character` (i.e. grapheme cluster) ending at `i`, i.e. the + /// previous `Character`. Return it and the start of that `Character`. + /// + /// `i` must be `Character`-aligned. + internal func _decodePreviousCharacter(_ i: Int) -> (Character, Int) { + precondition(_boundsCheck(i &- 1)) + return _decodePreviousCharacter(unchecked: i) + } + + /// Decode the `Character` (i.e. grapheme cluster) ending at `i`, i.e. the + /// previous `Character`. Return it and the start of that `Character`. + /// + /// `i` must be `Character`-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + internal func _decodePreviousCharacter( + unchecked i: Int + ) -> (Character, Int) { + _internalInvariant(_boundsCheck(i &- 1)) + precondition(_isScalarAligned(unchecked: i)) + return _decodePreviousCharacter(uncheckedAssumingAligned: i) + } + + /// Decode the `Character` (i.e. grapheme cluster) ending at `i`, i.e. the + /// previous `Character`. Return it and the start of that `Character`. + /// + /// `i` must be `Character`-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + /// + /// This function does not validate that `i` is `Character`-aligned; this is + /// an unsafe operation if `i` isn't. + internal func _decodePreviousCharacter( + uncheckedAssumingAligned i: Int + ) -> (Character, Int) { + _internalInvariant(_boundsCheck(i &- 1)) + _internalInvariant(_isScalarAligned(unchecked: i)) + return unsafe _start()._decodeCharacter( + endingAt: i, limitedBy: count) + } + +} + +// TODO: internal? +@available(SwiftStdlib 6.2, *) +extension UTF8Span { + /// Whether `i` is in bounds + @_alwaysEmitIntoClient + internal func _boundsCheck(_ i: Int) -> Bool { + i >= 0 && i < count + } + /// Whether `bounds` is in bounds + @_alwaysEmitIntoClient + internal func _boundsCheck(_ bounds: Range) -> Bool { + _boundsCheck(bounds.lowerBound) + && _boundsCheck(bounds.upperBound &- 1) + } +} + +// Future work: UTF-16 support when we get views + + diff --git a/stdlib/public/core/UTF8SpanInternalHelpers.swift b/stdlib/public/core/UTF8SpanInternalHelpers.swift new file mode 100644 index 00000000000..9f7a2518042 --- /dev/null +++ b/stdlib/public/core/UTF8SpanInternalHelpers.swift @@ -0,0 +1,179 @@ +/* + + Additional helpers build on stdlibDuplicates.swift + + */ + +// TODO: Should we update our unicode helpers file to call these instead? + +// import Builtin + +extension UnsafeRawPointer { + // @_alwaysEmitIntoClient + internal func _loadByte(_ i: Int) -> UInt8 { + _internalInvariant(i >= 0) + return unsafe (self+i).loadUnaligned(as: UInt8.self) + } + + // @_alwaysEmitIntoClient + internal func _isUTF8Continuation(_ i: Int) -> Bool { + unsafe UTF8.isContinuation(_loadByte(i)) + } + + // @_alwaysEmitIntoClient + internal func _isScalarAligned(_ i: Int) -> Bool { + _internalInvariant(i >= 0) + return unsafe !_isUTF8Continuation(i) + } + + // @_alwaysEmitIntoClient + internal func _scalarLength(startingAt i: Int) -> Int { + unsafe _utf8ScalarLength(_loadByte(i)) + } + + // NOTE: Adaptation of `_decodeScalar` to work on URP +// @_alwaysEmitIntoClient + internal func _decodeScalar( + startingAt i: Int + ) -> (Unicode.Scalar, nextScalarStart: Int) { + let cu0 = unsafe _loadByte(i) + let len = _utf8ScalarLength(cu0) + let next = len &+ i + switch len { + case 1: return (_decodeUTF8(cu0), next) + case 2: return unsafe (_decodeUTF8(cu0, _loadByte(i &+ 1)), next) + case 3: return unsafe ( + _decodeUTF8(cu0, _loadByte(i &+ 1), _loadByte(i &+ 2)), next + ) + case 4: + return ( + unsafe _decodeUTF8( + cu0, _loadByte(i &+ 1), _loadByte(i &+ 2), _loadByte(i &+ 3) + ), + next + ) + default: Builtin.unreachable() + } + } + + // @_alwaysEmitIntoClient + internal func _decodeScalar( + endingAt i: Int + ) -> (Unicode.Scalar, previousScalarStart: Int) { + // TODO: no need to double load the bytes... + let start = unsafe _previousScalarStart(i) + return unsafe (_decodeScalar(startingAt: start).0, start) + } + + // @_alwaysEmitIntoClient + internal func _previousScalarStart(_ i: Int) -> Int { + var prev = i &- 1 + _internalInvariant(prev >= 0) + while unsafe _isUTF8Continuation(prev) { + prev &-= 1 + _internalInvariant(prev >= 0) + } + _internalInvariant(unsafe i == prev + _utf8ScalarLength(_loadByte(prev))) + return prev + } + + // @_alwaysEmitIntoClient + internal func _scalarAlign(_ i: Int) -> Int { + var i = i + while _slowPath(unsafe !_isScalarAligned(i)) { + i &-= 1 + } + return i + } +} + +extension UnsafeRawPointer { + // TODO: ASCII fast path wrappers around ufi functions + + // TODO: hook up to real grapheme breaking + internal func _urbp(_ range: Range) -> UnsafeRawBufferPointer { + unsafe .init(start: self + range.lowerBound, count: range.count) + } + + @_alwaysEmitIntoClient + internal func _ubp(_ range: Range) -> UnsafeBufferPointer { + unsafe UnsafeBufferPointer( + start: UnsafePointer((self+range.lowerBound)._rawValue), + count: range.count) + } + + internal func _str(_ range: Range) -> String { + unsafe String(decoding: _urbp(range) , as: UTF8.self) + } + + // @usableFromInline + internal func _nextCharacterStart( + _ i: Int, limitedBy end: Int + ) -> Int { + _internalInvariant((0.. Int { + _internalInvariant(i > 0 && i <= end) + _internalInvariant(unsafe i == end || _isScalarAligned(i)) + + return _previousGraphemeClusterBoundary(endingAt: i) { idx in + guard idx > 0 else { return nil } + let (scalar, prior) = unsafe _decodeScalar(endingAt: idx) + return (scalar, prior) + } + } + + // @usableFromInline + internal func _decodeCharacter( + startingAt i: Int, limitedBy end: Int + ) -> (Character, nextCharacterStart: Int) { + let nextStart = unsafe _nextCharacterStart(i, limitedBy: end) + return unsafe (Character(_str(i.. (Character, nextCharacterStart: Int) { + let start = unsafe _previousCharacterStart(i, limitedBy: end) + _internalInvariant(start >= 0) + + return unsafe (Character(_str(start..) + } + + // Returns isASCII + // TODO: return more values + internal func _validateUTF8( + limitedBy end: Int + ) throws(UTF8.ValidationError) -> Bool { + switch unsafe validateUTF8(_ubp(0.. UnicodeScalarIterator { + .init(self) + } + + /// Iterate the `Unicode.Scalar`s contents of a `UTF8Span`. + /// + /// **TODO**: Examples + @frozen + public struct UnicodeScalarIterator: ~Escapable { + public let codeUnits: UTF8Span + + /// The byte offset of the start of the next scalar. This is + /// always scalar-aligned. + fileprivate(set) + public var currentCodeUnitOffset: Int + + @lifetime(copy codeUnits) + public init(_ codeUnits: UTF8Span) { + self.codeUnits = codeUnits + self.currentCodeUnitOffset = 0 + } + + private var _start: UnsafeRawPointer { + unsafe codeUnits._start() + } + + /// Decode and return the scalar starting at `currentCodeUnitOffset`. + /// After the function returns, `currentCodeUnitOffset` holds the + /// position at the end of the returned scalar, which is also the start + /// of the next scalar. + /// + /// Returns `nil` if at the end of the `UTF8Span`. + @lifetime(self: copy self) + public mutating func next() -> Unicode.Scalar? { + guard currentCodeUnitOffset < codeUnits.count else { + return nil + } + + _internalInvariant(codeUnits._isScalarAligned(unchecked: currentCodeUnitOffset)) + let (result, newPos) = unsafe _start._decodeScalar(startingAt: currentCodeUnitOffset) + self.currentCodeUnitOffset = newPos + return result + } + + /// Decode and return the scalar ending at `currentCodeUnitOffset`. After + /// the function returns, `currentCodeUnitOffset` holds the position at + /// the start of the returned scalar, which is also the end of the + /// previous scalar. + /// + /// Returns `nil` if at the start of the `UTF8Span`. + @lifetime(self: copy self) + public mutating func previous() -> Unicode.Scalar? { + guard currentCodeUnitOffset > 0 else { + return nil + } + + _internalInvariant(codeUnits._isScalarAligned(unchecked: currentCodeUnitOffset)) + let (result, newPos) = unsafe _start._decodeScalar(endingAt: currentCodeUnitOffset) + self.currentCodeUnitOffset = newPos + return result + } + + + /// Advance `codeUnitOffset` to the end of the current scalar, without + /// decoding it. + /// + /// Returns the number of `Unicode.Scalar`s skipped over, which can be 0 + /// if at the end of the UTF8Span. + @lifetime(self: copy self) + public mutating func skipForward() -> Int { + guard currentCodeUnitOffset < codeUnits.count else { + return 0 + } + + _internalInvariant(codeUnits._isScalarAligned(unchecked: currentCodeUnitOffset)) + + currentCodeUnitOffset &+= unsafe _start._scalarLength(startingAt: currentCodeUnitOffset) + return 1 + } + + /// Advance `codeUnitOffset` to the end of `n` scalars, without decoding + /// them. + /// + /// Returns the number of `Unicode.Scalar`s skipped over, which can be + /// fewer than `n` if at the end of the UTF8Span. + @lifetime(self: copy self) + public mutating func skipForward(by n: Int) -> Int { + var numSkipped = 0 + while numSkipped < n && skipForward() != 0 { + numSkipped += 1 + } + + return numSkipped + } + + /// Move `codeUnitOffset` to the start of the previous scalar, without + /// decoding it. + /// + /// Returns the number of `Unicode.Scalar`s skipped over, which can be 0 + /// if at the start of the UTF8Span. + @lifetime(self: copy self) + public mutating func skipBack() -> Int { + guard currentCodeUnitOffset > 0 else { + return 0 + } + + _internalInvariant(codeUnits._isScalarAligned(unchecked: currentCodeUnitOffset)) + + currentCodeUnitOffset = unsafe _start._previousScalarStart(currentCodeUnitOffset) + return 1 + } + + /// Move `codeUnitOffset` to the start of the previous `n` scalars, + /// without decoding them. + /// + /// Returns the number of `Unicode.Scalar`s skipped over, which can be + /// fewer than `n` if at the start of the UTF8Span. + @lifetime(self: copy self) + public mutating func skipBack(by n: Int) -> Int { + var numSkipped = 0 + while numSkipped < n && skipBack() != 0 { + numSkipped += 1 + } + + return numSkipped + } + + /// Reset to the nearest scalar-aligned code unit offset `<= i`. + /// + /// **TODO**: Example + @lifetime(self: copy self) + public mutating func reset(roundingBackwardsFrom i: Int) { + self.currentCodeUnitOffset = codeUnits._scalarAlignBackwards(i) + } + + /// Reset to the nearest scalar-aligned code unit offset `>= i`. + /// + /// **TODO**: Example + @lifetime(self: copy self) + public mutating func reset(roundingForwardsFrom i: Int) { + self.currentCodeUnitOffset = codeUnits._scalarAlignForwards(i) + } + + /// Reset this iterator to `codeUnitOffset`, skipping _all_ safety + /// checks (including bounds checks). + /// + /// Note: This is only for very specific, low-level use cases. If + /// `codeUnitOffset` is not properly scalar-aligned, this function can + /// result in undefined behavior when, e.g., `next()` is called. + /// + /// TODO: verify that we're not UB, just garabage-data or guaranteed + /// trap! + /// + /// For example, this could be used by a regex engine to backtrack to a + /// known-valid previous position. + /// + @unsafe + @lifetime(self: copy self) + public mutating func reset(toUnchecked codeUnitOffset: Int) { + _internalInvariant(codeUnits._isScalarAligned(unchecked: codeUnitOffset)) + self.currentCodeUnitOffset = codeUnitOffset + } + + /// Returns the UTF8Span containing all the content up to the iterator's + /// current position. + /// + /// The resultant `UTF8Span` has the same lifetime constraints as `self`. + @lifetime(copy self) + public func prefix() -> UTF8Span { + let slice = codeUnits.span._extracting(0.. UTF8Span { + let slice = codeUnits.span._extracting(currentCodeUnitOffset.. CharacterIterator { + .init(self) + } + + /// Iterate the `Character` contents of a `UTF8Span`. + /// + /// **TODO**: Examples + public struct CharacterIterator: ~Escapable { + public let codeUnits: UTF8Span + + /// The byte offset of the start of the next `Character`. This is always + /// scalar-aligned. It is always `Character`-aligned relative to the last + /// call to `reset` (or the start of the span if not called). + fileprivate(set) + public var currentCodeUnitOffset: Int + + @lifetime(copy codeUnits) + public init(_ codeUnits: UTF8Span) { + self.codeUnits = codeUnits + self.currentCodeUnitOffset = 0 + } + + private var _start: UnsafeRawPointer { + unsafe codeUnits._start() + } + + /// Return the `Character` starting at `currentCodeUnitOffset`. After the + /// function returns, `currentCodeUnitOffset` holds the position at the + /// end of the `Character`, which is also the start of the next + /// `Character`. + /// + /// Returns `nil` if at the end of the `UTF8Span`. + @lifetime(self: copy self) + public mutating func next() -> Character? { + guard currentCodeUnitOffset < codeUnits.count else { return nil } + + _internalInvariant(codeUnits._isScalarAligned(unchecked: currentCodeUnitOffset)) + let (result, newPos) = unsafe _start._decodeCharacter( + startingAt: currentCodeUnitOffset, + limitedBy: codeUnits.count + ) + self.currentCodeUnitOffset = newPos + return result + } + + /// Return the `Character` ending at `currentCodeUnitOffset`. After the + /// function returns, `currentCodeUnitOffset` holds the position at the + /// start of the returned `Character`, which is also the end of the + /// previous `Character`. + /// + /// Returns `nil` if at the start of the `UTF8Span`. + @lifetime(self: copy self) + public mutating func previous() -> Character? { + guard currentCodeUnitOffset > 0 else { return nil } + + _internalInvariant(codeUnits._isScalarAligned(unchecked: currentCodeUnitOffset)) + let (result, newPos) = unsafe _start._decodeCharacter( + endingAt: currentCodeUnitOffset, + limitedBy: codeUnits.count) + self.currentCodeUnitOffset = newPos + return result + } + + /// Advance `codeUnitOffset` to the end of the current `Character`, + /// without constructing it. + /// + /// Returns the number of `Character`s skipped over, which can be 0 + /// if at the end of the UTF8Span. + @lifetime(self: copy self) + public mutating func skipForward() -> Int { + guard currentCodeUnitOffset < codeUnits.count else { + return 0 + } + + _internalInvariant(codeUnits._isScalarAligned(unchecked: currentCodeUnitOffset)) + + self.currentCodeUnitOffset = unsafe _start._nextCharacterStart(currentCodeUnitOffset, limitedBy: codeUnits.count) + return 1 + } + + /// Advance `codeUnitOffset` to the end of `n` `Characters`, without + /// constructing them. + /// + /// Returns the number of `Character`s skipped over, which can be + /// fewer than `n` if at the end of the UTF8Span. + @lifetime(self: copy self) + public mutating func skipForward(by n: Int) -> Int { + var numSkipped = 0 + while numSkipped < n && skipForward() != 0 { + numSkipped += 1 + } + + return numSkipped + } + + /// Move `codeUnitOffset` to the start of the previous `Character`, + /// without constructing it. + /// + /// Returns the number of `Character`s skipped over, which can be 0 + /// if at the start of the UTF8Span. + @lifetime(self: copy self) + public mutating func skipBack() -> Int { + guard currentCodeUnitOffset > 0 else { + return 0 + } + + _internalInvariant(codeUnits._isScalarAligned(unchecked: currentCodeUnitOffset)) + + currentCodeUnitOffset = unsafe _start._previousCharacterStart(currentCodeUnitOffset, limitedBy: codeUnits.count) + return 1 + + } + + /// Move `codeUnitOffset` to the start of the previous `n` `Character`s, + /// without constructing them. + /// + /// Returns the number of `Character`s skipped over, which can be + /// fewer than `n` if at the start of the UTF8Span. + @lifetime(self: copy self) + public mutating func skipBack(by n: Int) -> Int { + var numSkipped = 0 + while numSkipped < n && skipBack() != 0 { + numSkipped += 1 + } + + return numSkipped + } + + /// Reset to the nearest character-aligned position `<= i`. + @lifetime(self: copy self) + public mutating func reset(roundingBackwardsFrom i: Int) { + self.currentCodeUnitOffset = codeUnits._scalarAlignBackwards(i) + } + + /// Reset to the nearest character-aligned position `>= i`. + @lifetime(self: copy self) + public mutating func reset(roundingForwardsFrom i: Int) { + self.currentCodeUnitOffset = codeUnits._scalarAlignForwards(i) + } + + /// Reset this iterator to `codeUnitOffset`, skipping _all_ safety + /// checks. + /// + /// Note: This is only for very specific, low-level use cases. If + /// `codeUnitOffset` is not properly scalar-aligned, this function can + /// result in undefined behavior when, e.g., `next()` is called. + /// + /// If `i` is scalar-aligned, but not `Character`-aligned, you may get + /// different results from running `Character` iteration. + /// + /// For example, this could be used by a regex engine to backtrack to a + /// known-valid previous position. + /// + @unsafe + @lifetime(self: copy self) + public mutating func reset(toUnchecked codeUnitOffset: Int) { + _internalInvariant(codeUnits._isScalarAligned(unchecked: codeUnitOffset)) + self.currentCodeUnitOffset = codeUnitOffset + } + + /// Returns the UTF8Span containing all the content up to the iterator's + /// current position. + @lifetime(copy self) + public func prefix() -> UTF8Span { + let slice = codeUnits.span._extracting(0.. UTF8Span { + let slice = codeUnits.span._extracting(currentCodeUnitOffset.. conditionalArgs, ConformanceExecutionContext &context ) { -#if SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES && SWIFT_PTRAUTH +#if SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES auto description = lookThroughOptionalConditionalWitnessTable( reinterpret_cast(wtable)) ->getDescription(); diff --git a/test/ASTGen/attrs.swift b/test/ASTGen/attrs.swift index 307dd7770c3..39c8f5379eb 100644 --- a/test/ASTGen/attrs.swift +++ b/test/ASTGen/attrs.swift @@ -2,7 +2,6 @@ // RUN: %target-swift-frontend-dump-parse \ // RUN: -enable-experimental-feature ABIAttribute \ -// RUN: -enable-experimental-feature ExecutionAttribute \ // RUN: -enable-experimental-feature Extern \ // RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -enable-experimental-feature RawLayout \ @@ -14,7 +13,6 @@ // RUN: %target-swift-frontend-dump-parse \ // RUN: -enable-experimental-feature ABIAttribute \ -// RUN: -enable-experimental-feature ExecutionAttribute \ // RUN: -enable-experimental-feature Extern \ // RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -enable-experimental-feature RawLayout \ @@ -29,7 +27,6 @@ // RUN: -module-abi-name ASTGen \ // RUN: -enable-experimental-feature ParserASTGen \ // RUN: -enable-experimental-feature ABIAttribute \ -// RUN: -enable-experimental-feature ExecutionAttribute \ // RUN: -enable-experimental-feature Extern \ // RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -enable-experimental-feature RawLayout \ @@ -42,7 +39,6 @@ // REQUIRES: swift_swift_parser // REQUIRES: swift_feature_ParserASTGen // REQUIRES: swift_feature_ABIAttribute -// REQUIRES: swift_feature_ExecutionAttribute // REQUIRES: swift_feature_Extern // REQUIRES: swift_feature_LifetimeDependence // REQUIRES: swift_feature_RawLayout @@ -195,19 +191,19 @@ struct StorageRestrctionTest { @_unavailableFromAsync struct UnavailFromAsyncStruct { } // expected-error {{'@_unavailableFromAsync' attribute cannot be applied to this declaration}} @_unavailableFromAsync(message: "foo bar") func UnavailFromAsyncFn() {} -@execution(concurrent) func testGlobal() async { // Ok +@concurrent func testGlobal() async { // Ok } do { - @execution(caller) func testLocal() async {} // Ok + nonisolated(nonsending) func testLocal() async {} // Ok struct Test { - @execution(concurrent) func testMember() async {} // Ok + @concurrent func testMember() async {} // Ok } } typealias testConvention = @convention(c) (Int) -> Int -typealias testExecution = @execution(concurrent) () async -> Void +typealias testExecution = @concurrent () async -> Void typealias testIsolated = @isolated(any) () -> Void protocol OpProto {} diff --git a/test/AutoDiff/SILOptimizer/BuildingSimulation.swift b/test/AutoDiff/SILOptimizer/BuildingSimulation.swift new file mode 100644 index 00000000000..b0b0dfd2f0d --- /dev/null +++ b/test/AutoDiff/SILOptimizer/BuildingSimulation.swift @@ -0,0 +1,228 @@ +// RUN: %target-swift-frontend -emit-sil -verify -O %s | %FileCheck %s +// REQUIRES: swift_in_compiler + +import _Differentiation + +// Simulation parameters +let trials = 100 +let timesteps = 20 +let dTime: Float = 0.1 + +// Definitions +let π = Float.pi + +struct SimParams: Differentiable { + var tube: TubeType = .init() + var slab: SlabType = .init() + var quanta: QuantaType = .init() + var tank: TankType = .init() + var startingTemp: Float +} + +struct TubeType: Differentiable { + var tubeSpacing: Float = 0.50292 // meters + var diameter: Float = 0.019 // m (3/4") + var thickness: Float = 0.001588 // m (1/16") + var resistivity: Float = 2.43 // (K/W)m +} + +struct SlabType: Differentiable { + var temp: Float = 21.1111111 // °C + var area: Float = 100.0 // m^2 + var Cp: Float = 0.2 + var density: Float = 2242.58 // kg/m^3 + var thickness: Float = 0.101 // m +} + +struct QuantaType: Differentiable { + var power: Float = 0.0 // Watt + var temp: Float = 60.0 // °C + var flow: Float = 0.0006309 // m^3/sec + var density: Float = 1000.0 // kg/m^3 + var Cp: Float = 4180.0 // ws/(kg • K) +} + +struct TankType: Differentiable { + var temp: Float = 70.0 + var volume: Float = 0.0757082 + var Cp: Float = 4180.000 + var density: Float = 1000.000 + var mass: Float = 75.708 +} + +// Computations + +@differentiable(reverse) +func computeResistance(floor: SlabType, tube: TubeType, quanta _: QuantaType) -> Float { + let geometry_coeff: Float = 10.0 + // let f_coff = 0.3333333 + + let tubingSurfaceArea = (floor.area / tube.tubeSpacing) * π * tube.diameter + let resistance_abs = tube.resistivity * tube.thickness / tubingSurfaceArea + + let resistance_corrected = resistance_abs * geometry_coeff // * (quanta.flow * f_coff) + + return resistance_corrected +} + +struct QuantaAndPower: Differentiable { + var quanta: QuantaType + var power: Float +} + + +extension Differentiable { + /// Applies the given closure to the derivative of `self`. + /// + /// Returns `self` like an identity function. When the return value is used in + /// a context where it is differentiated with respect to, applies the given + /// closure to the derivative of the return value. + @inlinable + @differentiable(reverse, wrt: self) + func withDerivative(_: @escaping (inout TangentVector) -> Void) -> Self { + return self + } + + @inlinable + @derivative(of: withDerivative) + func _vjpWithDerivative( + _ body: @escaping (inout TangentVector) -> Void + ) -> (value: Self, pullback: (TangentVector) -> TangentVector) { + return (self, { grad in + var grad = grad + body(&grad) + return grad + }) + } +} + +@differentiable(reverse) +func computeLoadPower(floor: SlabType, tube: TubeType, quanta: QuantaType) -> QuantaAndPower { + let resistance_abs = computeResistance(floor: floor, tube: tube, quanta: quanta) + + let conductance: Float = 1 / resistance_abs + let dTemp = floor.temp - quanta.temp + let power = dTemp * conductance + + var updatedQuanta = quanta + updatedQuanta.power = power + let loadPower = -power + + return QuantaAndPower(quanta: updatedQuanta, power: loadPower) +} + +@differentiable(reverse) +func updateQuanta(quanta: QuantaType) -> QuantaType { + let workingVolume = (quanta.flow * dTime) + let workingMass = (workingVolume * quanta.density) + let workingEnergy = quanta.power * dTime + let TempRise = workingEnergy / quanta.Cp / workingMass + var updatedQuanta = quanta + updatedQuanta.temp = quanta.temp + TempRise + + updatedQuanta.power = 0 + return updatedQuanta +} + +@differentiable(reverse) +func updateBuildingModel(power: Float, floor: SlabType) -> SlabType { + var updatedFloor = floor + + let floorVolume = floor.area * floor.thickness + let floorMass = floorVolume * floor.density + + updatedFloor.temp = floor.temp + ((power * dTime) / floor.Cp / floorMass) + return updatedFloor +} + +struct TankAndQuanta: Differentiable { + var tank: TankType + var quanta: QuantaType +} + +@differentiable(reverse) +func updateSourceTank(store: TankType, quanta: QuantaType) -> TankAndQuanta { + var updatedStore = store + var updatedQuanta = quanta + + let massPerTime = quanta.flow * quanta.density + let dTemp = store.temp - quanta.temp + let power = dTemp * massPerTime * quanta.Cp + + updatedQuanta.power = power + + let tankMass = store.volume * store.density + let TempRise = (power * dTime) / store.Cp / tankMass + updatedStore.temp = store.temp + TempRise + + return TankAndQuanta(tank: updatedStore, quanta: updatedQuanta) +} + +var simParams = SimParams(startingTemp: 33.3) + +@differentiable(reverse) +@inlinable public func absDifferentiable(_ value: Float) -> Float { + if value < 0 { + return -value + } + return value +} + +func lossCalc(pred: Float, gt: Float) -> Float { + let diff = pred - gt + return absDifferentiable(diff) +} + +// Simulations + +// Ensure things are properly specialized +// CHECK-LABEL: sil hidden @$s18BuildingSimulation8simulate9simParamsSfAA03SimE0V_tFTJrSpSr +// CHECK: function_ref specialized pullback of updateSourceTank(store:quanta:) +// CHECK-NOT: function_ref closure #1 in static Float._vjpMultiply(lhs:rhs:) +// CHECK-NOT: function_ref pullback of updateQuanta(quanta:) +// CHECK: function_ref specialized pullback of updateQuanta(quanta:) +@differentiable(reverse) +func simulate(simParams: SimParams) -> Float { + let pexTube = simParams.tube + var slab = simParams.slab + var tank = simParams.tank + var quanta = simParams.quanta + + slab.temp = simParams.startingTemp + for _ in 0 ..< timesteps { + let tankAndQuanta = updateSourceTank(store: tank, quanta: quanta) + tank = tankAndQuanta.tank + quanta = tankAndQuanta.quanta + + quanta = updateQuanta(quanta: quanta) + + let quantaAndPower = computeLoadPower(floor: slab, tube: pexTube, quanta: quanta) + quanta = quantaAndPower.quanta + let powerToBuilding = quantaAndPower.power + quanta = updateQuanta(quanta: quanta) + + slab = updateBuildingModel(power: powerToBuilding, floor: slab) + } + return slab.temp +} + +var blackHole: Any? +@inline(never) +func dontLetTheCompilerOptimizeThisAway(_ x: T) { + blackHole = x +} + +@differentiable(reverse) +func fullPipe(simParams: SimParams) -> Float { + let pred = simulate(simParams: simParams) + let loss = lossCalc(pred: pred, gt: 27.344767) + return loss +} + +for _ in 0 ..< trials { + let forwardOnly = fullPipe(simParams: simParams) + dontLetTheCompilerOptimizeThisAway(forwardOnly) + + let grad = gradient(at: simParams, of: fullPipe) + dontLetTheCompilerOptimizeThisAway(grad) +} diff --git a/test/AutoDiff/SILOptimizer/pullback_generation.swift b/test/AutoDiff/SILOptimizer/pullback_generation.swift index 9f3ea25a65a..3bb1ab02ba0 100644 --- a/test/AutoDiff/SILOptimizer/pullback_generation.swift +++ b/test/AutoDiff/SILOptimizer/pullback_generation.swift @@ -182,22 +182,22 @@ func f4(a: NonTrivial) -> Float { } // CHECK-LABEL: sil private [ossa] @$s19pullback_generation2f41aSfAA10NonTrivialV_tFTJpSpSr : $@convention(thin) (Float, @guaranteed Builtin.NativeObject) -> @owned NonTrivial { -// CHECK: bb5(%67 : @owned $NonTrivial, %68 : $Float, %69 : @owned $(predecessor: _AD__$s19pullback_generation2f41aSfAA10NonTrivialV_tF_bb2__Pred__src_0_wrt_0, @callee_guaranteed (@inout Float) -> Float)): -// CHECK: %88 = alloc_stack $NonTrivial +// CHECK: bb5(%[[#ARG0:]] : @owned $NonTrivial, %[[#]] : $Float, %[[#]] : @owned $(predecessor: _AD__$s19pullback_generation2f41aSfAA10NonTrivialV_tF_bb2__Pred__src_0_wrt_0, @callee_guaranteed (@inout Float) -> Float)): +// CHECK: %[[#T0:]] = alloc_stack $NonTrivial // Non-trivial value must be copied or there will be a // double consume when all owned parameters are destroyed // at the end of the basic block. -// CHECK: %89 = copy_value %67 : $NonTrivial +// CHECK: %[[#T1:]] = copy_value %[[#ARG0]] : $NonTrivial -// CHECK: store %89 to [init] %88 : $*NonTrivial -// CHECK: %91 = struct_element_addr %88 : $*NonTrivial, #NonTrivial.x -// CHECK: %92 = alloc_stack $Float -// CHECK: store %86 to [trivial] %92 : $*Float -// CHECK: %94 = witness_method $Float, #AdditiveArithmetic."+=" : (Self.Type) -> (inout Self, Self) -> () : $@convention(witness_method: AdditiveArithmetic) <τ_0_0 where τ_0_0 : AdditiveArithmetic> (@inout τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () -// CHECK: %95 = metatype $@thick Float.Type -// CHECK: %96 = apply %94(%91, %92, %95) : $@convention(witness_method: AdditiveArithmetic) <τ_0_0 where τ_0_0 : AdditiveArithmetic> (@inout τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () -// CHECK: destroy_value %67 : $NonTrivial +// CHECK: store %[[#T1]] to [init] %[[#T0]] : $*NonTrivial +// CHECK: %[[#T2:]] = struct_element_addr %[[#T0]] : $*NonTrivial, #NonTrivial.x +// CHECK: %[[#T3:]] = alloc_stack $Float +// CHECK: store %[[#T4:]] to [trivial] %[[#T3]] : $*Float +// CHECK: %[[#T5:]] = witness_method $Float, #AdditiveArithmetic."+=" : (Self.Type) -> (inout Self, Self) -> () : $@convention(witness_method: AdditiveArithmetic) <τ_0_0 where τ_0_0 : AdditiveArithmetic> (@inout τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () +// CHECK: %[[#T6:]] = metatype $@thick Float.Type +// CHECK: %[[#]] = apply %[[#T5]](%[[#T2]], %[[#T3]], %[[#T6]]) : $@convention(witness_method: AdditiveArithmetic) <τ_0_0 where τ_0_0 : AdditiveArithmetic> (@inout τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () +// CHECK: destroy_value %[[#ARG0]] : $NonTrivial @differentiable(reverse) func move_value(x: Float) -> Float { @@ -218,5 +218,5 @@ func move_value(x: Float) -> Float { // CHECK: %[[#]] = apply %[[#T2]](%[[#T1]], %[[#T3]]) : $@convention(witness_method: AdditiveArithmetic) <τ_0_0 where τ_0_0 : AdditiveArithmetic> (@thick τ_0_0.Type) -> @out τ_0_0 // CHECK: %[[#T4:]] = load [trivial] %[[#T1]] // CHECK: dealloc_stack %[[#T1]] -// CHECK: bb4(%113 : $Builtin.RawPointer): +// CHECK: bb4(%[[#]] : $Builtin.RawPointer): // CHECK: br bb5(%[[#]] : $Float, %[[#]] : $Float, %[[#T4]] : $Float, %[[#]] : $(predecessor: _AD__$s19pullback_generation10move_value1xS2f_tF_bb2__Pred__src_0_wrt_0)) diff --git a/test/AutoDiff/SILOptimizer/pullback_generation_loop_adjoints.swift b/test/AutoDiff/SILOptimizer/pullback_generation_loop_adjoints.swift new file mode 100644 index 00000000000..c8cc1af2026 --- /dev/null +++ b/test/AutoDiff/SILOptimizer/pullback_generation_loop_adjoints.swift @@ -0,0 +1,316 @@ +// RUN: %target-swift-frontend -emit-sil -Xllvm --sil-print-after=differentiation %s 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil -Xllvm --debug-only=differentiation %s 2>&1 | %FileCheck --check-prefix=DEBUG %s + +// Needed for '--debug-only' +// REQUIRES: asserts + +import _Differentiation + +@differentiable(reverse) +func repeat_while_loop(x: Float) -> Float { + var result : Float + repeat { + result = x + 0 + } while 0 == 1 + return result +} + +// DEBUG-LABEL: [AD] Running PullbackCloner on +// DEBUG-NEXT: // repeat_while_loop +// DEBUG: [AD] Begin search for adjoints of loop-local active values + +// CHECK-LABEL: // pullback of repeat_while_loop(x:) + +// DEBUG-NEXT: [AD] Original bb1 belongs to a loop, original header bb1, pullback header bb3 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#AARG0:]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#A0:]] = alloc_stack [var_decl] $Float, var, name "result", type $Float +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#A1:]] = apply %[[#A2:]](%[[#AARG0]], %[[#A3:]], %[[#A4:]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#A1]] = apply %[[#A2]](%[[#AARG0]], %[[#A3]], %[[#A4]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#A5:]] = begin_access [modify] [static] %[[#A0]] : $*Float + +// CHECK: bb3(%[[ARG31:[0-9]+]] : $Float, %[[ARG32:[0-9]+]] : $Float, %[[ARG33:[0-9]+]] : @owned $(predecessor: _AD__$s33pullback_generation_loop_adjoints013repeat_while_C01xS2f_tF_bb1__Pred__src_0_wrt_0, @callee_guaranteed (Float) -> Float)): +// CHECK: (%[[T01:[0-9]+]], %[[T02:[0-9]+]]) = destructure_tuple %[[ARG33]] +// CHECK: %[[T03:[0-9]+]] = load [trivial] %[[V1:[0-9]+]] + +/// Ensure that we do not add adjoint from the previous iteration +/// The incorrect SIL looked like the following: +/// %[[T03]] = load [trivial] %[[V1]] +/// store %[[#B08]] to [trivial] %[[#B13]] // <-- we check absence of this +/// store %[[T03]] to [trivial] %[[#B12]] +/// %62 = witness_method $Float, #AdditiveArithmetic."+" +/// %63 = metatype $@thick Float.Type +/// %64 = apply %62(%[[#]], %[[#B12]], %[[#B13]], %63) +// CHECK-NOT: store %[[ARG32]] to [trivial] %[[#]] + +// CHECK: %[[T04:[0-9]+]] = witness_method $Float, #AdditiveArithmetic.zero!getter : (Self.Type) -> () -> Self : $@convention(witness_method: AdditiveArithmetic) <τ_0_0 where τ_0_0 : AdditiveArithmetic> (@thick τ_0_0.Type) -> @out τ_0_0 +// CHECK: %[[T05:[0-9]+]] = metatype $@thick Float.Type +// CHECK: %[[#]] = apply %[[T04]](%[[V1]], %[[T05]]) : $@convention(witness_method: AdditiveArithmetic) <τ_0_0 where τ_0_0 : AdditiveArithmetic> (@thick τ_0_0.Type) -> @out τ_0_0 + +/// It's crucial that we call pullback with T03 which does not contain adjoints from previous iterations +// CHECK: %[[T07:[0-9]+]] = apply %[[T02]](%[[T03]]) : $@callee_guaranteed (Float) -> Float + +// CHECK: destroy_value %[[T02]] +// CHECK: %[[T08:[0-9]+]] = alloc_stack $Float +// CHECK: %[[T09:[0-9]+]] = alloc_stack $Float +// CHECK: %[[T10:[0-9]+]] = alloc_stack $Float +// CHECK: store %[[ARG31]] to [trivial] %[[T09]] +// CHECK: store %[[T07]] to [trivial] %[[T10]] +// CHECK: %[[T11:[0-9]+]] = witness_method $Float, #AdditiveArithmetic."+" : (Self.Type) -> (Self, Self) -> Self : $@convention(witness_method: AdditiveArithmetic) <τ_0_0 where τ_0_0 : AdditiveArithmetic> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0 +// CHECK: %[[T12:[0-9]+]] = metatype $@thick Float.Type +// CHECK: %[[#]] = apply %[[T11]](%[[T08]], %[[T10]], %[[T09]], %[[T12]]) : $@convention(witness_method: AdditiveArithmetic) <τ_0_0 where τ_0_0 : AdditiveArithmetic> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0 +// CHECK: destroy_addr %[[T09]] +// CHECK: destroy_addr %[[T10]] +// CHECK: dealloc_stack %[[T10]] +// CHECK: dealloc_stack %[[T09]] +// CHECK: %[[T14:[0-9]+]] = load [trivial] %[[T08]] +// CHECK: dealloc_stack %[[T08]] +// CHECK: debug_value %[[T14]], let, name "x", argno 1 +// CHECK: copy_addr %[[V1]] to %[[V2:[0-9]+]] +// CHECK: debug_value %[[T14]], let, name "x", argno 1 +// CHECK: copy_addr %[[V1]] to %[[V3:[0-9]+]] +// CHECK: switch_enum %[[T01]], case #_AD__$s33pullback_generation_loop_adjoints013repeat_while_C01xS2f_tF_bb1__Pred__src_0_wrt_0.bb2!enumelt: bb4, case #_AD__$s33pullback_generation_loop_adjoints013repeat_while_C01xS2f_tF_bb1__Pred__src_0_wrt_0.bb0!enumelt: bb6, forwarding: @owned + +// CHECK: bb4(%[[ARG41:[0-9]+]] : $Builtin.RawPointer): +// CHECK: %[[T15:[0-9]+]] = pointer_to_address %[[ARG41]] to [strict] $*(predecessor: _AD__$s33pullback_generation_loop_adjoints013repeat_while_C01xS2f_tF_bb2__Pred__src_0_wrt_0) +// CHECK: %[[T16:[0-9]+]] = load [trivial] %[[T15]] +// CHECK: br bb5(%[[T14]], %[[T03]], %[[T16]]) + +// DEBUG-NEXT: [AD] Original bb2 belongs to a loop, original header bb1, pullback header bb3 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#AARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#A0]] = alloc_stack [var_decl] $Float, var, name "result", type $Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#A1]] = apply %[[#A2]](%[[#AARG0]], %[[#A3]], %[[#A4]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#A5]] = begin_access [modify] [static] %[[#A0]] : $*Float + +// CHECK: bb5(%[[ARG51:[0-9]+]] : $Float, %[[ARG52:[0-9]+]] : $Float, %[[ARG53:[0-9]+]] : $(predecessor: _AD__$s33pullback_generation_loop_adjoints013repeat_while_C01xS2f_tF_bb2__Pred__src_0_wrt_0)): + +/// Ensure that we do not zero adjoints for non-loop-local active values +// CHECK-NOT: witness_method $Float, #AdditiveArithmetic.zero!getter + +// CHECK: %[[T17:[0-9]+]] = destructure_tuple %[[ARG53]] +// CHECK: debug_value %[[ARG51]], let, name "x", argno 1 +// CHECK: copy_addr %[[V2]] to %[[V1]] +// CHECK: switch_enum %[[T17]], case #_AD__$s33pullback_generation_loop_adjoints013repeat_while_C01xS2f_tF_bb2__Pred__src_0_wrt_0.bb1!enumelt: bb1, forwarding: @owned + +// DEBUG-NEXT: [AD] End search for adjoints of loop-local active values + +@differentiable(reverse) +func repeat_while_loop_nested(_ x: Float) -> Float { + var result = x + var i = 0 + repeat { + var temp = x + var j = 0 + repeat { + temp = temp * x + j += 1 + } while j < 2 + result = result * temp + i += 1 + } while i < 2 + return result +} + +// DEBUG-LABEL: [AD] Running PullbackCloner on +// DEBUG-NEXT: // repeat_while_loop_nested +// DEBUG: [AD] Begin search for adjoints of loop-local active values + +// DEBUG-NEXT: [AD] Original bb4 belongs to a loop, original header bb1, pullback header bb10 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#BARG0:]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B00:]] = alloc_stack [var_decl] $Float, var, name "result", type $Float +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is NOT a projection, zeroing its adjoint buffer in loop header: %[[#B01:]] = alloc_stack [var_decl] $Float, var, name "temp", type $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B02:]] = begin_access [read] [static] %[[#B01]] : $*Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B03:]] = load [trivial] %[[#B02]] : $*Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B04:]] = apply %[[#B05:]](%[[#B03]], %[[#BARG0]], %[[#B06:]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B07:]] = begin_access [modify] [static] %[[#B01]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#B08:]] = begin_access [read] [static] %[[#B00]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#B09:]] = load [trivial] %[[#B08]] : $*Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#B09]] = load [trivial] %[[#B08]] : $*Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#B10:]] = begin_access [read] [static] %[[#B01]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#B11:]] = load [trivial] %[[#B10]] : $*Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#B11]] = load [trivial] %[[#B10]] : $*Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#B12:]] = apply %[[#B13:]](%[[#B09]], %[[#B11]], %[[#B14:]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#B12]] = apply %[[#B13]](%[[#B09]], %[[#B11]], %[[#B14]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#B15:]] = begin_access [modify] [static] %[[#B00]] : $*Float + +// DEBUG-NEXT: [AD] Original bb2 belongs to a loop, original header bb2, pullback header bb6 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#BARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B00]] = alloc_stack [var_decl] $Float, var, name "result", type $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B01]] = alloc_stack [var_decl] $Float, var, name "temp", type $Float +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#B02]] = begin_access [read] [static] %[[#B01]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#B03]] = load [trivial] %[[#B02]] : $*Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#B03]] = load [trivial] %[[#B02]] : $*Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#B04]] = apply %[[#B05]](%[[#B03]], %[[#BARG0]], %[[#B06]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#B04]] = apply %[[#B05]](%[[#B03]], %[[#BARG0]], %[[#B06]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#B07]] = begin_access [modify] [static] %[[#B01]] : $*Float + +// DEBUG-NEXT: [AD] Original bb3 belongs to a loop, original header bb2, pullback header bb6 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#BARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B00]] = alloc_stack [var_decl] $Float, var, name "result", type $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B01]] = alloc_stack [var_decl] $Float, var, name "temp", type $Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B02]] = begin_access [read] [static] %[[#B01]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B03]] = load [trivial] %[[#B02]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B04]] = apply %[[#B05]](%[[#B03]], %[[#BARG0]], %[[#B06]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B07]] = begin_access [modify] [static] %[[#B01]] : $*Float + +// DEBUG-NEXT: [AD] Original bb1 belongs to a loop, original header bb1, pullback header bb10 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#BARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B00]] = alloc_stack [var_decl] $Float, var, name "result", type $Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B01]] = alloc_stack [var_decl] $Float, var, name "temp", type $Float + +// DEBUG-NEXT: [AD] Original bb5 belongs to a loop, original header bb1, pullback header bb10 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#BARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B00]] = alloc_stack [var_decl] $Float, var, name "result", type $Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B01]] = alloc_stack [var_decl] $Float, var, name "temp", type $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B02]] = begin_access [read] [static] %[[#B01]] : $*Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B03]] = load [trivial] %[[#B02]] : $*Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B04]] = apply %[[#B05]](%[[#B03]], %[[#BARG0]], %[[#B06]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#B07]] = begin_access [modify] [static] %[[#B01]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B08]] = begin_access [read] [static] %[[#B00]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B09]] = load [trivial] %[[#B08]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B10]] = begin_access [read] [static] %[[#B01]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B11]] = load [trivial] %[[#B10]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B12]] = apply %[[#B13]](%[[#B09]], %[[#B11]], %[[#B14]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] The following active value is loop-local, but it was already processed, skipping: %[[#B15]] = begin_access [modify] [static] %[[#B00]] : $*Float + +// DEBUG-NEXT: [AD] End search for adjoints of loop-local active values + +@differentiable(reverse) +func repeat_while_loop_condition(_ x: Float) -> Float { + var result = x + var i = 0 + repeat { + if i == 2 { + break + } + if i == 0 { + result = result * x + } else { + result = result * result + } + i += 1 + } while i < 10 + return result +} + +// DEBUG-LABEL: [AD] Running PullbackCloner on +// DEBUG-NEXT: // repeat_while_loop_condition +// DEBUG: [AD] Begin search for adjoints of loop-local active values + +// DEBUG-NEXT: [AD] Original bb6 belongs to a loop, original header bb1, pullback header bb10 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#CARG0:]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#C00:]] = alloc_stack [var_decl] $Float, var, name "result", type $Float + +// DEBUG-NEXT: [AD] Original bb1 belongs to a loop, original header bb1, pullback header bb10 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#CARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#C00]] = alloc_stack [var_decl] $Float, var, name "result", type $Float + +// DEBUG-NEXT: [AD] Original bb5 belongs to a loop, original header bb1, pullback header bb10 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#CARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#C00]] = alloc_stack [var_decl] $Float, var, name "result", type $Float +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#C01:]] = begin_access [read] [static] %[[#C00]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#C02:]] = load [trivial] %[[#C01]] : $*Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#C02]] = load [trivial] %[[#C01]] : $*Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#C03:]] = begin_access [read] [static] %[[#C00]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#C04:]] = load [trivial] %[[#C03]] : $*Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#C04]] = load [trivial] %[[#C03]] : $*Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#C05:]] = apply %[[#C06:]](%[[#C02]], %[[#C04]], %[[#C07:]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#C05]] = apply %[[#C06]](%[[#C02]], %[[#C04]], %[[#C07]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#]] = begin_access [modify] [static] %[[#C00]] : $*Float + +// DEBUG-NEXT: [AD] Original bb4 belongs to a loop, original header bb1, pullback header bb10 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#CARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#C00]] = alloc_stack [var_decl] $Float, var, name "result", type $Float +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#C08:]] = begin_access [read] [static] %[[#C00]] : $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#C09:]] = load [trivial] %[[#C08]] : $*Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#C09]] = load [trivial] %[[#C08]] : $*Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#C10:]] = apply %[[#C11:]](%[[#C09]], %[[#CARG0]], %[[#C12:]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#C10]] = apply %[[#C11]](%[[#C09]], %[[#CARG0]], %[[#C12]]) : $@convention(method) (Float, Float, @thin Float.Type) -> Float +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Float] +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#]] = begin_access [modify] [static] %[[#C00]] : $*Float + +// DEBUG-NEXT: [AD] Original bb7 belongs to a loop, original header bb1, pullback header bb10 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#CARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#C00]] = alloc_stack [var_decl] $Float, var, name "result", type $Float + +// DEBUG-NEXT: [AD] Original bb3 belongs to a loop, original header bb1, pullback header bb10 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#CARG0]] = argument of bb0 : $Float +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#C00]] = alloc_stack [var_decl] $Float, var, name "result", type $Float + +// DEBUG-NEXT: [AD] End search for adjoints of loop-local active values + +typealias FloatArrayTan = Array.TangentVector + +func identity(_ array: [Float]) -> [Float] { + var results: [Float] = [] + for i in withoutDerivative(at: array.indices) { + results += [array[i]] + } + return results +} + +pullback(at: [1, 2, 3], of: identity)(FloatArrayTan([4, -5, 6])) + +// DEBUG-LABEL: [AD] Running PullbackCloner on +// DEBUG-NEXT: // identity +// DEBUG: [AD] Begin search for adjoints of loop-local active values + +// DEBUG-NEXT: [AD] Original bb1 belongs to a loop, original header bb1, pullback header bb3 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#DARG0:]] = argument of bb0 : $Array +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#D0:]] = alloc_stack [var_decl] $Array, var, name "results", type $Array + +// DEBUG-NEXT: [AD] Original bb2 belongs to a loop, original header bb1, pullback header bb3 +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#DARG0:]] = argument of bb0 : $Array +// DEBUG-NEXT: [AD] The following active value is NOT loop-local, skipping: %[[#D0]] = alloc_stack [var_decl] $Array, var, name "results", type $Array +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#D1:]] = apply %[[#]](%[[#]]) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#D:]] = apply %[[#]](%[[#]]) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Array.DifferentiableView] +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: (**%[[#D2:]]**, %[[#D3:]]) = destructure_tuple %[[#D1]] : $(Array, Builtin.RawPointer) +// DEBUG-NEXT: [AD] Setting adjoint value for (**%[[#D2]]**, %[[#D3]]) = destructure_tuple %[[#D1]] : $(Array, Builtin.RawPointer) +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Array.DifferentiableView] +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Materializing adjoint for Zero[$Array.DifferentiableView] +// DEBUG-NEXT: [AD] Recorded temporary %[[#]] = load [take] %[[#]] : $*Array.DifferentiableView +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#D4:]] = pointer_to_address %[[#D5:]] : $Builtin.RawPointer to [strict] $*Float +// DEBUG-NEXT: [AD] The following active value is loop-local, zeroing its adjoint value in loop header: %[[#D6:]] = apply %[[#]](%[[#D2]]) : $@convention(thin) <τ_0_0> (@owned Array<τ_0_0>) -> @owned Array<τ_0_0> +// DEBUG-NEXT: [AD] Setting adjoint value for %[[#D6]] = apply %[[#]](%[[#D2]]) : $@convention(thin) <τ_0_0> (@owned Array<τ_0_0>) -> @owned Array<τ_0_0> +// DEBUG-NEXT: [AD] No debug variable found. +// DEBUG-NEXT: [AD] The new adjoint value, replacing the existing one, is: Zero[$Array.DifferentiableView] +// DEBUG-NEXT: [AD] The following active value is loop-local, checking if it's adjoint is a projection +// DEBUG-NEXT: [AD] Adjoint for the following value is a projection, skipping: %[[#]] = begin_access [modify] [static] %[[#D0]] : $*Array + +// DEBUG-NEXT: [AD] End search for adjoints of loop-local active values diff --git a/test/AutoDiff/validation-test/control_flow.swift b/test/AutoDiff/validation-test/control_flow.swift index 915049b0a52..70f5260aa73 100644 --- a/test/AutoDiff/validation-test/control_flow.swift +++ b/test/AutoDiff/validation-test/control_flow.swift @@ -628,12 +628,8 @@ ControlFlowTests.test("Loops") { } while i < 2 return result } - // FIXME(TF-584): Investigate incorrect (too big) gradient values for - // repeat-while loops. - // expectEqual((8, 12), valueWithGradient(at: 2, of: repeat_while_loop)) - // expectEqual((27, 27), valueWithGradient(at: 3, of: repeat_while_loop)) - expectEqual((8, 18), valueWithGradient(at: 2, of: repeat_while_loop)) - expectEqual((27, 36), valueWithGradient(at: 3, of: repeat_while_loop)) + expectEqual((8, 12), valueWithGradient(at: 2, of: repeat_while_loop)) + expectEqual((27, 27), valueWithGradient(at: 3, of: repeat_while_loop)) func repeat_while_loop_nonactive_initial_value(_ x: Float) -> Float { var result: Float = 1 @@ -644,12 +640,87 @@ ControlFlowTests.test("Loops") { } while i < 2 return result } - // FIXME(TF-584): Investigate incorrect (too big) gradient values for - // repeat-while loops. - // expectEqual((4, 4), valueWithGradient(at: 2, of: repeat_while_loop_nonactive_initial_value)) - // expectEqual((9, 6), valueWithGradient(at: 3, of: repeat_while_loop_nonactive_initial_value)) - expectEqual((4, 5), valueWithGradient(at: 2, of: repeat_while_loop_nonactive_initial_value)) - expectEqual((9, 7), valueWithGradient(at: 3, of: repeat_while_loop_nonactive_initial_value)) + expectEqual((4, 4), valueWithGradient(at: 2, of: repeat_while_loop_nonactive_initial_value)) + expectEqual((9, 6), valueWithGradient(at: 3, of: repeat_while_loop_nonactive_initial_value)) + + @differentiable(reverse) + func repeat_while_loop_nested_repeat(_ x: Float) -> Float { + var result = x + var i = 0 + repeat { + var temp = x + var j = 0 + repeat { + temp = temp * x + j += 1 + } while j < 2 + result = result * temp + i += 1 + } while i < 2 + return result + } + + expectEqual((128, 448), valueWithGradient(at: 2, of: repeat_while_loop_nested_repeat)) + expectEqual((2187, 5103), valueWithGradient(at: 3, of: repeat_while_loop_nested_repeat)) + + @differentiable(reverse) + func repeat_while_loop_nested_while(_ x: Float) -> Float { + var result = x + var i = 0 + repeat { + var temp = x + var j = 0 + while j < 2 { + temp = temp * x + j += 1 + } + result = result * temp + i += 1 + } while i < 2 + return result + } + + expectEqual((128, 448), valueWithGradient(at: 2, of: repeat_while_loop_nested_while)) + expectEqual((2187, 5103), valueWithGradient(at: 3, of: repeat_while_loop_nested_while)) + + @differentiable(reverse) + func repeat_while_loop_nested_for(_ x: Float) -> Float { + var result = x + var i = 0 + repeat { + var temp = x + for j in 0..<2 { + temp = temp * x + } + result = result * temp + i += 1 + } while i < 2 + return result + } + + expectEqual((128, 448), valueWithGradient(at: 2, of: repeat_while_loop_nested_for)) + expectEqual((2187, 5103), valueWithGradient(at: 3, of: repeat_while_loop_nested_for)) + + @differentiable(reverse) + func repeat_while_loop_condition(_ x: Float) -> Float { + var result = x + var i = 0 + repeat { + if i == 2 { + break + } + if i == 0 { + result = result * x + } else { + result = result * result + } + i += 1 + } while i < 10 + return result + } + + expectEqual((16, 32), valueWithGradient(at: 2, of: repeat_while_loop_condition)) + expectEqual((81, 108), valueWithGradient(at: 3, of: repeat_while_loop_condition)) func loop_continue(_ x: Float) -> Float { var result = x diff --git a/test/CAS/macro_plugin_external.swift b/test/CAS/macro_plugin_external.swift index f4688ba9f19..7bed89ac31e 100644 --- a/test/CAS/macro_plugin_external.swift +++ b/test/CAS/macro_plugin_external.swift @@ -43,11 +43,78 @@ // RUN: %target-swift-frontend \ // RUN: -typecheck -verify -cache-compile-job -cas-path %t/cas \ // RUN: -swift-version 5 -disable-implicit-swift-modules \ -// RUN: -external-plugin-path %t/plugins/#%swift-plugin-server \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \ // RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map.casid \ // RUN: %t/macro.swift @%t/MyApp.cmd +// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-serialized -module-name MyApp -module-cache-path %t/clang-module-cache -O \ +// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \ +// RUN: %t/macro.swift -o %t/deps2.json -swift-version 5 -cache-compile-job -cas-path %t/cas -external-plugin-path %t/plugins#%swift-plugin-server \ +// RUN: -scanner-prefix-map %t=/^test -scanner-prefix-map %swift-bin-dir=/^bin -resolved-plugin-verification + +// RUN: %S/Inputs/SwiftDepsExtractor.py %t/deps2.json MyApp casFSRootID > %t/fs.casid +// RUN: %cache-tool -cas-path %t/cas -cache-tool-action print-include-tree-list @%t/fs.casid | %FileCheck %s --check-prefix=FS-REMAP -DLIB=%target-library-name(MacroDefinition) + +/// CASFS is remapped. +// FS-REMAP: /^test/plugins/[[LIB]] + +// RUN: %S/Inputs/BuildCommandExtractor.py %t/deps2.json clang:SwiftShims > %t/SwiftShims2.cmd +// RUN: %swift_frontend_plain @%t/SwiftShims2.cmd + +// RUN: %S/Inputs/BuildCommandExtractor.py %t/deps2.json MyApp > %t/MyApp2.cmd +// RUN: %{python} %S/Inputs/GenerateExplicitModuleMap.py %t/deps2.json > %t/map2.json +// RUN: llvm-cas --cas %t/cas --make-blob --data %t/map2.json > %t/map2.casid + +/// Command-line is remapped. +// RUN: %FileCheck %s --check-prefix=CMD-REMAP --input-file=%t/MyApp2.cmd -DLIB=%target-library-name(MacroDefinition) + +// CMD-REMAP: -resolved-plugin-verification +// CMD-REMAP-NEXT: -load-resolved-plugin +// CMD-REMAP-NEXT: /^test/plugins/[[LIB]]#/^bin/swift-plugin-server#MacroDefinition + +// RUN: %target-swift-frontend \ +// RUN: -emit-module -o %t/Macro.swiftmodule -cache-compile-job -cas-path %t/cas \ +// RUN: -swift-version 5 -disable-implicit-swift-modules -O \ +// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \ +// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map2.casid -Rmacro-loading -Rcache-compile-job \ +// RUN: /^test/macro.swift @%t/MyApp2.cmd -cache-replay-prefix-map /^test=%t -cache-replay-prefix-map /^bin=%swift-bin-dir 2>&1 | %FileCheck %s --check-prefix=REMARK +// REMAKR: remark: cache miss +// REMARK: remark: loaded macro implementation module 'MacroDefinition' from compiler plugin server + +/// Encoded PLUGIN_SEARCH_OPTION is remapped. +// RUN: llvm-bcanalyzer -dump %t/Macro.swiftmodule | %FileCheck %s --check-prefix=MOD -DLIB=%target-library-name(MacroDefinition) + +// MOD: blob data = '/^test/plugins/[[LIB]]#/^bin/swift-plugin-server#MacroDefinition' + +/// Cache hit has no macro-loading remarks because no macro is actually loaded and the path might not be correct due to different mapping. +// RUN: %target-swift-frontend \ +// RUN: -emit-module -o %t/Macro.swiftmodule -cache-compile-job -cas-path %t/cas \ +// RUN: -swift-version 5 -disable-implicit-swift-modules -O \ +// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \ +// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map2.casid -Rmacro-loading -Rcache-compile-job \ +// RUN: /^test/macro.swift @%t/MyApp2.cmd -cache-replay-prefix-map /^test=%t -cache-replay-prefix-map /^bin=%swift-bin-dir 2>&1 | %FileCheck %s --check-prefix=NO-REMARK +// NO-REMARK: remark: replay output file +// NO-REMARK-NOT: remark: loaded macro implementation module 'MacroDefinition' from compiler plugin server + +/// Update timestamp, the build should still work. +// RUN: touch %t/plugins/%target-library-name(MacroDefinition) +// RUN: %target-swift-frontend \ +// RUN: -emit-module -o %t/Macro.swiftmodule -cache-compile-job -cas-path %t/cas \ +// RUN: -swift-version 5 -disable-implicit-swift-modules -O \ +// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \ +// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map2.casid -Rmacro-loading -Rcache-compile-job -cache-disable-replay \ +// RUN: /^test/macro.swift @%t/MyApp2.cmd -cache-replay-prefix-map /^test=%t -cache-replay-prefix-map /^bin=%swift-bin-dir + +/// Change the dylib content, this will fail the build. +// RUN: echo " " >> %t/plugins/%target-library-name(MacroDefinition) +// RUN: not %target-swift-frontend \ +// RUN: -emit-module -o %t/Macro.swiftmodule -cache-compile-job -cas-path %t/cas \ +// RUN: -swift-version 5 -disable-implicit-swift-modules -O \ +// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \ +// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map2.casid -Rmacro-loading -Rcache-compile-job -cache-disable-replay \ +// RUN: /^test/macro.swift @%t/MyApp2.cmd -cache-replay-prefix-map /^test=%t -cache-replay-prefix-map /^bin=%swift-bin-dir 2>&1 | %FileCheck %s --check-prefix=FAILED +// FAILED: plugin has changed since dependency scanning + //--- nomacro.swift func test() {} diff --git a/test/ClangImporter/rdar123543707.swift b/test/ClangImporter/rdar123543707.swift index e631a80256f..a4de2c6d3d7 100644 --- a/test/ClangImporter/rdar123543707.swift +++ b/test/ClangImporter/rdar123543707.swift @@ -7,8 +7,10 @@ import Module import Module_Private.Sub4 @_objcImplementation extension Module { - // expected-warning@-1 {{extension for main class interface should provide implementation for class method 'version()'}} - // expected-warning@-2 {{extension for main class interface should provide implementation for class method 'alloc()'}} + // expected-warning@-1 {{extension for main class interface does not provide all required implementations}} + // expected-note@-2 {{missing class method 'version()'}} + // expected-note@-3 {{missing class method 'alloc()'}} + // expected-note@-4 {{add stubs for missing '@implementation' requirements}} {{40-40=\n @objc(version)\n open class func version() -> UnsafePointer! {\n <#code#>\n \}\n\n @objc(alloc)\n open class func alloc() -> Self! {\n <#code#>\n \}\n}} } extension Module: @retroactive ModuleProto {} // no-error diff --git a/test/Concurrency/Runtime/actor_assert_precondition_executor_isIsolatingCurrentContext_swift62_mode.swift b/test/Concurrency/Runtime/actor_assert_precondition_executor_isIsolatingCurrentContext_swift62_mode.swift index 00c68a9b752..e651b7f1abf 100644 --- a/test/Concurrency/Runtime/actor_assert_precondition_executor_isIsolatingCurrentContext_swift62_mode.swift +++ b/test/Concurrency/Runtime/actor_assert_precondition_executor_isIsolatingCurrentContext_swift62_mode.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift %import-libdispatch -parse-as-library %s -o %t/a.out +// RUN: %target-build-swift %import-libdispatch -Xfrontend -disable-availability-checking -parse-as-library %s -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s @@ -93,25 +93,22 @@ actor ActorOnIsCheckImplementingExecutor { @main struct Main { static func main() async { - if #available(SwiftStdlib 6.2, *) { + let hasIsIsolatingCurrentContextExecutor = IsIsolatingExecutor() + let justCheckIsolatedExecutor = JustCheckIsolatedExecutor() - let hasIsIsolatingCurrentContextExecutor = IsIsolatingExecutor() - let justCheckIsolatedExecutor = JustCheckIsolatedExecutor() - - print("=== do not crash with executor which implements isIsolatingCurrentContext") - let hasIsCheckActor = ActorOnIsCheckImplementingExecutor(on: hasIsIsolatingCurrentContextExecutor) - await hasIsCheckActor.checkPreconditionIsolated() - // CHECK: Before preconditionIsolated - // CHECK-NOT: called: checkIsolated - // CHECK: called: isIsolatingCurrentContext - // CHECK-NOT: called: checkIsolated - // CHECK: After preconditionIsolated + print("=== do not crash with executor which implements isIsolatingCurrentContext") + let hasIsCheckActor = ActorOnIsCheckImplementingExecutor(on: hasIsIsolatingCurrentContextExecutor) + await hasIsCheckActor.checkPreconditionIsolated() + // CHECK: Before preconditionIsolated + // CHECK-NOT: called: checkIsolated + // CHECK: called: isIsolatingCurrentContext + // CHECK-NOT: called: checkIsolated + // CHECK: After preconditionIsolated - // CHECK: Before assumeIsolated - // CHECK-NOT: called: checkIsolated - // CHECK: called: isIsolatingCurrentContext - // CHECK-NOT: called: checkIsolated - // CHECK: After assumeIsolated - } + // CHECK: Before assumeIsolated + // CHECK-NOT: called: checkIsolated + // CHECK: called: isIsolatingCurrentContext + // CHECK-NOT: called: checkIsolated + // CHECK: After assumeIsolated } } diff --git a/test/Concurrency/Runtime/actor_assert_precondition_executor_not_implemented_isIsolatingCurrentContext_swift62_mode.swift b/test/Concurrency/Runtime/actor_assert_precondition_executor_not_implemented_isIsolatingCurrentContext_swift62_mode.swift index 693075078ce..dc83d4d708c 100644 --- a/test/Concurrency/Runtime/actor_assert_precondition_executor_not_implemented_isIsolatingCurrentContext_swift62_mode.swift +++ b/test/Concurrency/Runtime/actor_assert_precondition_executor_not_implemented_isIsolatingCurrentContext_swift62_mode.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift %import-libdispatch -parse-as-library %s -o %t/a.out +// RUN: %target-build-swift %import-libdispatch -Xfrontend -disable-availability-checking -parse-as-library %s -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s @@ -80,25 +80,22 @@ actor ActorOnIsCheckImplementingExecutor { @main struct Main { static func main() async { - if #available(SwiftStdlib 6.2, *) { + let hasIsIsolatingCurrentContextExecutor = IsIsolatingExecutor() + let justCheckIsolatedExecutor = JustCheckIsolatedExecutor() - let hasIsIsolatingCurrentContextExecutor = IsIsolatingExecutor() - let justCheckIsolatedExecutor = JustCheckIsolatedExecutor() - - print("do checkIsolated with executor which does NOT implement isIsolatingCurrentContext") - let checkIsolatedActor = ActorOnIsCheckImplementingExecutor(on: justCheckIsolatedExecutor) - await checkIsolatedActor.checkPreconditionIsolated() - // CHECK: Before preconditionIsolated - // CHECK-NOT: called: isIsolatingCurrentContext - // CHECK: called: checkIsolated - // CHECK-NOT: called: isIsolatingCurrentContext - // CHECK: After preconditionIsolated + print("do checkIsolated with executor which does NOT implement isIsolatingCurrentContext") + let checkIsolatedActor = ActorOnIsCheckImplementingExecutor(on: justCheckIsolatedExecutor) + await checkIsolatedActor.checkPreconditionIsolated() + // CHECK: Before preconditionIsolated + // CHECK-NOT: called: isIsolatingCurrentContext + // CHECK: called: checkIsolated + // CHECK-NOT: called: isIsolatingCurrentContext + // CHECK: After preconditionIsolated - // CHECK: Before assumeIsolated - // CHECK-NOT: called: isIsolatingCurrentContext - // CHECK: called: checkIsolated - // CHECK-NOT: called: isIsolatingCurrentContext - // CHECK: After assumeIsolated - } + // CHECK: Before assumeIsolated + // CHECK-NOT: called: isIsolatingCurrentContext + // CHECK: called: checkIsolated + // CHECK-NOT: called: isIsolatingCurrentContext + // CHECK: After assumeIsolated } } diff --git a/test/Concurrency/Runtime/async_taskgroup_addUnlessCancelled.swift b/test/Concurrency/Runtime/async_taskgroup_addUnlessCancelled.swift new file mode 100644 index 00000000000..12bfa0abc26 --- /dev/null +++ b/test/Concurrency/Runtime/async_taskgroup_addUnlessCancelled.swift @@ -0,0 +1,59 @@ +// RUN: %target-run-simple-swift( -target %target-swift-5.1-abi-triple -parse-as-library) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency + +// REQUIRES: concurrency_runtime +// UNSUPPORTED: back_deployment_runtime + +@available(SwiftStdlib 6.0, *) +func test_withTaskGroup_addUnlessCancelled() async throws { + let task = Task { + await withTaskGroup(of: Void.self) { group in + print("Inner: Sleep...") + try? await Task.sleep(for: .seconds(60)) // we'll never actually wait 10 seconds, as this will be woken up by cancel + print("Inner: Task.isCancelled: \(Task.isCancelled)") + + let added = group.addTaskUnlessCancelled { + print("Added Task! Child Task.isCancelled: \(Task.isCancelled)") + } + print("Inner: Task added = \(added)") // CHECK: Inner: Task added = false + } + } + + print("Outer: Cancel!") + task.cancel() + print("Outer: Cancelled") + + await task.value +} + +@available(SwiftStdlib 6.0, *) +func test_withDiscardingTaskGroup_addUnlessCancelled() async throws { + let task = Task { + await withDiscardingTaskGroup { group in + print("Inner: Sleep...") + try? await Task.sleep(for: .seconds(60)) // we'll never actually wait 10 seconds, as this will be woken up by cancel + print("Inner: Task.isCancelled: \(Task.isCancelled)") + + let added = group.addTaskUnlessCancelled { + print("Added Task! Child Task.isCancelled: \(Task.isCancelled)") + } + print("Inner: Task added = \(added)") // CHECK: Inner: Task added = false + } + } + + print("Outer: Cancel!") + task.cancel() + print("Outer: Cancelled") + + await task.value +} + +@available(SwiftStdlib 6.0, *) +@main struct Main { + static func main() async { + try! await test_withTaskGroup_addUnlessCancelled() + try! await test_withDiscardingTaskGroup_addUnlessCancelled() + } +} diff --git a/test/Concurrency/Runtime/custom_main_executor.swift b/test/Concurrency/Runtime/custom_main_executor.swift index bb5c0f5ef1b..08e9b8f7ce6 100644 --- a/test/Concurrency/Runtime/custom_main_executor.swift +++ b/test/Concurrency/Runtime/custom_main_executor.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library -executor-factory SimpleExecutorFactory) | %FileCheck %s +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library) | %FileCheck %s // REQUIRES: concurrency // REQUIRES: executable_test @@ -13,6 +13,8 @@ import StdlibUnittest import Synchronization +typealias DefaultExecutorFactory = SimpleExecutorFactory + struct SimpleExecutorFactory: ExecutorFactory { public static var mainExecutor: any MainExecutor { print("Creating main executor") diff --git a/test/Concurrency/Runtime/isolated_conformance.swift b/test/Concurrency/Runtime/isolated_conformance.swift index ca3a3534404..bc795e22e59 100644 --- a/test/Concurrency/Runtime/isolated_conformance.swift +++ b/test/Concurrency/Runtime/isolated_conformance.swift @@ -1,9 +1,8 @@ -// RUN: %target-run-simple-swift(-enable-experimental-feature IsolatedConformances -target %target-swift-5.1-abi-triple) | %FileCheck %s +// RUN: %target-run-simple-swift(-target %target-swift-5.1-abi-triple) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency // REQUIRES: concurrency_runtime -// REQUIRES: swift_feature_IsolatedConformances // UNSUPPORTED: back_deployment_runtime // FIXME: WebAssembly doesn't currently have a good way to install the diff --git a/test/Concurrency/Runtime/nonisolated_inherits_isolation.swift b/test/Concurrency/Runtime/nonisolated_inherits_isolation.swift index e82c09d1cf4..e2aff4a4634 100644 --- a/test/Concurrency/Runtime/nonisolated_inherits_isolation.swift +++ b/test/Concurrency/Runtime/nonisolated_inherits_isolation.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift( -swift-version 6 -g %import-libdispatch -import-objc-header %S/Inputs/RunOnMainActor.h -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution ) +// RUN: %target-run-simple-swift( -swift-version 6 -g %import-libdispatch -import-objc-header %S/Inputs/RunOnMainActor.h -enable-experimental-feature AsyncCallerExecution ) // REQUIRES: executable_test // REQUIRES: concurrency @@ -6,7 +6,6 @@ // REQUIRES: libdispatch // REQUIRES: asserts -// REQUIRES: swift_feature_ExecutionAttribute // REQUIRES: swift_feature_AsyncCallerExecution // UNSUPPORTED: freestanding @@ -34,13 +33,13 @@ struct CustomActor { } } -@execution(caller) +nonisolated(nonsending) func executionCallerIsolation() async { checkIfOnMainQueue() } // Expected to always crash -@execution(concurrent) +@concurrent func executionConcurrentIsolation() async { checkIfOnMainQueue() } diff --git a/test/Concurrency/Runtime/startSynchronously.swift b/test/Concurrency/Runtime/startSynchronously.swift index 27ebea1557b..8d8bacd1710 100644 --- a/test/Concurrency/Runtime/startSynchronously.swift +++ b/test/Concurrency/Runtime/startSynchronously.swift @@ -1,4 +1,3 @@ -// REQUIRES: rdar145735542 // RUN: %empty-directory(%t) // RUN: %target-build-swift -Xfrontend -disable-availability-checking %s %import-libdispatch -swift-version 6 -o %t/a.out // RUN: %target-codesign %t/a.out @@ -62,6 +61,42 @@ extension ThreadID: @unchecked Sendable {} @globalActor actor MyGlobalActor { static let shared: MyGlobalActor = MyGlobalActor() + + @MyGlobalActor + static func test() {} +} + +final class NaiveQueueExecutor: SerialExecutor { + let queue: DispatchQueue + + init(queue: DispatchQueue) { + self.queue = queue + } + + public func enqueue(_ job: consuming ExecutorJob) { + let unowned = UnownedJob(job) + print("NaiveQueueExecutor(\(self.queue.label)) enqueue [thread:\(getCurrentThreadID())]") + queue.async { + unowned.runSynchronously(on: self.asUnownedSerialExecutor()) + } + } +} + +@globalActor +actor DifferentGlobalActor { + static let queue = DispatchQueue(label: "DifferentGlobalActor-queue") + let executor: NaiveQueueExecutor + nonisolated let unownedExecutor: UnownedSerialExecutor + + init() { + self.executor = NaiveQueueExecutor(queue: DifferentGlobalActor.queue) + self.unownedExecutor = executor.asUnownedSerialExecutor() + } + + static let shared: DifferentGlobalActor = DifferentGlobalActor() + + @DifferentGlobalActor + static func test() {} } // Test on all platforms @@ -94,6 +129,49 @@ func syncOnMyGlobalActor() -> [Task] { return [t1, tt] } +func syncOnMyGlobalActorHopToDifferentActor() -> [Task] { + MyGlobalActor.shared.preconditionIsolated("Should be executing on the global actor here") + print("Confirmed to be on @MyGlobalActor") + + // This task must be guaranteed to happen AFTER 'tt' because we are already on this actor + // so this enqueue must happen after we give up the actor. + print("schedule Task { @DifferentGlobalActor }, before startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + let t1 = Task { @DifferentGlobalActor in + print("inside Task { @DifferentGlobalActor } [thread:\(getCurrentThreadID())] @ :\(#line)") + DifferentGlobalActor.shared.preconditionIsolated("Expected Task{} to be on DifferentGlobalActor") + } + + print("before startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + let outerTID = getCurrentThreadID() + let tt = Task.startSynchronously { @DifferentGlobalActor in + let innerTID = getCurrentThreadID() + print("inside startSynchronously, outer thread = \(outerTID)") + print("inside startSynchronously, inner thread = \(innerTID)") + if (compareThreadIDs(outerTID, .equal, innerTID)) { + // This case specifically is NOT synchronously run because we specified a different isolation for the closure + // and FORCED a hop to the DifferentGlobalActor executor. + print("ERROR! Outer Thread ID must NOT equal Thread ID inside runSynchronously synchronous part!") + } + // We crucially need to see this task be enqueued on the different global actor, + // so it did not execute "synchronously" after all - it had to hop to the other actor. + dispatchPrecondition(condition: .onQueue(DifferentGlobalActor.queue)) + DifferentGlobalActor.shared.preconditionIsolated("Expected Task.startSynchronously { @DifferentGlobalActor in } to be on DifferentGlobalActor") + + print("inside startSynchronously, sleep now [thread:\(getCurrentThreadID())] @ :\(#line)") + _ = try? await Task.sleep(for: .milliseconds(100)) + + print("inside startSynchronously, after sleep [thread:\(getCurrentThreadID())] @ :\(#line)") + dispatchPrecondition(condition: .onQueue(DifferentGlobalActor.queue)) + DifferentGlobalActor.shared.preconditionIsolated("Expected Task.startSynchronously { @DifferentGlobalActor in } to be on DifferentGlobalActor") + + // do something here + await MyGlobalActor.test() + DifferentGlobalActor.test() + } + + return [t1, tt] +} + func syncOnNonTaskThread(synchronousTask behavior: SynchronousTaskBehavior) { let sem1 = DispatchSemaphore(value: 0) let sem2 = DispatchSemaphore(value: 0) @@ -162,6 +240,33 @@ await Task { @MyGlobalActor in // resume on some other thread // CHECK: after sleep, inside startSynchronously +print("\n\n==== ------------------------------------------------------------------") +print("syncOnMyGlobalActorHopToDifferentActor()") + +await Task { @MyGlobalActor in + MyGlobalActor.shared.preconditionIsolated("Should be executing on the global actor here") + for t in syncOnMyGlobalActorHopToDifferentActor() { + await t.value + } +}.value + +// Assertion Notes: We expect the task to be on the specified queue as we force the Task.startSynchronously +// task to enqueue on the DifferentGlobalActor, however we CANNOT use threads to verify this behavior, +// because dispatch may still pull tricks and reuse threads. We can only verify that we're on the right +// queue, and that the `enqueue` calls on the target executor happen when we expect them to. +// +// CHECK: syncOnMyGlobalActorHopToDifferentActor() +// CHECK: Confirmed to be on @MyGlobalActor +// CHECK: before startSynchronously + +// This IS actually enqueueing on the target actor (not synchronous), as expected: +// CHECK: NaiveQueueExecutor(DifferentGlobalActor-queue) enqueue +// CHECK: inside startSynchronously, sleep now + +// After the sleep we get back onto the specified executor as expected +// CHECK: NaiveQueueExecutor(DifferentGlobalActor-queue) enqueue +// CHECK: inside startSynchronously, after sleep + print("\n\n==== ------------------------------------------------------------------") var behavior: SynchronousTaskBehavior = .suspend print("syncOnNonTaskThread(synchronousTask: \(behavior))") @@ -347,23 +452,6 @@ callActorFromStartSynchronousTask(recipient: .recipientOnQueue(RecipientOnQueue( // CHECK-NOT: ERROR! // CHECK: inside startSynchronously, done -final class NaiveQueueExecutor: SerialExecutor { - let queue: DispatchQueue - - init(queue: DispatchQueue) { - self.queue = queue - } - - public func enqueue(_ job: consuming ExecutorJob) { - let unowned = UnownedJob(job) - print("NaiveQueueExecutor(\(self.queue.label)) enqueue... [thread:\(getCurrentThreadID())]") - queue.async { - print("NaiveQueueExecutor(\(self.queue.label)) enqueue: run [thread:\(getCurrentThreadID())]") - unowned.runSynchronously(on: self.asUnownedSerialExecutor()) - } - } -} - actor RecipientOnQueue { let executor: NaiveQueueExecutor nonisolated let unownedExecutor: UnownedSerialExecutor diff --git a/test/Concurrency/Runtime/startSynchronously_order.swift b/test/Concurrency/Runtime/startSynchronously_order.swift new file mode 100644 index 00000000000..03927dfaa7e --- /dev/null +++ b/test/Concurrency/Runtime/startSynchronously_order.swift @@ -0,0 +1,64 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s %import-libdispatch -swift-version 6 -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: concurrency_runtime + +// UNSUPPORTED: back_deployment_runtime +// UNSUPPORTED: back_deploy_concurrency +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: freestanding + +import _Concurrency + +let max = 1000 + +func bar(x: Int, cc: CheckedContinuation) { + Task.startSynchronously { + print("Task \(x) started") + try! await Task.sleep(nanoseconds: 10000) + if (x == max) { + cc.resume() + } + } +} + +await withCheckedContinuation { (cc: CheckedContinuation) in + for i in 1...max { + bar(x: i, cc: cc) + } +} + +// CHECK: Task 1 started +// CHECK: Task 2 started +// CHECK: Task 3 started +// CHECK: Task 4 started +// CHECK: Task 5 started +// CHECK: Task 6 started +// CHECK: Task 7 started +// CHECK: Task 8 started +// CHECK: Task 9 started +// CHECK: Task 10 started +// CHECK: Task 11 started +// CHECK: Task 12 started +// CHECK: Task 13 started +// CHECK: Task 14 started +// CHECK: Task 15 started +// CHECK: Task 16 started +// CHECK: Task 17 started +// CHECK: Task 18 started +// CHECK: Task 19 started +// CHECK: Task 20 started +// CHECK: Task 21 started +// CHECK: Task 22 started +// CHECK: Task 23 started +// CHECK: Task 24 started +// CHECK: Task 25 started +// CHECK: Task 26 started +// CHECK: Task 27 started +// CHECK: Task 28 started +// CHECK: Task 29 started +// CHECK: Task 30 started \ No newline at end of file diff --git a/test/Concurrency/async_sequence_existential.swift b/test/Concurrency/async_sequence_existential.swift index 66452abe84a..4f84592f9bd 100644 --- a/test/Concurrency/async_sequence_existential.swift +++ b/test/Concurrency/async_sequence_existential.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple %s -emit-sil -o /dev/null -verify -// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple %s -dump-ast 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple -primary-file %s -dump-ast -o %t.ast.txt +// RUN: %FileCheck %s < %t.ast.txt // REQUIRES: concurrency diff --git a/test/Concurrency/attr_execution/adoption_mode.swift b/test/Concurrency/attr_execution/adoption_mode.swift index f8cdcad4c39..6ce3737b4e9 100644 --- a/test/Concurrency/attr_execution/adoption_mode.swift +++ b/test/Concurrency/attr_execution/adoption_mode.swift @@ -1,69 +1,68 @@ -// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -swift-version 5 -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution:adoption -// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -swift-version 6 -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution:adoption +// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -swift-version 5 -enable-experimental-feature AsyncCallerExecution:adoption +// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -swift-version 6 -enable-experimental-feature AsyncCallerExecution:adoption -// REQUIRES: swift_feature_ExecutionAttribute // REQUIRES: swift_feature_AsyncCallerExecution struct G { init(_: T) {} } -@execution(concurrent) func globalAsyncF() async {} +@concurrent func globalAsyncF() async {} // MARK: Functions do { func syncF() {} - @execution(concurrent) func executionConcurrentAsyncF() async {} - @execution(caller) func executionCallerAsyncF() async {} + @concurrent func executionConcurrentAsyncF() async {} + nonisolated(nonsending) func executionCallerAsyncF() async {} @MainActor func mainActorAsyncF() async {} func isolatedParamAsyncF( isolation: isolated (any Actor)? = #isolation ) async {} - // expected-warning@+1:20 {{feature 'AsyncCallerExecution' will cause nonisolated async local function 'asyncF' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{3-3=@execution(concurrent) }}{{none}} + // expected-warning@+1:20 {{feature 'AsyncCallerExecution' will cause nonisolated async local function 'asyncF' to run on the caller's actor; use @concurrent to preserve behavior}}{{3-3=@concurrent }}{{none}} nonisolated func asyncF() async {} struct S { init(sync: ()) {} - @execution(concurrent) init(executionAsync: ()) async {} + @concurrent init(executionAsync: ()) async {} @MainActor init(mainActorAsync: ()) async {} - // expected-warning@+1:5 {{feature 'AsyncCallerExecution' will cause nonisolated async initializer 'init' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{5-5=@execution(concurrent) }}{{none}} + // expected-warning@+1:5 {{feature 'AsyncCallerExecution' will cause nonisolated async initializer 'init' to run on the caller's actor; use @concurrent to preserve behavior}}{{5-5=@concurrent }}{{none}} init(async: ()) async {} func syncF() {} - @execution(concurrent) func executionAsyncF() async {} + @concurrent func executionAsyncF() async {} @MainActor func mainActorAsyncF() async {} - // expected-warning@+2:17 {{feature 'AsyncCallerExecution' will cause nonisolated async instance method 'asyncF' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{-1:5-5=@execution(concurrent) }}{{none}} + // expected-warning@+2:17 {{feature 'AsyncCallerExecution' will cause nonisolated async instance method 'asyncF' to run on the caller's actor; use @concurrent to preserve behavior}}{{-1:5-5=@concurrent }}{{none}} nonisolated public func asyncF() async {} } protocol P { init(sync: ()) - @execution(concurrent) init(executionAsync: ()) async + @concurrent init(executionAsync: ()) async @MainActor init(mainActorAsync: ()) async - // expected-warning@+1:5 {{feature 'AsyncCallerExecution' will cause nonisolated async initializer 'init' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{5-5=@execution(concurrent) }}{{none}} + // expected-warning@+1:5 {{feature 'AsyncCallerExecution' will cause nonisolated async initializer 'init' to run on the caller's actor; use @concurrent to preserve behavior}}{{5-5=@concurrent }}{{none}} init(async: ()) async func syncF() - @execution(concurrent) func executionAsyncF() async + @concurrent func executionAsyncF() async @MainActor func mainActorAsyncF() async - // expected-warning@+1:10 {{feature 'AsyncCallerExecution' will cause nonisolated async instance method 'asyncF' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{5-5=@execution(concurrent) }}{{none}} + // expected-warning@+1:10 {{feature 'AsyncCallerExecution' will cause nonisolated async instance method 'asyncF' to run on the caller's actor; use @concurrent to preserve behavior}}{{5-5=@concurrent }}{{none}} func asyncF() async } } protocol Functions {} extension Functions { init(sync: ()) {} - @execution(concurrent) init(executionAsync: ()) async {} + @concurrent init(executionAsync: ()) async {} @MainActor init(mainActorAsync: ()) async {} - // expected-warning@+1:3 {{feature 'AsyncCallerExecution' will cause nonisolated async initializer 'init' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{3-3=@execution(concurrent) }}{{none}} + // expected-warning@+1:3 {{feature 'AsyncCallerExecution' will cause nonisolated async initializer 'init' to run on the caller's actor; use @concurrent to preserve behavior}}{{3-3=@concurrent }}{{none}} init(async: ()) async {} func syncF() {} - @execution(concurrent) func executionAsyncF() async {} + @concurrent func executionAsyncF() async {} @MainActor func mainActorAsyncF() async {} - // expected-warning@+1:8 {{feature 'AsyncCallerExecution' will cause nonisolated async instance method 'asyncF' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{3-3=@execution(concurrent) }}{{none}} + // expected-warning@+1:8 {{feature 'AsyncCallerExecution' will cause nonisolated async instance method 'asyncF' to run on the caller's actor; use @concurrent to preserve behavior}}{{3-3=@concurrent }}{{none}} func asyncF() async {} } @@ -76,17 +75,17 @@ do { var syncS: Int { get {} set {} } subscript(syncS _: Int) -> Int { get {} } - @execution(concurrent) var executionAsyncS: Int { get async {} } - @execution(concurrent) subscript(executionAsyncS _: Int) -> Int { get async {} } + @concurrent var executionAsyncS: Int { get async {} } + @concurrent subscript(executionAsyncS _: Int) -> Int { get async {} } @MainActor var mainActorAsyncS: Int { get async {} } @MainActor subscript(mainActorAsyncS _: Int) -> Int { get async {} } - // expected-warning@+2:7 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for property 'asyncS' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{-1:5-5=@execution(concurrent) }}{{none}} + // expected-warning@+2:7 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for property 'asyncS' to run on the caller's actor; use @concurrent to preserve behavior}}{{-1:5-5=@concurrent }}{{none}} var asyncS: Int { get async {} } - // expected-warning@+2:7 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for subscript 'subscript' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{-1:5-5=@execution(concurrent) }}{{none}} + // expected-warning@+2:7 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for subscript 'subscript' to run on the caller's actor; use @concurrent to preserve behavior}}{{-1:5-5=@concurrent }}{{none}} subscript(asyncS _: Int) -> Int { get async throws {} } @@ -96,15 +95,15 @@ do { var syncS: Int { get } subscript(syncS _: Int) -> Int { get } - @execution(concurrent) var executionAsyncS: Int { get async } - @execution(concurrent) subscript(executionAsyncS _: Int) -> Int { get async } + @concurrent var executionAsyncS: Int { get async } + @concurrent subscript(executionAsyncS _: Int) -> Int { get async } @MainActor var mainActorAsyncS: Int { get async } @MainActor subscript(mainActorAsyncS _: Int) -> Int { get async } - // expected-warning@+1:23 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for property 'asyncS' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{5-5=@execution(concurrent) }}{{none}} + // expected-warning@+1:23 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for property 'asyncS' to run on the caller's actor; use @concurrent to preserve behavior}}{{5-5=@concurrent }}{{none}} var asyncS: Int { get async } - // expected-warning@+1:39 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for subscript 'subscript' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{5-5=@execution(concurrent) }}{{none}} + // expected-warning@+1:39 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for subscript 'subscript' to run on the caller's actor; use @concurrent to preserve behavior}}{{5-5=@concurrent }}{{none}} subscript(asyncS _: Int) -> Int { get async } } } @@ -113,17 +112,17 @@ extension Storage { var syncS: Int { get {} set {} } subscript(syncS _: Int) -> Int { get {} } - @execution(concurrent) var executionAsyncS: Int { get async {} } - @execution(concurrent) subscript(executionAsyncS _: Int) -> Int { get async {} } + @concurrent var executionAsyncS: Int { get async {} } + @concurrent subscript(executionAsyncS _: Int) -> Int { get async {} } @MainActor var mainActorAsyncS: Int { get async {} } @MainActor subscript(mainActorAsyncS _: Int) -> Int { get async {} } - // expected-warning@+2:5 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for property 'asyncS' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{-1:3-3=@execution(concurrent) }}{{none}} + // expected-warning@+2:5 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for property 'asyncS' to run on the caller's actor; use @concurrent to preserve behavior}}{{-1:3-3=@concurrent }}{{none}} var asyncS: Int { get async {} } - // expected-warning@+2:5 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for subscript 'subscript' to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{-1:3-3=@execution(concurrent) }}{{none}} + // expected-warning@+2:5 {{feature 'AsyncCallerExecution' will cause nonisolated async getter for subscript 'subscript' to run on the caller's actor; use @concurrent to preserve behavior}}{{-1:3-3=@concurrent }}{{none}} subscript(asyncS _: Int) -> Int { get async throws {} } @@ -135,11 +134,11 @@ do { enum E { case esac( sync: () -> Void, - executionAsync: @execution(concurrent) () async -> Void, + executionAsync: @concurrent () async -> Void, mainActorAsync: @MainActor () async -> Void, - // expected-warning@+1:14 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{14-14=@execution(concurrent) }}{{none}} + // expected-warning@+1:14 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14=@concurrent }}{{none}} async: () async -> Void, - // expected-warning@+1:26 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{26-26=@execution(concurrent) }}{{none}} + // expected-warning@+1:26 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{26-26=@concurrent }}{{none}} structuralAsync: G<() async -> Void> ) } @@ -147,11 +146,11 @@ do { struct S { subscript( sync: () -> Void, - executionAsync: @execution(concurrent) () async -> Void, + executionAsync: @concurrent () async -> Void, mainActorAsync: @MainActor () async -> Void, - // expected-warning@+1:14 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{14-14=@execution(concurrent) }}{{none}} + // expected-warning@+1:14 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14=@concurrent }}{{none}} async: () async -> Void, - // expected-warning@+1:26 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{26-26=@execution(concurrent) }}{{none}} + // expected-warning@+1:26 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{26-26=@concurrent }}{{none}} structuralAsync: G<() async -> Void> ) -> Int { 0 @@ -160,21 +159,21 @@ do { func foo( sync: () -> Void, - executionAsync: @execution(concurrent) () async -> Void, + executionAsync: @concurrent () async -> Void, mainActorAsync: @MainActor () async -> Void, - // expected-warning@+1:12 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{12-12=@execution(concurrent) }}{{none}} + // expected-warning@+1:12 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{12-12=@concurrent }}{{none}} async: () async -> Void, - // expected-warning@+1:24 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{24-24=@execution(concurrent) }}{{none}} + // expected-warning@+1:24 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{24-24=@concurrent }}{{none}} structuralAsync: G<() async -> Void> ) {} let _ = { ( sync: () -> Void, - executionAsync: @execution(concurrent) () async -> Void, + executionAsync: @concurrent () async -> Void, mainActorAsync: @MainActor () async -> Void, - // expected-warning@+1:12 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{12-12=@execution(concurrent) }}{{none}} + // expected-warning@+1:12 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{12-12=@concurrent }}{{none}} async: () async -> Void, - // expected-warning@+1:24 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{24-24=@execution(concurrent) }}{{none}} + // expected-warning@+1:24 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{24-24=@concurrent }}{{none}} structuralAsync: G<() async -> Void> ) in } @@ -184,11 +183,11 @@ do { do { struct G { struct Sync where T == () -> Void {} - struct ExecutionAsync where T == @execution(concurrent) () async -> Void {} + struct ExecutionAsync where T == @concurrent () async -> Void {} struct MainActorAsync where T == @MainActor () async -> Void {} - // expected-warning@+1:29 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{29-29=@execution(concurrent) }}{{none}} + // expected-warning@+1:29 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{29-29=@concurrent }}{{none}} struct Async where T == () async -> Void {} - // expected-warning@+1:41 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{41-41=@execution(concurrent) }}{{none}} + // expected-warning@+1:41 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{41-41=@concurrent }}{{none}} struct StructuralAsync where T == G<() async -> Void> {} } } @@ -196,11 +195,11 @@ do { // MARK: Variables do { let _: () -> Void - let _: @execution(concurrent) () async -> Void + let _: @concurrent () async -> Void let _: @MainActor () async -> Void - // expected-warning@+1:10 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{10-10=@execution(concurrent) }}{{none}} + // expected-warning@+1:10 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{10-10=@concurrent }}{{none}} let _: () async -> Void - // expected-warning@+1:12 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{12-12=@execution(concurrent) }}{{none}} + // expected-warning@+1:12 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{12-12=@concurrent }}{{none}} let _: G<() async -> Void> } @@ -209,11 +208,11 @@ do { let anything: Any let _ = anything as? () -> Void - let _ = anything as? @execution(concurrent) () async -> Void + let _ = anything as? @concurrent () async -> Void let _ = anything as? @MainActor () async -> Void - // expected-warning@+1:24 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{24-24=@execution(concurrent) }}{{none}} + // expected-warning@+1:24 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{24-24=@concurrent }}{{none}} let _ = anything as? () async -> Void - // expected-warning@+1:26 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{26-26=@execution(concurrent) }}{{none}} + // expected-warning@+1:26 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{26-26=@concurrent }}{{none}} let _ = anything as? G<() async -> Void> } @@ -223,40 +222,40 @@ do { func nonisolatedF() { let _ = { () -> Void in } - let _ = { @execution(concurrent) () async -> Void in } + let _ = { @concurrent () async -> Void in } let _ = { @MainActor () async -> Void in } - // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{15-15=@execution(concurrent) }}{{none}} + // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{15-15=@concurrent }}{{none}} let _ = { () async -> Void in } func takesInts(_: Int...) {} - // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{14-14= @execution(concurrent) in }}{{none}} + // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14= @concurrent in }}{{none}} let _ = {await globalAsyncF()} - // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{14-14= @execution(concurrent) in }}{{none}} + // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14= @concurrent in }}{{none}} let _ = { await globalAsyncF() } - // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{14-14= @execution(concurrent) in }}{{none}} + // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14= @concurrent in }}{{none}} let _ = { await globalAsyncF() takesInts($0, $1, $2) } - // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{25-25=@execution(concurrent) }}{{none}} + // expected-warning@+1:13 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{25-25=@concurrent }}{{none}} let _ = { @Sendable in await globalAsyncF() } - // expected-warning@+2:18 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{18-18=@execution(concurrent) }}{{none}} - // expected-warning@+1:45 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{47-47=@execution(concurrent) }}{{none}} + // expected-warning@+2:18 {{feature 'AsyncCallerExecution' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{18-18=@concurrent }}{{none}} + // expected-warning@+1:45 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{47-47=@concurrent }}{{none}} var closure: (Int, Int) async -> Void = { a, b in await globalAsyncF() } - // expected-warning@+1:15 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{+1:7-7=@execution(concurrent) }}{{none}} + // expected-warning@+1:15 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{+1:7-7=@concurrent }}{{none}} closure = { a, b async in await globalAsyncF() } - // expected-warning@+1:15 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @execution(concurrent) to preserve behavior}}{{17-17=@execution(concurrent) }}{{none}} + // expected-warning@+1:15 {{feature 'AsyncCallerExecution' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{17-17=@concurrent }}{{none}} closure = { (a, b) in await globalAsyncF() } diff --git a/test/Concurrency/attr_execution/attr_execution.swift b/test/Concurrency/attr_execution/attr_execution.swift index 23700de37ed..e7c600789fb 100644 --- a/test/Concurrency/attr_execution/attr_execution.swift +++ b/test/Concurrency/attr_execution/attr_execution.swift @@ -1,17 +1,16 @@ -// RUN: %target-swift-emit-silgen -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -enable-experimental-feature AsyncCallerExecution %s | %FileCheck %s -// REQUIRES: swift_feature_ExecutionAttribute // REQUIRES: swift_feature_AsyncCallerExecution // CHECK-LABEL: // concurrentTest() // CHECK: // Isolation: nonisolated // CHECK: sil hidden [ossa] @$s14attr_execution14concurrentTestyyYaF : $@convention(thin) @async () -> () { -@execution(concurrent) +@concurrent func concurrentTest() async {} // CHECK-LABEL: // callerTest() // CHECK: // Isolation: caller_isolation_inheriting // CHECK: sil hidden [ossa] @$s14attr_execution10callerTestyyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> () { -@execution(caller) +nonisolated(nonsending) func callerTest() async {} diff --git a/test/Concurrency/attr_execution/conversions.swift b/test/Concurrency/attr_execution/conversions.swift index 31137244680..75a996f093a 100644 --- a/test/Concurrency/attr_execution/conversions.swift +++ b/test/Concurrency/attr_execution/conversions.swift @@ -1,69 +1,64 @@ -// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -enable-experimental-feature ExecutionAttribute +// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple // REQUIRES: concurrency -// REQUIRES: swift_feature_ExecutionAttribute @globalActor actor MyActor { static let shared = MyActor() } -@execution(concurrent) +@concurrent func concurrentTest() async { } -@execution(caller) +nonisolated(nonsending) func callerTest() async { } @MainActor func actorIsolated() async {} -let _: @execution(caller) () async -> Void = concurrentTest // Ok -let _: @execution(concurrent) () async -> Void = callerTest // Ok +let _: nonisolated(nonsending) () async -> Void = concurrentTest // Ok +let _: @concurrent () async -> Void = callerTest // Ok let _: @MainActor () async -> Void = concurrentTest // Ok let _: @MainActor () async -> Void = callerTest // Ok let _: @isolated(any) () async -> Void = concurrentTest // Ok let _: @isolated(any) () async -> Void = callerTest -// expected-error@-1 {{cannot convert value of type '@execution(caller) () async -> ()' to specified type '@isolated(any) () async -> Void'}} +// expected-error@-1 {{cannot convert value of type 'nonisolated(nonsending) () async -> ()' to specified type '@isolated(any) () async -> Void'}} -let _: @execution(caller) () async -> Void = actorIsolated // Ok -let _: @execution(concurrent) () async -> Void = actorIsolated // Ok +let _: nonisolated(nonsending) () async -> Void = actorIsolated // Ok +let _: @concurrent () async -> Void = actorIsolated // Ok func testIsolationErasure(fn: @escaping @isolated(any) () async -> Void) { - let _: @execution(concurrent) () async -> Void = fn // Ok - let _: @execution(caller) () async -> Void = fn // Ok + let _: @concurrent () async -> Void = fn // Ok + let _: nonisolated(nonsending) () async -> Void = fn // Ok } -func testUpcast(arr: [@execution(caller) () async -> Void]) { +func testUpcast(arr: [nonisolated(nonsending) () async -> Void]) { let _: [() async -> Void] = arr // Ok - collection upcast let _: [String: () async -> Void] = ["": arr] - // expected-error@-1 {{cannot convert value of type '[@execution(caller) () async -> Void]' to expected dictionary value type '() async -> Void'}} + // expected-error@-1 {{cannot convert value of type '[nonisolated(nonsending) () async -> Void]' to expected dictionary value type '() async -> Void'}} } // Isolated parameter -func testParameterIsolation(fn: @escaping (isolated (any Actor)?) async -> Void, caller: @escaping @execution(caller) (String) async -> Void) { - let _: @execution(caller) () async -> Void = fn - // expected-error@-1 {{cannot convert value of type '(isolated (any Actor)?) async -> Void' to specified type '@execution(caller) () async -> Void'}} - let _: @execution(concurrent) () async -> Void = fn +func testParameterIsolation(fn: @escaping (isolated (any Actor)?) async -> Void, caller: nonisolated(nonsending) @escaping (String) async -> Void) { + let _: nonisolated(nonsending) () async -> Void = fn + // expected-error@-1 {{cannot convert value of type '(isolated (any Actor)?) async -> Void' to specified type 'nonisolated(nonsending) () async -> Void'}} + let _: @concurrent () async -> Void = fn // expected-error@-1 {{cannot convert value of type '(isolated (any Actor)?) async -> Void' to specified type '() async -> Void'}} let _: (isolated (any Actor)?) async -> Void = callerTest // Ok let _: (isolated (any Actor)?) -> Void = callerTest - // expected-error@-1 {{invalid conversion from 'async' function of type '@execution(caller) () async -> ()' to synchronous function type '(isolated (any Actor)?) -> Void'}} + // expected-error@-1 {{invalid conversion from 'async' function of type 'nonisolated(nonsending) () async -> ()' to synchronous function type '(isolated (any Actor)?) -> Void'}} let _: (isolated (any Actor)?) async -> Void = concurrentTest // expected-error@-1 {{cannot convert value of type '() async -> ()' to specified type '(isolated (any Actor)?) async -> Void'}} let _: (isolated (any Actor)?, Int) async -> Void = callerTest - // expected-error@-1 {{cannot convert value of type '@execution(caller) () async -> ()' to specified type '(isolated (any Actor)?, Int) async -> Void'}} + // expected-error@-1 {{cannot convert value of type 'nonisolated(nonsending) () async -> ()' to specified type '(isolated (any Actor)?, Int) async -> Void'}} let _: (String, isolated any Actor) async -> Void = caller // Ok let _: (isolated (any Actor)?, String) async -> Void = caller // Ok - - let _: (Int, isolated any Actor) async -> Void = { @execution(caller) x in } // Ok - let _: (Int, isolated any Actor) async -> Void = { @execution(caller) (x: Int) in } // Ok - let _: (isolated any Actor, Int, String) async -> Void = { @execution(caller) (x: Int, _: String) in } // Ok } // Non-conversion situations @@ -73,19 +68,19 @@ do { func test(_: S, _: T.Type) {} test(S<() async -> Void>(), type(of: callerTest)) - // expected-error@-1 {{cannot convert value of type '(@execution(caller) () async -> ()).Type' to expected argument type '(() async -> Void).Type'}} + // expected-error@-1 {{cannot convert value of type '(nonisolated(nonsending) () async -> ()).Type' to expected argument type '(() async -> Void).Type'}} - test(S<@execution(caller) () async -> Void>(), type(of: concurrentTest)) - // expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(@execution(caller) () async -> Void).Type'}} + test(S Void>(), type(of: concurrentTest)) + // expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(nonisolated(nonsending) () async -> Void).Type'}} test(S<@MainActor () async -> Void>(), type(of: callerTest)) - // expected-error@-1 {{cannot convert value of type '(@execution(caller) () async -> ()).Type' to expected argument type '(@MainActor () async -> Void).Type'}} + // expected-error@-1 {{cannot convert value of type '(nonisolated(nonsending) () async -> ()).Type' to expected argument type '(@MainActor () async -> Void).Type'}} test(S<@MainActor () async -> Void>(), type(of: concurrentTest)) // expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(@MainActor () async -> Void).Type'}} test(S<(isolated (any Actor)?) async -> Void>(), type(of: callerTest)) - // expected-error@-1 {{cannot convert value of type '(@execution(caller) () async -> ()).Type' to expected argument type '((isolated (any Actor)?) async -> Void).Type'}} + // expected-error@-1 {{cannot convert value of type '(nonisolated(nonsending) () async -> ()).Type' to expected argument type '((isolated (any Actor)?) async -> Void).Type'}} test(S<(isolated (any Actor)?) async -> Void>(), type(of: concurrentTest)) // expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '((isolated (any Actor)?) async -> Void).Type'}} @@ -93,22 +88,16 @@ do { test(S<@isolated(any) () async -> Void>(), type(of: concurrentTest)) // expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(@isolated(any) () async -> Void).Type'}} test(S<@isolated(any) () async -> Void>(), type(of: callerTest)) - // expected-error@-1 {{cannot convert value of type '(@execution(caller) () async -> ()).Type' to expected argument type '(@isolated(any) () async -> Void).Type'}} + // expected-error@-1 {{cannot convert value of type '(nonisolated(nonsending) () async -> ()).Type' to expected argument type '(@isolated(any) () async -> Void).Type'}} } do { - let _: () -> Void = { @execution(concurrent) in + let _: () -> Void = { @concurrent in // expected-error@-1 {{invalid conversion from 'async' function of type '() async -> Void' to synchronous function type '() -> Void'}} } - - func test(_: () -> Void) {} - - test { @execution(caller) in - // expected-error@-1 {{cannot pass function of type '@execution(caller) () async -> ()' to parameter expecting synchronous function type}} - } } -// Converting to `@execution(caller)` function +// Converting to `nonisolated(nonsending)` function class NonSendable {} func testNonSendableDiagnostics( @@ -117,40 +106,40 @@ func testNonSendableDiagnostics( erased1: @escaping @Sendable @isolated(any) (NonSendable) async -> Void, erased2: @escaping @Sendable @isolated(any) () async -> NonSendable, nonIsolated1: @escaping @Sendable (NonSendable) -> Void, - nonIsolated2: @escaping @Sendable @execution(concurrent) (NonSendable) async -> Void, + nonIsolated2: @escaping @Sendable @concurrent (NonSendable) async -> Void, nonIsolated3: @escaping @Sendable () -> NonSendable, - nonIsolated4: @escaping @Sendable @execution(concurrent) () async -> NonSendable, - caller1: @escaping @Sendable @execution(caller) (NonSendable) async -> Void, - caller2: @escaping @Sendable @execution(caller) () async -> NonSendable + nonIsolated4: @escaping @Sendable @concurrent () async -> NonSendable, + caller1: nonisolated(nonsending) @escaping @Sendable (NonSendable) async -> Void, + caller2: nonisolated(nonsending) @escaping @Sendable () async -> NonSendable ) { - let _: @execution(caller) (NonSendable) async -> Void = globalActor1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} - // expected-error@-1 {{cannot convert '@MainActor @Sendable (NonSendable) async -> Void' to '@execution(caller) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} - let _: @execution(caller) () async -> NonSendable = globalActor2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} - // expected-error@-1 {{cannot convert '@MainActor @Sendable () async -> NonSendable' to '@execution(caller) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} + let _: nonisolated(nonsending) (NonSendable) async -> Void = globalActor1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + // expected-error@-1 {{cannot convert '@MainActor @Sendable (NonSendable) async -> Void' to 'nonisolated(nonsending) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} + let _: nonisolated(nonsending) () async -> NonSendable = globalActor2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + // expected-error@-1 {{cannot convert '@MainActor @Sendable () async -> NonSendable' to 'nonisolated(nonsending) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} - let _: @execution(caller) (NonSendable) async -> Void = erased1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} - // expected-error@-1 {{cannot convert '@isolated(any) @Sendable (NonSendable) async -> Void' to '@execution(caller) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} - let _: @execution(caller) () async -> NonSendable = erased2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} - // expected-error@-1 {{cannot convert '@isolated(any) @Sendable () async -> NonSendable' to '@execution(caller) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} + let _: nonisolated(nonsending) (NonSendable) async -> Void = erased1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + // expected-error@-1 {{cannot convert '@isolated(any) @Sendable (NonSendable) async -> Void' to 'nonisolated(nonsending) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} + let _: nonisolated(nonsending) () async -> NonSendable = erased2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + // expected-error@-1 {{cannot convert '@isolated(any) @Sendable () async -> NonSendable' to 'nonisolated(nonsending) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} - let _: @execution(caller) (NonSendable) async -> Void = nonIsolated1 // Ok - let _: @execution(caller) (NonSendable) async -> Void = nonIsolated2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} - // expected-error@-1 {{cannot convert '@Sendable (NonSendable) async -> Void' to '@execution(caller) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} + let _: nonisolated(nonsending) (NonSendable) async -> Void = nonIsolated1 // Ok + let _: nonisolated(nonsending) (NonSendable) async -> Void = nonIsolated2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + // expected-error@-1 {{cannot convert '@Sendable (NonSendable) async -> Void' to 'nonisolated(nonsending) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} - let _: @execution(caller) () async -> NonSendable = nonIsolated3 // Ok - let _: @execution(caller) () async -> NonSendable = nonIsolated4 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} - // expected-error@-1 {{cannot convert '@Sendable () async -> NonSendable' to '@execution(caller) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} + let _: nonisolated(nonsending) () async -> NonSendable = nonIsolated3 // Ok + let _: nonisolated(nonsending) () async -> NonSendable = nonIsolated4 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + // expected-error@-1 {{cannot convert '@Sendable () async -> NonSendable' to 'nonisolated(nonsending) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} - let _: @execution(concurrent) (NonSendable) async -> Void = erased1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + let _: @concurrent (NonSendable) async -> Void = erased1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} // expected-warning@-1 {{cannot convert '@isolated(any) @Sendable (NonSendable) async -> Void' to '(NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} - let _: @execution(concurrent) () async -> NonSendable = erased2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + let _: @concurrent () async -> NonSendable = erased2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} // expected-warning@-1 {{cannot convert '@isolated(any) @Sendable () async -> NonSendable' to '() async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} - let _: @execution(concurrent) (NonSendable) async -> Void = caller1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} - // expected-warning@-1 {{cannot convert '@execution(caller) @Sendable (NonSendable) async -> Void' to '(NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} - let _: @execution(concurrent) () async -> NonSendable = caller2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} - // expected-warning@-1 {{cannot convert '@execution(caller) @Sendable () async -> NonSendable' to '() async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} + let _: @concurrent (NonSendable) async -> Void = caller1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + // expected-warning@-1 {{cannot convert 'nonisolated(nonsending) @Sendable (NonSendable) async -> Void' to '(NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} + let _: @concurrent () async -> NonSendable = caller2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} + // expected-warning@-1 {{cannot convert 'nonisolated(nonsending) @Sendable () async -> NonSendable' to '() async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}} let _: @MainActor (NonSendable) async -> Void = nonIsolated1 // Ok let _: @MainActor (NonSendable) async -> Void = nonIsolated2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}} diff --git a/test/Concurrency/attr_execution/conversions_silgen.swift b/test/Concurrency/attr_execution/conversions_silgen.swift index 4ed0ec15f13..64cc5b920d1 100644 --- a/test/Concurrency/attr_execution/conversions_silgen.swift +++ b/test/Concurrency/attr_execution/conversions_silgen.swift @@ -1,21 +1,20 @@ -// RUN: %target-swift-emit-silgen %s -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple -enable-experimental-feature ExecutionAttribute -DSWIFT_FIVE | %FileCheck -check-prefix CHECK -check-prefix FIVE %s -// RUN: %target-swift-emit-silgen %s -swift-version 6 -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple -enable-experimental-feature ExecutionAttribute | %FileCheck -check-prefix CHECK -check-prefix SIX %s +// RUN: %target-swift-emit-silgen %s -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple -DSWIFT_FIVE | %FileCheck -check-prefix CHECK -check-prefix FIVE %s +// RUN: %target-swift-emit-silgen %s -swift-version 6 -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple | %FileCheck -check-prefix CHECK -check-prefix SIX %s // We codegen slightly differently for swift 5 vs swift 6, so we need to check // both. // REQUIRES: asserts // REQUIRES: concurrency -// REQUIRES: swift_feature_ExecutionAttribute //////////////////////// // MARK: Declarations // //////////////////////// -@execution(caller) +nonisolated(nonsending) func globalCallerFunc() async -> () {} -@execution(concurrent) +@concurrent func globalConcurrentFunc() async -> () {} class NonSendableKlass { @@ -25,16 +24,16 @@ class SendableKlass : @unchecked Sendable { init() {} } -@execution(caller) +nonisolated(nonsending) func globalCallerFuncSendableKlass(_ x: SendableKlass) async -> () {} -@execution(concurrent) +@concurrent func globalConcurrentFuncSendableKlass(_ x: SendableKlass) async -> () {} -@execution(caller) +nonisolated(nonsending) func globalCallerFuncSendableKlass(_ x: SendableKlass) async -> SendableKlass { fatalError() } -@execution(concurrent) +@concurrent func globalConcurrentFuncSendableKlass(_ x: SendableKlass) async -> SendableKlass { fatalError() } @@ -48,8 +47,8 @@ func globalConcurrentFuncSendableKlass(_ x: SendableKlass) async -> SendableKlas // CHECK: [[THUNK:%.*]] = function_ref @$sScA_pSgIegHg_IegH_TR : $@convention(thin) @async (@guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()) -> () // CHECK: partial_apply [callee_guaranteed] [[THUNK]]([[FUNC_COPY]]) // CHECK: } // end sil function '$s21attr_execution_silgen33testCallerToConcurrentNonIsolatedyyyyYaYCcYaF' -public func testCallerToConcurrentNonIsolated(_ x: @escaping @execution(caller) () async -> ()) async { - let y: @execution(concurrent) () async -> () = x +public func testCallerToConcurrentNonIsolated(_ x: nonisolated(nonsending) @escaping () async -> ()) async { + let y: @concurrent () async -> () = x await y() } @@ -69,8 +68,8 @@ public func testCallerToConcurrentNonIsolated(_ x: @escaping @execution(caller) // CHECK: partial_apply [callee_guaranteed] [[THUNK]]([[FUNC_COPY]]) // CHECK: } // end sil function '$s21attr_execution_silgen31testCallerToConcurrentMainActoryyyyYaYCcYaF' @MainActor -public func testCallerToConcurrentMainActor(_ x: @escaping @execution(caller) () async -> ()) async { - let y: @execution(concurrent) () async -> () = x +public func testCallerToConcurrentMainActor(_ x: nonisolated(nonsending) @escaping () async -> ()) async { + let y: @concurrent () async -> () = x await y() } @@ -80,8 +79,8 @@ public func testCallerToConcurrentMainActor(_ x: @escaping @execution(caller) () // CHECK: [[THUNK:%.*]] = function_ref @$sIegH_ScA_pSgIegHg_TR : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed @async @callee_guaranteed () -> ()) -> () // CHECK: partial_apply [callee_guaranteed] [[THUNK]]([[FUNC_COPY]]) // CHECK: } // end sil function '$s21attr_execution_silgen33testConcurrentToCallerNonIsolatedyyyyYacYaF' -public func testConcurrentToCallerNonIsolated(_ x: @escaping @execution(concurrent) () async -> ()) async { - let y: @execution(caller) () async -> () = x +public func testConcurrentToCallerNonIsolated(_ x: @escaping @concurrent () async -> ()) async { + let y: nonisolated(nonsending) () async -> () = x await y() } @@ -100,8 +99,8 @@ public func testConcurrentToCallerNonIsolated(_ x: @escaping @execution(concurre // CHECK: partial_apply [callee_guaranteed] [[THUNK]]([[FUNC_COPY]]) // CHECK: } // end sil function '$s21attr_execution_silgen42testConcurrentToCallerNonIsolatedMainActoryyyyYacYaF' @MainActor -public func testConcurrentToCallerNonIsolatedMainActor(_ x: @escaping @execution(concurrent) () async -> ()) async { - let y: @execution(caller) () async -> () = x +public func testConcurrentToCallerNonIsolatedMainActor(_ x: @escaping @concurrent () async -> ()) async { + let y: nonisolated(nonsending) () async -> () = x await y() } @@ -124,10 +123,10 @@ public func testConcurrentToCallerNonIsolatedMainActor(_ x: @escaping @execution // CHECK: [[BORROW_COPY_Z:%.*]] = begin_borrow [[COPY_Z]] // CHECK: apply [[BORROW_COPY_Z]]() // CHECK: } // end sil function '$s21attr_execution_silgen016testConcurrentToE0yyyyYacYaF' -public func testConcurrentToConcurrent(_ x: @escaping @execution(concurrent) () async -> ()) async { - let y: @execution(concurrent) () async -> () = x +public func testConcurrentToConcurrent(_ x: @escaping @concurrent () async -> ()) async { + let y: @concurrent () async -> () = x await y() - let z: @execution(concurrent) () async -> () = globalConcurrentFunc + let z: @concurrent () async -> () = globalConcurrentFunc await z() } @@ -142,10 +141,10 @@ public func testConcurrentToConcurrent(_ x: @escaping @execution(concurrent) () // CHECK: } // end sil function '$s21attr_execution_silgen012testCallerToE0yyyyYaYCcYaF' // // z has a round trip issue. -public func testCallerToCaller(_ x: @escaping @execution(caller) () async -> ()) async { - let y: @execution(caller) () async -> () = x +public func testCallerToCaller(_ x: nonisolated(nonsending) @escaping () async -> ()) async { + let y: nonisolated(nonsending) () async -> () = x await y() - let z: @execution(caller) () async -> () = globalCallerFunc + let z: nonisolated(nonsending) () async -> () = globalCallerFunc await z() } @@ -162,9 +161,9 @@ public func testCallerToCaller(_ x: @escaping @execution(caller) () async -> ()) // CHECK: [[Y2_B_C_B:%.*]] = begin_borrow [[Y2_B_C]] // CHECK: apply [[Y2_B_C_B]]([[ACTOR]]) // CHECK: } // end sil function '$s21attr_execution_silgen24testCallerLocalVariablesyyyyYaYCcYaF' -public func testCallerLocalVariables(_ x: @escaping @execution(caller) () async -> ()) async { - let y: @execution(caller) () async -> () = x - let y2: @execution(caller) () async -> () = y +public func testCallerLocalVariables(_ x: nonisolated(nonsending) @escaping () async -> ()) async { + let y: nonisolated(nonsending) () async -> () = x + let y2: nonisolated(nonsending) () async -> () = y await y2() } @@ -179,9 +178,9 @@ public func testCallerLocalVariables(_ x: @escaping @execution(caller) () async // CHECK: [[Y2_B_C_B:%.*]] = begin_borrow [[Y2_B_C]] // CHECK: apply [[Y2_B_C_B]]() // CHECK: } // end sil function '$s21attr_execution_silgen28testConcurrentLocalVariablesyyyyYacYaF' -public func testConcurrentLocalVariables(_ x: @escaping @execution(concurrent) () async -> ()) async { - let y: @execution(concurrent) () async -> () = x - let y2: @execution(concurrent) () async -> () = y +public func testConcurrentLocalVariables(_ x: @escaping @concurrent () async -> ()) async { + let y: @concurrent () async -> () = x + let y2: @concurrent () async -> () = y await y2() } @@ -196,9 +195,9 @@ public func testConcurrentLocalVariables(_ x: @escaping @execution(concurrent) ( // CHECK: [[THUNK_2:%.*]] = function_ref @$sIegH_ScA_pSgIegHg_TR : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed @async @callee_guaranteed () -> ()) -> () // CHECK: [[PA_2:%.*]] = partial_apply [callee_guaranteed] [[THUNK_2]]([[Y_B_C]]) // CHECK: } // end sil function '$s21attr_execution_silgen34testCallerConcurrentLocalVariablesyyyyYaYCcYaF' -public func testCallerConcurrentLocalVariables(_ x: @escaping @execution(caller) () async -> ()) async { - let y: @execution(concurrent) () async -> () = x - let y2: @execution(caller) () async -> () = y +public func testCallerConcurrentLocalVariables(_ x: nonisolated(nonsending) @escaping () async -> ()) async { + let y: @concurrent () async -> () = x + let y2: nonisolated(nonsending) () async -> () = y await y2() } @@ -213,9 +212,9 @@ public func testCallerConcurrentLocalVariables(_ x: @escaping @execution(caller) // CHECK: [[THUNK_2:%.*]] = function_ref @$sScA_pSgIegHg_IegH_TR : $@convention(thin) @async (@guaranteed @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()) -> () // CHECK: [[PA_2:%.*]] = partial_apply [callee_guaranteed] [[THUNK_2]]([[Y_B_C]]) // CHECK: } // end sil function '$s21attr_execution_silgen34testConcurrentCallerLocalVariablesyyyyYacYaF' -public func testConcurrentCallerLocalVariables(_ x: @escaping @execution(concurrent) () async -> ()) async { - let y: @execution(caller) () async -> () = x - let y2: @execution(concurrent) () async -> () = y +public func testConcurrentCallerLocalVariables(_ x: @escaping @concurrent () async -> ()) async { + let y: nonisolated(nonsending) () async -> () = x + let y2: @concurrent () async -> () = y await y2() } @@ -261,8 +260,8 @@ public func testConcurrentCallerLocalVariables(_ x: @escaping @execution(concurr // FIVE: apply [[V4_B_C_B]]() // CHECK: } // end sil function '$s21attr_execution_silgen22globalActorConversionsyyyyYac_yyYaYCctYaF' -func globalActorConversions(_ x: @escaping @execution(concurrent) () async -> (), - _ y: @escaping @execution(caller) () async -> ()) async { +func globalActorConversions(_ x: @escaping @concurrent () async -> (), + _ y: nonisolated(nonsending) @escaping () async -> ()) async { let v1: @MainActor () async -> Void = globalCallerFunc await v1() let v2: @MainActor () async -> Void = globalConcurrentFunc @@ -320,8 +319,8 @@ func globalActorConversions(_ x: @escaping @execution(concurrent) () async -> () // FIVE: apply [[V4_B_C_B]]({{%.*}}) // CHECK: } // end sil function '$s21attr_execution_silgen23globalActorConversions2yyyAA13SendableKlassCYac_yADYaYCctYaF' -func globalActorConversions2(_ x: @escaping @execution(concurrent) (SendableKlass) async -> (), - _ y: @escaping @execution(caller) (SendableKlass) async -> ()) async { +func globalActorConversions2(_ x: @escaping @concurrent (SendableKlass) async -> (), + _ y: nonisolated(nonsending) @escaping (SendableKlass) async -> ()) async { let v1: @MainActor (SendableKlass) async -> Void = globalCallerFuncSendableKlass await v1(SendableKlass()) let v2: @MainActor (SendableKlass) async -> Void = globalConcurrentFuncSendableKlass @@ -332,7 +331,7 @@ func globalActorConversions2(_ x: @escaping @execution(concurrent) (SendableKlas let v4: @MainActor (SendableKlass) async -> Void = y await v4(SendableKlass()) #endif - let v5: @execution(concurrent) (SendableKlass) async -> Void = y + let v5: @concurrent (SendableKlass) async -> Void = y await v5(SendableKlass()) } @@ -381,8 +380,8 @@ func globalActorConversions2(_ x: @escaping @execution(concurrent) (SendableKlas // CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[Y_C]]) // CHECK: [[V5:%.*]] = move_value [lexical] [var_decl] [[PA]] // CHECK: } // end sil function '$s21attr_execution_silgen23globalActorConversions3yyAA13SendableKlassCADYac_A2DYaYCctYaF' -func globalActorConversions3(_ x: @escaping @execution(concurrent) (SendableKlass) async -> SendableKlass, - _ y: @escaping @execution(caller) (SendableKlass) async -> SendableKlass) async { +func globalActorConversions3(_ x: @escaping @concurrent (SendableKlass) async -> SendableKlass, + _ y: nonisolated(nonsending) @escaping (SendableKlass) async -> SendableKlass) async { let v1: @MainActor (SendableKlass) async -> SendableKlass = globalCallerFuncSendableKlass _ = await v1(SendableKlass()) let v2: @MainActor (SendableKlass) async -> SendableKlass = globalConcurrentFuncSendableKlass @@ -393,7 +392,7 @@ func globalActorConversions3(_ x: @escaping @execution(concurrent) (SendableKlas let v4: @MainActor (SendableKlass) async -> SendableKlass = y _ = await v4(SendableKlass()) #endif - let v5: @execution(concurrent) (SendableKlass) async -> SendableKlass = y + let v5: @concurrent (SendableKlass) async -> SendableKlass = y _ = await v5(SendableKlass()) } @@ -421,8 +420,8 @@ func globalActorConversions3(_ x: @escaping @execution(concurrent) (SendableKlas func conversionsFromSyncToAsync(_ x: @escaping @Sendable (NonSendableKlass) -> Void, _ y: @escaping @MainActor @Sendable (SendableKlass) -> Void, _ z: @escaping @MainActor @Sendable (NonSendableKlass) -> Void) async { - let _: @execution(caller) (NonSendableKlass) async -> Void = x - let _: @execution(caller) (SendableKlass) async -> Void = y - let _: @execution(concurrent) (SendableKlass) async -> Void = y - let _: @execution(concurrent) (NonSendableKlass) async -> Void = z + let _: nonisolated(nonsending) (NonSendableKlass) async -> Void = x + let _: nonisolated(nonsending) (SendableKlass) async -> Void = y + let _: @concurrent (SendableKlass) async -> Void = y + let _: @concurrent (NonSendableKlass) async -> Void = z } diff --git a/test/Concurrency/attr_execution/protocols_silgen.swift b/test/Concurrency/attr_execution/protocols_silgen.swift index 78e81d36d14..37bdaa54649 100644 --- a/test/Concurrency/attr_execution/protocols_silgen.swift +++ b/test/Concurrency/attr_execution/protocols_silgen.swift @@ -1,16 +1,15 @@ -// RUN: %target-swift-emit-silgen %s -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple -enable-experimental-feature ExecutionAttribute -DSWIFT_FIVE | %FileCheck -check-prefix CHECK -check-prefix FIVE %s -// RUN: %target-swift-emit-silgen %s -swift-version 6 -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple -enable-experimental-feature ExecutionAttribute | %FileCheck -check-prefix CHECK -check-prefix SIX %s +// RUN: %target-swift-emit-silgen %s -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple -DSWIFT_FIVE | %FileCheck -check-prefix CHECK -check-prefix FIVE %s +// RUN: %target-swift-emit-silgen %s -swift-version 6 -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple | %FileCheck -check-prefix CHECK -check-prefix SIX %s // We codegen slightly differently for swift 5 vs swift 6, so we need to check // both. // REQUIRES: asserts // REQUIRES: concurrency -// REQUIRES: swift_feature_ExecutionAttribute protocol P { - @execution(caller) func callerTest() async - @execution(concurrent) func concurrentTest() async + nonisolated(nonsending) func callerTest() async + @concurrent func concurrentTest() async @MainActor func mainActorTest() async } @@ -47,7 +46,7 @@ struct AllCaller : P { // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen9AllCallerV10callerTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, AllCaller) -> () // CHECK: apply [[FUNC]]([[ACTOR]], [[LOAD]]) // CHECK: } // end sil function '$s21attr_execution_silgen9AllCallerVAA1PA2aDP10callerTestyyYaFTW' - @execution(caller) func callerTest() async {} + nonisolated(nonsending) func callerTest() async {} // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen9AllCallerVAA1PA2aDP14concurrentTestyyYaFTW : $@convention(witness_method: P) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @in_guaranteed AllCaller) -> () { // CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional, [[SELF:%.*]] : $*AllCaller): @@ -56,7 +55,7 @@ struct AllCaller : P { // CHECK: [[NIL:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK: apply [[FUNC]]([[NIL]], [[LOAD]]) // CHECK: } // end sil function '$s21attr_execution_silgen9AllCallerVAA1PA2aDP14concurrentTestyyYaFTW' - @execution(caller) func concurrentTest() async {} + nonisolated(nonsending) func concurrentTest() async {} // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen9AllCallerVAA1PA2aDP13mainActorTestyyYaFTW : $@convention(witness_method: P) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @in_guaranteed AllCaller) -> () { // CHECK: bb0({{%.*}} : @guaranteed $Optional, [[SELF:%.*]] : $*AllCaller): @@ -67,7 +66,7 @@ struct AllCaller : P { // CHECK: [[OPT_MAIN_ACTOR:%.*]] = enum $Optional, #Optional.some!enumelt, [[EXIS_MAIN_ACTOR]] // CHECK: apply [[FUNC]]([[OPT_MAIN_ACTOR]], [[LOAD]]) // CHECK: } // end sil function '$s21attr_execution_silgen9AllCallerVAA1PA2aDP13mainActorTestyyYaFTW' - @execution(caller) func mainActorTest() async {} + nonisolated(nonsending) func mainActorTest() async {} } struct AllConcurrent : P { @@ -80,7 +79,7 @@ struct AllConcurrent : P { // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen13AllConcurrentV10callerTestyyYaF : $@convention(method) @async (AllConcurrent) -> () // CHECK: apply [[FUNC]]([[LOAD]]) // CHECK: } // end sil function '$s21attr_execution_silgen13AllConcurrentVAA1PA2aDP10callerTestyyYaFTW' - @execution(concurrent) func callerTest() async {} + @concurrent func callerTest() async {} // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen13AllConcurrentVAA1PA2aDP14concurrentTestyyYaFTW : $@convention(witness_method: P) @async (@in_guaranteed AllConcurrent) -> () { // CHECK: bb0([[SELF:%.*]] : @@ -88,7 +87,7 @@ struct AllConcurrent : P { // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen13AllConcurrentV14concurrentTestyyYaF : $@convention(method) @async (AllConcurrent) -> () // CHECK: apply [[FUNC]]([[LOAD]]) // CHECK: } // end sil function '$s21attr_execution_silgen13AllConcurrentVAA1PA2aDP14concurrentTestyyYaFTW' - @execution(concurrent) func concurrentTest() async {} + @concurrent func concurrentTest() async {} // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen13AllConcurrentVAA1PA2aDP13mainActorTestyyYaFTW : $@convention(witness_method: P) @async (@in_guaranteed AllConcurrent) -> () { // CHECK: bb0([[SELF:%.*]] : @@ -96,7 +95,7 @@ struct AllConcurrent : P { // CHECK: [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen13AllConcurrentV13mainActorTestyyYaF : $@convention(method) @async (AllConcurrent) -> () // CHECK: apply [[FUNC]]([[LOAD]]) // CHECK: } // end sil function '$s21attr_execution_silgen13AllConcurrentVAA1PA2aDP13mainActorTestyyYaFTW' - @execution(concurrent) func mainActorTest() async {} + @concurrent func mainActorTest() async {} } struct AllMainActor : P { diff --git a/test/Concurrency/concurrent_value_checking.swift b/test/Concurrency/concurrent_value_checking.swift index 811e07cd7f2..22136c8c5a4 100644 --- a/test/Concurrency/concurrent_value_checking.swift +++ b/test/Concurrency/concurrent_value_checking.swift @@ -305,7 +305,7 @@ var concurrentFuncVar: (@Sendable (NotConcurrent) -> Void)? = nil // expected-wa // ---------------------------------------------------------------------- func acceptConcurrentUnary(_: @Sendable (T) -> T) { } -func concurrentClosures(_: T) { // expected-note{{consider making generic parameter 'T' conform to the 'Sendable' protocol}} {{26-26=: Sendable}} +func concurrentClosures(_: T) { // expected-note{{consider making generic parameter 'T' conform to the 'Sendable' protocol}} {{44-44= & Sendable}} acceptConcurrentUnary { (x: T) in _ = x // ok acceptConcurrentUnary { _ in x } // expected-warning{{capture of 'x' with non-sendable type 'T' in a '@Sendable' closure}} diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift index d76b30c13a1..25411b6864c 100644 --- a/test/Concurrency/global_actor_inference.swift +++ b/test/Concurrency/global_actor_inference.swift @@ -127,6 +127,7 @@ protocol Interface { @MainActor class Object: Interface { // expected-note@-1{{turn data races into runtime errors with '@preconcurrency'}}{{15-15=@preconcurrency }} + // expected-note@-2{{isolate this conformance to the main actor with '@MainActor'}} var baz: Int = 42 // expected-note{{main actor-isolated property 'baz' cannot satisfy nonisolated requirement}} } diff --git a/test/Concurrency/global_actor_inference_swift6.swift b/test/Concurrency/global_actor_inference_swift6.swift index fb795a446a3..8ddf537e0d8 100644 --- a/test/Concurrency/global_actor_inference_swift6.swift +++ b/test/Concurrency/global_actor_inference_swift6.swift @@ -207,6 +207,7 @@ protocol InferenceConflictWithSuperclass: MainActorSuperclass, InferSomeGlobalAc class C2: MainActorSuperclass, InferenceConflictWithSuperclass { //expected-note@-1 {{turn data races into runtime errors with '@preconcurrency'}} // expected-note@-2{{mark all declarations used in the conformance 'nonisolated'}} + // expected-note@-3{{isolate this conformance to the main actor with '@MainActor'}} func f() {} diff --git a/test/Concurrency/isolated_conformance.swift b/test/Concurrency/isolated_conformance.swift index 03c702f2a17..92343133eaa 100644 --- a/test/Concurrency/isolated_conformance.swift +++ b/test/Concurrency/isolated_conformance.swift @@ -1,6 +1,5 @@ -// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 -enable-experimental-feature IsolatedConformances %s +// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 5 -strict-concurrency=complete %s -// REQUIRES: swift_feature_IsolatedConformances // REQUIRES: concurrency protocol P { @@ -11,7 +10,7 @@ protocol P { // Definition of isolated conformances // ---------------------------------------------------------------------------- -// expected-error@+4{{conformance of 'CWithNonIsolated' to protocol 'P' crosses into main actor-isolated code and can cause data races}} +// expected-warning@+4{{conformance of 'CWithNonIsolated' to protocol 'P' crosses into main actor-isolated code and can cause data races}} // expected-note@+3{{mark all declarations used in the conformance 'nonisolated'}} // expected-note@+2{{isolate this conformance to the main actor with '@MainActor'}}{{25-25=@MainActor }} @MainActor @@ -54,7 +53,7 @@ protocol Q { associatedtype A: P } -// expected-error@+2{{conformance of 'SMissingIsolation' to protocol 'Q' crosses into main actor-isolated code and can cause data races}} +// expected-warning@+2{{conformance of 'SMissingIsolation' to protocol 'Q' crosses into main actor-isolated code and can cause data races}} @MainActor struct SMissingIsolation: Q { // expected-note@-1{{conformance depends on main actor-isolated conformance of 'C' to protocol 'P'}} @@ -66,7 +65,7 @@ struct PWrapper: P { func f() { } } -// expected-error@+2{{conformance of 'SMissingIsolationViaWrapper' to protocol 'Q' crosses into main actor-isolated code and can cause data races}} +// expected-warning@+2{{conformance of 'SMissingIsolationViaWrapper' to protocol 'Q' crosses into main actor-isolated code and can cause data races}} @MainActor struct SMissingIsolationViaWrapper: Q { // expected-note@-1{{conformance depends on main actor-isolated conformance of 'C' to protocol 'P'}} @@ -84,7 +83,7 @@ struct S: @MainActor Q { typealias A = C } -// expected-error@+3{{conformance of 'SMismatchedActors' to protocol 'Q' crosses into global actor 'SomeGlobalActor'-isolated code and can cause data races}} +// expected-warning@+3{{conformance of 'SMismatchedActors' to protocol 'Q' crosses into global actor 'SomeGlobalActor'-isolated code and can cause data races}} // expected-note@+2{{conformance depends on global actor 'SomeGlobalActor'-isolated conformance of 'C2' to protocol 'P'}} @MainActor struct SMismatchedActors: @MainActor Q { @@ -149,7 +148,41 @@ func testIsolatedConformancesOfOtherGlobalActor(c: CMismatchedIsolation) { } func testIsolationConformancesFromOutside(c: C) { - acceptP(c) // expected-error{{main actor-isolated conformance of 'C' to 'P' cannot be used in nonisolated context}} - let _: any P = c // expected-error{{main actor-isolated conformance of 'C' to 'P' cannot be used in nonisolated context}} - let _ = PWrapper() // expected-error{{main actor-isolated conformance of 'C' to 'P' cannot be used in nonisolated context}} + acceptP(c) // expected-warning{{main actor-isolated conformance of 'C' to 'P' cannot be used in nonisolated context}} + let _: any P = c // expected-warning{{main actor-isolated conformance of 'C' to 'P' cannot be used in nonisolated context}} + let _ = PWrapper() // expected-warning{{main actor-isolated conformance of 'C' to 'P' cannot be used in nonisolated context}} +} + +protocol HasAssociatedType { + associatedtype A +} + +func acceptHasAssocWithP(_: T) where T.A: P { } + +func acceptSendableHasAssocWithP(_: T) where T.A: P { } +// expected-note@-1{{'acceptSendableHasAssocWithP' declared here}} + + +struct HoldsC: HasAssociatedType { + typealias A = C +} + +extension HasAssociatedType { + static func acceptAliased(_: T.Type) where A == T { } +} + +extension HasAssociatedType where Self: Sendable { + static func acceptSendableAliased(_: T.Type) where A == T { } +} + +func testIsolatedConformancesOnAssociatedTypes(hc: HoldsC, c: C) { + acceptHasAssocWithP(hc) + acceptSendableHasAssocWithP(hc) // expected-error{{main actor-isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a 'Sendable' type parameter }} + + HoldsC.acceptAliased(C.self) // okay + + // FIXME: the following should produce an error, because the isolated + // conformance of C: P can cross isolation boundaries via the Sendable Self's + // associated type. + HoldsC.acceptSendableAliased(C.self) } diff --git a/test/Concurrency/isolated_conformance_default_actor.swift b/test/Concurrency/isolated_conformance_default_actor.swift index 3b302234207..ce8cce07ecc 100644 --- a/test/Concurrency/isolated_conformance_default_actor.swift +++ b/test/Concurrency/isolated_conformance_default_actor.swift @@ -1,6 +1,5 @@ -// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 -enable-experimental-feature IsolatedConformances -default-isolation MainActor %s +// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 -default-isolation MainActor %s -// REQUIRES: swift_feature_IsolatedConformances // REQUIRES: concurrency nonisolated diff --git a/test/Concurrency/isolated_conformance_inference.swift b/test/Concurrency/isolated_conformance_inference.swift index ed0bc91ac2a..5210513b198 100644 --- a/test/Concurrency/isolated_conformance_inference.swift +++ b/test/Concurrency/isolated_conformance_inference.swift @@ -1,8 +1,7 @@ -// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 -enable-experimental-feature IsolatedConformances -enable-experimental-feature InferIsolatedConformances %s +// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 -enable-upcoming-feature InferIsolatedConformances %s -// REQUIRES: swift_feature_IsolatedConformances -// REQUIRES: swift_feature_InferIsolatedConformances // REQUIRES: concurrency +// REQUIRES: swift_feature_InferIsolatedConformances protocol P { func f() diff --git a/test/Concurrency/preconcurrency_conformances.swift b/test/Concurrency/preconcurrency_conformances.swift index 95e1a853b22..6b2a3971232 100644 --- a/test/Concurrency/preconcurrency_conformances.swift +++ b/test/Concurrency/preconcurrency_conformances.swift @@ -108,6 +108,7 @@ final class K : @preconcurrency Initializable { final class MainActorK: Initializable { // expected-note@-1{{turn data races into runtime errors with '@preconcurrency'}}{{25-25=@preconcurrency }} // expected-note@-2{{mark all declarations used in the conformance 'nonisolated'}} + // expected-note@-3{{isolate this conformance to the main actor with '@MainActor'}} init() { } // expected-note{{main actor-isolated initializer 'init()' cannot satisfy nonisolated requirement}} } @@ -236,6 +237,7 @@ do { // expected-warning@-1:21 {{@preconcurrency attribute on conformance to 'P3' has no effect}} // expected-note@-2:45 {{turn data races into runtime errors with '@preconcurrency'}} // expected-note@-3{{mark all declarations used in the conformance 'nonisolated'}} + // expected-note@-4{{isolate this conformance to the main actor with '@MainActor'}} func foo() {} // expected-note@-1 {{main actor-isolated instance method 'foo()' cannot satisfy nonisolated requirement}} } @@ -244,6 +246,7 @@ do { // expected-warning@-1:21 {{@preconcurrency attribute on conformance to 'P3' has no effect}} // expected-note@-2:25 {{turn data races into runtime errors with '@preconcurrency'}} // expected-note@-3{{mark all declarations used in the conformance 'nonisolated'}} + // expected-note@-4{{isolate this conformance to the main actor with '@MainActor'}} func foo() {} // expected-note@-1 {{main actor-isolated instance method 'foo()' cannot satisfy nonisolated requirement}} } @@ -304,6 +307,7 @@ do { // expected-warning@-1:21 {{@preconcurrency attribute on conformance to 'P5' has no effect}} // expected-note@-2{{turn data races into runtime errors with '@preconcurrency'}} // expected-note@-3{{mark all declarations used in the conformance 'nonisolated'}} + // expected-note@-4{{isolate this conformance to the main actor with '@MainActor'}} func foo() {} // expected-note@-1 {{main actor-isolated instance method 'foo()' cannot satisfy nonisolated requirement}} } diff --git a/test/Concurrency/predates_concurrency.swift b/test/Concurrency/predates_concurrency.swift index 7f3f6f734dc..545ece63784 100644 --- a/test/Concurrency/predates_concurrency.swift +++ b/test/Concurrency/predates_concurrency.swift @@ -234,6 +234,7 @@ extension MainActorPreconcurrency: NotIsolated { // expected-complete-note@-1{{add '@preconcurrency' to the 'NotIsolated' conformance to suppress isolation-related diagnostics}}{{36-36=@preconcurrency }} // expected-complete-tns-note@-2{{turn data races into runtime errors with '@preconcurrency'}}{{36-36=@preconcurrency }} // expected-complete-tns-note@-3{{mark all declarations used in the conformance 'nonisolated'}} + // expected-complete-tns-note@-4{{isolate this conformance to the main actor with '@MainActor'}} func requirement() {} // expected-complete-tns-note@-1 {{main actor-isolated instance method 'requirement()' cannot satisfy nonisolated requirement}} // expected-complete-tns-note@-2 {{calls to instance method 'requirement()' from outside of its actor context are implicitly asynchronous}} diff --git a/test/Concurrency/regionanalysis_trackable_value.sil b/test/Concurrency/regionanalysis_trackable_value.sil new file mode 100644 index 00000000000..12a573c821e --- /dev/null +++ b/test/Concurrency/regionanalysis_trackable_value.sil @@ -0,0 +1,642 @@ +// RUN: %target-sil-opt -module-name infer --test-runner %s 2>&1 | %FileCheck %s + +// REQUIRES: concurrency +// REQUIRES: asserts + +// PLEASE READ THIS! +// +// This test is specifically meant to test how we look through underlying +// objects for region analysis. + +sil_stage raw + +import Swift +import Builtin +import _Concurrency + +//////////////////////// +// MARK: Declarations // +//////////////////////// + +class NonSendableKlass { +} + +class SendableKlass : @unchecked Sendable { +} + +struct Struct2 { + let nsLet: NonSendableKlass + let sLet: SendableKlass + var nsVar: NonSendableKlass + var sVar: SendableKlass +} + +struct Struct { + let nsLet: NonSendableKlass + let sLet: SendableKlass + var nsVar: NonSendableKlass + var sVar: SendableKlass + + let struct2Let: Struct2 + var struct2Var: Struct2 + + let sStruct: SendableStruct +} + +struct SendableStruct : @unchecked Sendable { + let nsLet: NonSendableKlass +} + +class NonSendableKlassWithState { + let sLet: Struct + var sVar: Struct + let recurseLet: NonSendableKlassWithState? + var recurseVar: NonSendableKlassWithState? +} + +actor Custom {} + +sil @transferNonSendableKlass : $@convention(thin) @async (@guaranteed NonSendableKlass) -> () +sil @useNonSendableKlass : $@convention(thin) (@guaranteed NonSendableKlass) -> () +sil @constructNonSendableKlass : $@convention(thin) () -> @owned NonSendableKlass +sil @constructStruct : $@convention(thin) () -> @owned Struct +sil @constructSendableStruct : $@convention(thin) () -> @owned SendableStruct + +sil @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () + +///////////////// +// MARK: Tests // +///////////////// + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_direct_access: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var NonSendableKlass } +// CHECK: end running test 1 of 1 on allocbox_direct_access: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_direct_access : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructNonSendableKlass : $@convention(thin) () -> @owned NonSendableKlass + %c = apply %f() : $@convention(thin) () -> @owned NonSendableKlass + %a = alloc_box ${ var NonSendableKlass } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*NonSendableKlass + + debug_value [trace] %p + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_nonsendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_nonsendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_nonsendable_let : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %nsLet = struct_element_addr %p : $*Struct, #Struct.nsLet + debug_value [trace] %nsLet + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_nonsendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_nonsendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_nonsendable_var : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %nsVar = struct_element_addr %p : $*Struct, #Struct.nsVar + debug_value [trace] %nsVar + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_sendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: yes][region_value_kind: disconnected]. +// CHECK: Rep Value: %6 = struct_element_addr %4 : $*Struct, #Struct.sLet +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_sendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_sendable_let : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %addr = struct_element_addr %p : $*Struct, #Struct.sLet + debug_value [trace] %addr + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_sendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: yes][region_value_kind: disconnected]. +// CHECK: Rep Value: %6 = struct_element_addr %4 : $*Struct, #Struct.sVar +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_sendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_sendable_var : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %addr = struct_element_addr %p : $*Struct, #Struct.sVar + debug_value [trace] %addr + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_let_grandfield_nonsendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_let_grandfield_nonsendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_let_grandfield_nonsendable_let : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %s2 = struct_element_addr %p : $*Struct, #Struct.struct2Let + %nsLet = struct_element_addr %s2 : $*Struct2, #Struct2.nsLet + + debug_value [trace] %nsLet + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_let_grandfield_nonsendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_let_grandfield_nonsendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_let_grandfield_nonsendable_var : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p + + %s2 = struct_element_addr %p : $*Struct, #Struct.struct2Let + %nsVar = struct_element_addr %s2 : $*Struct2, #Struct2.nsVar + debug_value [trace] %nsVar + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_let_grandfield_sendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: yes][region_value_kind: disconnected]. +// CHECK: Rep Value: %7 = struct_element_addr %6 : $*Struct2, #Struct2.sLet +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_let_grandfield_sendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_let_grandfield_sendable_let : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %s2 = struct_element_addr %p : $*Struct, #Struct.struct2Let + %addr = struct_element_addr %s2 : $*Struct2, #Struct2.sLet + debug_value [trace] %addr + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_let_grandfield_sendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: yes][region_value_kind: disconnected]. +// CHECK: Rep Value: %7 = struct_element_addr %6 : $*Struct2, #Struct2.sVar +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_let_grandfield_sendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_let_grandfield_sendable_var : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %s2 = struct_element_addr %p : $*Struct, #Struct.struct2Let + %addr = struct_element_addr %s2 : $*Struct2, #Struct2.sVar + debug_value [trace] %addr + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_var_grandfield_nonsendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_var_grandfield_nonsendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_var_grandfield_nonsendable_let : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %s2 = struct_element_addr %p : $*Struct, #Struct.struct2Var + %nsLet = struct_element_addr %s2 : $*Struct2, #Struct2.nsLet + + debug_value [trace] %nsLet + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_var_grandfield_nonsendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_var_grandfield_nonsendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_var_grandfield_nonsendable_var : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p + + %s2 = struct_element_addr %p : $*Struct, #Struct.struct2Var + %nsVar = struct_element_addr %s2 : $*Struct2, #Struct2.nsVar + debug_value [trace] %nsVar + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_var_grandfield_sendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: yes][region_value_kind: disconnected]. +// CHECK: Rep Value: %7 = struct_element_addr %6 : $*Struct2, #Struct2.sLet +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_var_grandfield_sendable_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_var_grandfield_sendable_let : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %s2 = struct_element_addr %p : $*Struct, #Struct.struct2Var + %addr = struct_element_addr %s2 : $*Struct2, #Struct2.sLet + debug_value [trace] %addr + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_struct_field_var_grandfield_sendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: yes][region_value_kind: disconnected]. +// CHECK: Rep Value: %7 = struct_element_addr %6 : $*Struct2, #Struct2.sVar +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var Struct } +// CHECK: end running test 1 of 1 on allocbox_access_struct_field_var_grandfield_sendable_var: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_struct_field_var_grandfield_sendable_var : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructStruct : $@convention(thin) () -> @owned Struct + %c = apply %f() : $@convention(thin) () -> @owned Struct + %a = alloc_box ${ var Struct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p : $*Struct + + %s2 = struct_element_addr %p : $*Struct, #Struct.struct2Var + %addr = struct_element_addr %s2 : $*Struct2, #Struct2.sVar + debug_value [trace] %addr + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_sendable_struct_field_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: yes][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %6 = struct_element_addr %4 : $*SendableStruct, #SendableStruct.nsLet +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_box ${ var SendableStruct } +// CHECK: end running test 1 of 1 on allocbox_access_sendable_struct_field_let: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_sendable_struct_field_let : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructSendableStruct : $@convention(thin) () -> @owned SendableStruct + %c = apply %f() : $@convention(thin) () -> @owned SendableStruct + %a = alloc_box ${ var SendableStruct } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %c to [init] %p + + %s2 = struct_element_addr %p : $*SendableStruct, #SendableStruct.nsLet + debug_value [trace] %s2 + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on allocbox_access_sendable_struct_field_let_2: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: yes][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %4 = struct_element_addr %2 : $*SendableStruct, #SendableStruct.nsLet +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: no][is_sendable: yes][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = alloc_stack $SendableStruct +// CHECK: end running test 1 of 1 on allocbox_access_sendable_struct_field_let_2: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @allocbox_access_sendable_struct_field_let_2 : $@convention(thin) () -> () { +bb0: + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %f = function_ref @constructSendableStruct : $@convention(thin) () -> @owned SendableStruct + %c = apply %f() : $@convention(thin) () -> @owned SendableStruct + %a = alloc_stack $SendableStruct + store %c to [init] %a + + %s2 = struct_element_addr %a : $*SendableStruct, #SendableStruct.nsLet + debug_value [trace] %s2 + + destroy_addr %a + dealloc_stack %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on class_lookthrough_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %6 = ref_element_addr %5 : $NonSendableKlassWithState, #NonSendableKlassWithState.sLet +// CHECK: end running test 1 of 1 on class_lookthrough_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @class_lookthrough_test : $@convention(thin) (@owned NonSendableKlassWithState) -> () { +bb0(%0 : @owned $NonSendableKlassWithState): + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %a = alloc_box ${ var NonSendableKlassWithState } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %0 to [init] %p : $*NonSendableKlassWithState + + %p2 = load_borrow %p + %s = ref_element_addr %p2 : $NonSendableKlassWithState, #NonSendableKlassWithState.sLet + debug_value [trace] %s + end_borrow %p2 + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// We model unchecked_enum_data as an assign, so we do not look through it. The +// result of this is that we consider it a separate value (even though we could +// cheat potentially). +// +// CHECK-LABEL: begin running test 1 of 1 on class_lookthrough_test_2: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %6 = ref_element_addr %5 : $NonSendableKlassWithState, #NonSendableKlassWithState.recurseLet +// CHECK: end running test 1 of 1 on class_lookthrough_test_2: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @class_lookthrough_test_2 : $@convention(thin) (@owned NonSendableKlassWithState) -> () { +bb0(%0 : @owned $NonSendableKlassWithState): + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %a = alloc_box ${ var NonSendableKlassWithState } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %0 to [init] %p : $*NonSendableKlassWithState + + %p2 = load_borrow %p + %s = ref_element_addr %p2 : $NonSendableKlassWithState, #NonSendableKlassWithState.recurseLet + %s2 = load_borrow %s + %s3 = unchecked_enum_data %s2 : $Optional, #Optional.some!enumelt + debug_value [trace] %s3 + end_borrow %s2 + end_borrow %p2 + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// We always stop at ref_element_addr since it is a base of a value. +// +// CHECK-LABEL: begin running test 1 of 1 on class_lookthrough_test_3: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %9 = ref_element_addr %8 : $NonSendableKlassWithState, #NonSendableKlassWithState.sLet +// CHECK: end running test 1 of 1 on class_lookthrough_test_3: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @class_lookthrough_test_3 : $@convention(thin) (@owned NonSendableKlassWithState) -> () { +bb0(%0 : @owned $NonSendableKlassWithState): + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %a = alloc_box ${ var NonSendableKlassWithState } + %ab = begin_borrow %a + %p = project_box %ab, 0 + store %0 to [init] %p : $*NonSendableKlassWithState + + %p2 = load_borrow %p + %s = ref_element_addr %p2 : $NonSendableKlassWithState, #NonSendableKlassWithState.recurseLet + %s2 = load_borrow %s + %s3 = unchecked_enum_data %s2 : $Optional, #Optional.some!enumelt + %s4 = ref_element_addr %s3 : $NonSendableKlassWithState, #NonSendableKlassWithState.sLet + debug_value [trace] %s4 + end_borrow %s2 + end_borrow %p2 + + end_borrow %ab + destroy_value %a + %9999 = tuple () + return %9999 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on actor_deinit_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %2 = unchecked_ref_cast %0 : $Custom to $Builtin.NativeObject +// CHECK: end running test 1 of 1 on actor_deinit_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @actor_deinit_test : $@convention(thin) (@guaranteed Custom) -> @owned Builtin.NativeObject { +bb0(%0 : @guaranteed $Custom): + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %2 = builtin "destroyDefaultActor"(%0) : $() + %3 = unchecked_ref_cast %0 to $Builtin.NativeObject + %4 = unchecked_ownership_conversion %3, @guaranteed to @owned + debug_value [trace] %3 + return %4 +} + +// CHECK-LABEL: begin running test 1 of 1 on project_box_loadable_test_case: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: yes][is_sendable: no][region_value_kind: task-isolated]. +// CHECK: Rep Value: %0 = argument of bb0 : $*{ var NonSendableKlass } +// CHECK: end running test 1 of 1 on project_box_loadable_test_case: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @project_box_loadable_test_case : $@convention(thin) @async (@in { var NonSendableKlass }) -> () { +bb0(%0 : $*{ var NonSendableKlass }): + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %1 = load [take] %0 + %2 = project_box %1, 0 + // function_ref transferIndirect + %3 = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () + %4 = apply [callee_isolation=nonisolated] [caller_isolation=global_actor] %3(%2) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () + debug_value [trace] %2 + destroy_value %1 + %6 = tuple () + return %6 +} + +// CHECK-LABEL: begin running test 1 of 1 on deep_value_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: yes][is_sendable: no][region_value_kind: task-isolated]. +// CHECK: Rep Value: %0 = argument of bb0 : $*{ var { var NonSendableKlass } } +// CHECK: end running test 1 of 1 on deep_value_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @deep_value_test : $@convention(thin) @async (@in_guaranteed { var { var NonSendableKlass } }) -> () { +bb0(%0 : $*{ var { var NonSendableKlass } }): + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %1 = load_borrow %0 + %2 = project_box %1, 0 + %3 = load_borrow %2 + %4 = project_box %3, 0 + // function_ref transferIndirect + %func = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () + apply [callee_isolation=nonisolated] [caller_isolation=global_actor] %func(%4) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () + debug_value [trace] %4 + end_borrow %3 + end_borrow %1 + %6 = tuple () + return %6 +} + +// CHECK-LABEL: begin running test 1 of 1 on deep_base_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: yes][region_value_kind: disconnected]. +// CHECK: Rep Value: %5 = struct_element_addr %4 : $*Struct2, #Struct2.sLet +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: yes][is_sendable: no][region_value_kind: task-isolated]. +// CHECK: Rep Value: %0 = argument of bb0 : $*{ var { var Struct2 } } +// CHECK: end running test 1 of 1 on deep_base_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @deep_base_test : $@convention(thin) @async (@in_guaranteed { var { var Struct2 } }) -> () { +bb0(%0 : $*{ var { var Struct2 } }): + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %1 = load_borrow %0 + %2 = project_box %1, 0 + %3 = load_borrow %2 + %4 = project_box %3, 0 + %5 = struct_element_addr %4 : $*Struct2, #Struct2.sLet + // function_ref transferIndirect + %func = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () + apply [callee_isolation=nonisolated] [caller_isolation=global_actor] %func(%5) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () + debug_value [trace] %5 + end_borrow %3 + end_borrow %1 + %6 = tuple () + return %6 +} + +// CHECK-LABEL: begin running test 1 of 1 on alloc_stack_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +// CHECK: Value: +// CHECK: TrackableValue. State: TrackableValueState[id: 0][is_no_alias: no][is_sendable: yes][region_value_kind: disconnected]. +// CHECK: Rep Value: %3 = struct_element_addr %1 : $*Struct2, #Struct2.sLet +// CHECK: Base: +// CHECK: TrackableValue. State: TrackableValueState[id: 1][is_no_alias: yes][is_sendable: no][region_value_kind: disconnected]. +// CHECK: Rep Value: %1 = alloc_stack $Struct2 +// CHECK: end running test 1 of 1 on alloc_stack_test: sil_regionanalysis_underlying_tracked_value with: @trace[0] +sil [ossa] @alloc_stack_test : $@convention(thin) @async (@owned Struct2) -> () { +bb0(%0 : @owned $Struct2): + specify_test "sil_regionanalysis_underlying_tracked_value @trace[0]" + %1 = alloc_stack $Struct2, let + store %0 to [init] %1 + + %5 = struct_element_addr %1 : $*Struct2, #Struct2.sLet + %func = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () + apply [callee_isolation=nonisolated] [caller_isolation=global_actor] %func(%5) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () + debug_value [trace] %5 + + destroy_addr %1 + dealloc_stack %1 + %9999 = tuple () + return %9999 : $() +} \ No newline at end of file diff --git a/test/Concurrency/sendable_metatype.swift b/test/Concurrency/sendable_metatype.swift index a21366ac6f0..13b8ff91076 100644 --- a/test/Concurrency/sendable_metatype.swift +++ b/test/Concurrency/sendable_metatype.swift @@ -1,8 +1,9 @@ -// RUN: %target-typecheck-verify-swift -swift-version 6 -enable-experimental-feature IsolatedConformances -emit-sil -o /dev/null +// RUN: %target-typecheck-verify-swift -swift-version 6 -emit-sil -o /dev/null // REQUIRES: concurrency -// REQUIRES: swift_feature_IsolatedConformances +protocol P { +} protocol Q { static func g() @@ -18,29 +19,35 @@ func acceptMetaOnMainActor(_: T.Type) { } // ------------------------------------------------------------------------- nonisolated func staticCallThroughMetaSmuggled(_: T.Type) { let x: Q.Type = T.self - Task.detached { // expected-error{{risks causing data races}} + Task.detached { // expected-warning{{risks causing data races}} x.g() // expected-note{{closure captures 'x' which is accessible to code in the current task}} } } nonisolated func passMetaSmuggled(_: T.Type) { let x: Q.Type = T.self - Task.detached { // expected-error{{risks causing data races}} + Task.detached { // expected-warning{{risks causing data races}} acceptMeta(x) // expected-note{{closure captures 'x' which is accessible to code in the current task}} } } nonisolated func passMetaSmuggledAny(_: T.Type) { let x: Any.Type = T.self - Task.detached { // expected-error{{risks causing data races}} - acceptMeta(x) // expected-note{{closure captures 'x' which is accessible to code in the current task}} + Task.detached { + acceptMeta(x) + } +} + +nonisolated func captureThroughMetaValMoReqs(_: T.Type) { + let x = T.self + Task.detached { + _ = x } } nonisolated func passToMainActorSmuggledAny(_: T.Type) async { let x: Any.Type = T.self - await acceptMetaOnMainActor(x) // expected-error{{sending value of non-Sendable type '(Any).Type' risks causing data races}} - // expected-note@-1{{sending task-isolated value of non-Sendable type '(Any).Type' to main actor-isolated global function}} + await acceptMetaOnMainActor(x) } // ------------------------------------------------------------------------- @@ -70,9 +77,9 @@ nonisolated func passSendableToMainActorSmuggledAny(_: T.Type) asyn // ------------------------------------------------------------------------- // Existential opening // ------------------------------------------------------------------------- -nonisolated func passMetaSmuggledAnyFromExistential(_ qT: Q.Type) { - let x: Any.Type = qT - Task.detached { // expected-error{{risks causing data races}} +nonisolated func passMetaSmuggledAnyFromExistential(_ pqT: (P & Q).Type) { + let x: P.Type = pqT + Task.detached { // expected-warning{{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} acceptMeta(x) // expected-note{{closure captures 'x' which is accessible to code in the current task}} } } diff --git a/test/Concurrency/sendable_metatype_typecheck.swift b/test/Concurrency/sendable_metatype_typecheck.swift index 6ba6def4956..6ae4c70d264 100644 --- a/test/Concurrency/sendable_metatype_typecheck.swift +++ b/test/Concurrency/sendable_metatype_typecheck.swift @@ -1,7 +1,6 @@ -// RUN: %target-typecheck-verify-swift -swift-version 6 -enable-experimental-feature IsolatedConformances +// RUN: %target-typecheck-verify-swift -swift-version 6 // REQUIRES: concurrency -// REQUIRES: swift_feature_IsolatedConformances // This test checks for typecheck-only diagnostics involving non-sendable // metatypes. @@ -10,31 +9,47 @@ protocol Q { static func g() } + +// Sendability of existential metatypes +fileprivate nonisolated let anyObjectArray: [AnyClass] = [] + +func testSendableExistential() { + _ = anyObjectArray +} + + nonisolated func acceptMeta(_: T.Type) { } nonisolated func staticCallThroughMetaVal(_: T.Type) { - let x = T.self // expected-error{{capture of non-sendable type 'T.Type' in an isolated closure}} + let x = T.self // expected-warning{{capture of non-sendable type 'T.Type' in an isolated closure}} Task.detached { - x.g() // expected-error{{capture of non-sendable type 'T.Type' in an isolated closure}} + x.g() // expected-warning{{capture of non-sendable type 'T.Type' in an isolated closure}} + } +} + +nonisolated func captureThroughMetaValMoReqs(_: T.Type) { + let x = T.self + Task.detached { + _ = x } } nonisolated func passMetaVal(_: T.Type) { - let x = T.self // expected-error{{capture of non-sendable type 'T.Type' in an isolated closure}} + let x = T.self // expected-warning{{capture of non-sendable type 'T.Type' in an isolated closure}} Task.detached { - acceptMeta(x) // expected-error{{capture of non-sendable type}} + acceptMeta(x) // expected-warning{{capture of non-sendable type}} } } nonisolated func staticCallThroughMeta(_: T.Type) { Task.detached { - T.g() // expected-error{{capture of non-sendable type}} + T.g() // expected-warning{{capture of non-sendable type}} } } nonisolated func passMeta(_: T.Type) { Task.detached { - acceptMeta(T.self) // expected-error{{capture of non-sendable type 'T.Type' in an isolated closure}} + acceptMeta(T.self) // expected-warning{{capture of non-sendable type 'T.Type' in an isolated closure}} } } @@ -72,3 +87,63 @@ nonisolated func passMetaWithMetaSendableVal(_: T.Type) x.g() // okay, because T is Sendable implies T.Type: Sendable } } + +struct GenericThingy { + func searchMe(_: (Element, Element) -> Bool) { } + + func test() where Element: Comparable { + // Ensure that this we infer a non-@Sendable function type for Comparable.< + searchMe(<) + + let _: (Element, Element) -> Bool = (>) + let _: @Sendable (Element, Element) -> Bool = (>) // expected-error{{converting non-sendable function value to '@Sendable (Element, Element) -> Bool' may introduce data races}} + } +} + +extension Int: Q { + static func g() { } +} + +extension String: Q { + static func g() { } +} + +class Holder: @unchecked Sendable { + // expected-note@+3{{disable concurrency-safety checks if accesses are protected by an external synchronization mechanism}} + // expected-note@+2{{add '@MainActor' to make static property 'globalExistentialThing' part of global actor 'MainActor'}} + // expected-warning@+1{{static property 'globalExistentialThing' is not concurrency-safe because non-'Sendable' type 'Dictionary' may have shared mutable state}} + static let globalExistentialThing: Dictionary = [ + 1: Int.self, + 2: String.self, + ] +} + +enum E: Sendable { +case q(Q.Type, Int) // expected-warning{{associated value 'q' of 'Sendable'-conforming enum 'E' has non-sendable type 'any Q.Type'}} +} + +struct S: Sendable { + var tuple: ([Q.Type], Int) // expected-warning{{stored property 'tuple' of 'Sendable'-conforming struct 'S' has non-sendable type '([any Q.Type], Int)'}} +} + +extension Q { + static func h() -> Self { } +} + +extension Array: Q where Element: Q { + static func g() { } +} + +struct GenericS { } + +extension GenericS: Q where T: Q { + static func g() { } +} + +extension GenericS: Sendable where T: Sendable { } + +final class TestStaticMembers { + init(_: T) { + let _: @Sendable () -> GenericS = GenericS.h // Ok + } +} diff --git a/test/Concurrency/transfernonsendable.swift b/test/Concurrency/transfernonsendable.swift index 1cafd30aa71..fa834d53081 100644 --- a/test/Concurrency/transfernonsendable.swift +++ b/test/Concurrency/transfernonsendable.swift @@ -1968,3 +1968,32 @@ extension NonIsolatedFinalKlass { // expected-tns-note @-1 {{sending task-isolated 'self.ns' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and task-isolated uses}} } } + +func mutableLocalCaptureDataRace() async { + var x = 0 + x = 0 + _ = x + + Task.detached { x = 1 } // expected-tns-warning {{sending value of non-Sendable type '() async -> ()' risks causing data races}} + // expected-tns-note @-1 {{Passing value of non-Sendable type '() async -> ()' as a 'sending' argument to static method 'detached(priority:operation:)' risks causing races in between local and caller code}} + + x = 2 // expected-tns-note {{access can happen concurrently}} +} + +func mutableLocalCaptureDataRace2() async { + var x = 0 + x = 0 + + Task.detached { x = 1 } // expected-tns-warning {{sending value of non-Sendable type '() async -> ()' risks causing data races}} + // expected-tns-note @-1 {{Passing value of non-Sendable type '() async -> ()' as a 'sending' argument to static method 'detached(priority:operation:)' risks causing races in between local and caller code}} + + print(x) // expected-tns-note {{access can happen concurrently}} +} + +func localCaptureDataRace3() async { + let x = 0 + + Task.detached { print(x) } + + print(x) +} diff --git a/test/Concurrency/transfernonsendable_nonisolatedunsafe.swift b/test/Concurrency/transfernonsendable_nonisolatedunsafe.swift index 5b67379d2ab..8d13a7cde8e 100644 --- a/test/Concurrency/transfernonsendable_nonisolatedunsafe.swift +++ b/test/Concurrency/transfernonsendable_nonisolatedunsafe.swift @@ -307,24 +307,24 @@ func useAfterTransferLetSquelchedIndirectAddressOnly(_ await transferToMainIndirect(ns) // expected-tns-warning @-1 {{sending 'ns' risks causing data races}} - // expected-tns-note @-2 {{sending 'ns' to main actor-isolated global function 'transferToMainIndirect' risks causing data races between main actor-isolated and local nonisolated uses}} + // expected-tns-note @-2 {{sending task-isolated 'ns' to main actor-isolated global function 'transferToMainIndirect' risks causing data races between main actor-isolated and task-isolated uses}} // expected-complete-warning @-3 {{passing argument of non-sendable type 'T' into main actor-isolated context may introduce data races}} - print(ns) // expected-tns-note {{access can happen concurrently}} + print(ns) await transferToMainIndirect(ns2) print(ns2) await transferToMainIndirect(ns3) // expected-tns-warning @-1 {{sending 'ns3' risks causing data races}} - // expected-tns-note @-2 {{sending 'ns3' to main actor-isolated global function 'transferToMainIndirect' risks causing data races between main actor-isolated and local nonisolated uses}} + // expected-tns-note @-2 {{sending task-isolated 'ns3' to main actor-isolated global function 'transferToMainIndirect' risks causing data races between main actor-isolated and task-isolated uses}} // expected-complete-warning @-3 {{passing argument of non-sendable type 'T' into main actor-isolated context may introduce data races}} - print(ns3) // expected-tns-note {{access can happen concurrently}} + print(ns3) await transferToMainIndirect(ns4) // expected-tns-warning @-1 {{sending 'ns4' risks causing data races}} - // expected-tns-note @-2 {{sending 'ns4' to main actor-isolated global function 'transferToMainIndirect' risks causing data races between main actor-isolated and local nonisolated uses}} + // expected-tns-note @-2 {{sending task-isolated 'ns4' to main actor-isolated global function 'transferToMainIndirect' risks causing data races between main actor-isolated and task-isolated uses}} // expected-complete-warning @-3 {{passing argument of non-sendable type 'T' into main actor-isolated context may introduce data races}} - print(ns4) // expected-tns-note {{access can happen concurrently}} + print(ns4) } //////////////////////// diff --git a/test/Constraints/availability_macos.swift b/test/Constraints/availability_macos.swift new file mode 100644 index 00000000000..06e326424ec --- /dev/null +++ b/test/Constraints/availability_macos.swift @@ -0,0 +1,37 @@ +// RUN: %target-typecheck-verify-swift + +// REQUIRES: OS=macosx + +struct A {} // expected-note * {{found this candidate}} +struct B {} // expected-note * {{found this candidate}} + +func ambiguousInFarFuture(_: A) {} + +@available(macOS 99, *) +func ambiguousInFarFuture(_: B) {} + +struct S { + func ambiguousInFarFuture(_: A) {} +} + +@available(macOS 99, *) +extension S { + func ambiguousInFarFuture(_: B) {} +} + +func testDeploymentTarget(_ s: S) { + ambiguousInFarFuture(.init()) + s.ambiguousInFarFuture(.init()) +} + +@available(macOS 99, *) +func testFarFuture(_ s: S) { + ambiguousInFarFuture(.init()) // expected-error {{ambiguous use of 'init()'}} + s.ambiguousInFarFuture(.init()) // expected-error {{ambiguous use of 'init()'}} +} + +@available(macOS, unavailable) +func testUnavailable(_ s: S) { + ambiguousInFarFuture(.init()) // expected-error {{ambiguous use of 'init()'}} + s.ambiguousInFarFuture(.init()) // expected-error {{ambiguous use of 'init()'}} +} diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index a8f737139eb..faf9971ef5a 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -1127,7 +1127,7 @@ func rdar17170728() { } let _ = [i, j, k].reduce(0 as Int?) { // expected-error {{missing argument label 'into:' in call}} - // expected-error@-1 {{cannot convert value of type 'Int?' to expected argument type '(inout @escaping (Bool, Bool) -> Bool?, Int?) throws -> ()'}} + // expected-error@-1 {{cannot convert value of type 'Int?' to expected argument type '(inout (Bool, Bool) -> Bool?, Int?) throws -> ()'}} $0 && $1 ? $0 + $1 : ($0 ? $0 : ($1 ? $1 : nil)) // expected-error@-1 {{binary operator '+' cannot be applied to two 'Bool' operands}} } diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 3bb986765a1..540bf28a22e 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -488,7 +488,7 @@ $s3red7MyActorC3runyxxyYaKACYcYTXEYaKlFZ ---> static red.MyActor.run(@red.MyA $s3red7MyActorC3runyxxyYaKYAYTXEYaKlFZ ---> static red.MyActor.run(@isolated(any) () async throws -> sending A) async throws -> A $s7ToolKit10TypedValueOACs5Error_pIgHTnTrzo_A2CsAD_pIegHiTrzr_TR ---> {T:} reabstraction thunk helper from @callee_guaranteed @async (@in_guaranteed sending ToolKit.TypedValue) -> sending (@out ToolKit.TypedValue, @error @owned Swift.Error) to @escaping @callee_guaranteed @async (@in sending ToolKit.TypedValue) -> (@out ToolKit.TypedValue, @error @out Swift.Error) $s16sending_mangling16NonSendableKlassCACIegTiTr_A2CIegTxTo_TR ---> {T:} reabstraction thunk helper from @escaping @callee_guaranteed (@in sending sending_mangling.NonSendableKlass) -> sending (@out sending_mangling.NonSendableKlass) to @escaping @callee_guaranteed (@owned sending sending_mangling.NonSendableKlass) -> sending (@owned sending_mangling.NonSendableKlass) -$s3red7MyActorC3runyxxyYaKYCXEYaKlFZ ---> static red.MyActor.run(@execution(caller) () async throws -> A) async throws -> A +$s3red7MyActorC3runyxxyYaKYCXEYaKlFZ ---> static red.MyActor.run(nonisolated(nonsending) () async throws -> A) async throws -> A $s5thing1PP1sAA1SVvxTwc ---> coro function pointer to thing.P.s.modify2 : thing.S _$s15raw_identifiers0020foospace_liaADEDGcjayyF ---> raw_identifiers.`foo space`() -> () _$s15raw_identifiers0018_3times_pgaIGJCFbhayyF ---> raw_identifiers.`3 times`() -> () diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index fb249319659..c75551c8e23 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -183,6 +183,7 @@ actor MyGenericGlobalActor { // ON_GLOBALVAR-DAG: Keyword/None: exclusivity[#Var Attribute#]; name=exclusivity // ON_GLOBALVAR-DAG: Keyword/None: preconcurrency[#Var Attribute#]; name=preconcurrency // ON_GLOBALVAR-DAG: Keyword/None: backDeployed[#Var Attribute#]; name=backDeployed +// ON_GLOBALVAR-DAG: Keyword/None: concurrent[#Var Attribute#]; name=concurrent // ON_GLOBALVAR-NOT: Keyword // ON_GLOBALVAR-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_GLOBALVAR-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#Property Wrapper#]; name=MyPropertyWrapper @@ -220,6 +221,7 @@ struct _S { // ON_PROPERTY-DAG: Keyword/None: exclusivity[#Var Attribute#]; name=exclusivity // ON_PROPERTY-DAG: Keyword/None: preconcurrency[#Var Attribute#]; name=preconcurrency // ON_PROPERTY-DAG: Keyword/None: backDeployed[#Var Attribute#]; name=backDeployed +// ON_PROPERTY-DAG: Keyword/None: concurrent[#Var Attribute#]; name=concurrent // ON_PROPERTY-NOT: Keyword // ON_PROPERTY-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_PROPERTY-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#Property Wrapper#]; name=MyPropertyWrapper @@ -251,6 +253,7 @@ struct _S { // ON_METHOD-DAG: Keyword/None: preconcurrency[#Func Attribute#]; name=preconcurrency // ON_METHOD-DAG: Keyword/None: backDeployed[#Func Attribute#]; name=backDeployed // ON_METHOD-DAG: Keyword/None: lifetime[#Func Attribute#]; name=lifetime +// ON_METHOD-DAG: Keyword/None: concurrent[#Func Attribute#]; name=concurrent // ON_METHOD-NOT: Keyword // ON_METHOD-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_METHOD-DAG: Decl[Struct]/CurrModule: MyPropertyWrapper[#Property Wrapper#]; name=MyPropertyWrapper @@ -326,6 +329,7 @@ struct _S { // ON_MEMBER_LAST-DAG: Keyword/None: storageRestrictions[#Declaration Attribute#]; name=storageRestrictions // ON_MEMBER_LAST-DAG: Keyword/None: lifetime[#Declaration Attribute#]; name=lifetime // ON_MEMBER_LAST-DAG: Keyword/None: extensible[#Declaration Attribute#]; name=extensible +// ON_MEMBER_LAST-DAG: Keyword/None: concurrent[#Declaration Attribute#]; name=concurrent // ON_MEMBER_LAST-NOT: Keyword // ON_MEMBER_LAST-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_MEMBER_LAST-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#Property Wrapper#]; name=MyPropertyWrapper @@ -399,6 +403,7 @@ func dummy2() {} // KEYWORD_LAST-DAG: Keyword/None: storageRestrictions[#Declaration Attribute#]; name=storageRestrictions // KEYWORD_LAST-DAG: Keyword/None: lifetime[#Declaration Attribute#]; name=lifetime // KEYWORD_LAST-DAG: Keyword/None: extensible[#Declaration Attribute#]; name=extensible +// KEYWORD_LAST-DAG: Keyword/None: concurrent[#Declaration Attribute#]; name=concurrent // KEYWORD_LAST-NOT: Keyword // KEYWORD_LAST-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // KEYWORD_LAST-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyGenericPropertyWrapper[#Property Wrapper#]; name=MyGenericPropertyWrapper diff --git a/test/IDE/complete_decl_attribute_feature_requirement.swift b/test/IDE/complete_decl_attribute_feature_requirement.swift index 8fabadd74d3..f3d12029bc2 100644 --- a/test/IDE/complete_decl_attribute_feature_requirement.swift +++ b/test/IDE/complete_decl_attribute_feature_requirement.swift @@ -7,8 +7,7 @@ // RUN: %batch-code-completion -filecheck-additional-suffix _DISABLED // RUN: %batch-code-completion -filecheck-additional-suffix _ENABLED \ -// RUN: -enable-experimental-feature ABIAttribute \ -// RUN: -enable-experimental-feature ExecutionAttribute +// RUN: -enable-experimental-feature ABIAttribute // NOTE: Please do not include the ", N items" after "Begin completions". The // item count creates needless merge conflicts given that an "End completions" @@ -18,18 +17,14 @@ // KEYWORD2: Begin completions // KEYWORD2_ENABLED-DAG: Keyword/None: abi[#Func Attribute#]; name=abi -// KEYWORD2_ENABLED-DAG: Keyword/None: execution[#Func Attribute#]; name=execution // KEYWORD2_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD2_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // KEYWORD2: End completions @#^KEYWORD3^# class C {} // KEYWORD3: Begin completions // KEYWORD3_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD3_ENABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // KEYWORD3_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD3_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // KEYWORD3: End completions @#^KEYWORD3_2?check=KEYWORD3^#IB class C2 {} @@ -38,68 +33,52 @@ @#^KEYWORD4^# enum E {} // KEYWORD4: Begin completions // KEYWORD4_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD4_ENABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // KEYWORD4_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD4_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // KEYWORD4: End completions @#^KEYWORD5^# struct S{} // KEYWORD5: Begin completions // KEYWORD5_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD5_ENABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // KEYWORD5_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD5_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // KEYWORD5: End completions @#^ON_GLOBALVAR^# var globalVar // ON_GLOBALVAR: Begin completions // ON_GLOBALVAR_ENABLED-DAG: Keyword/None: abi[#Var Attribute#]; name=abi -// ON_GLOBALVAR_ENABLED-DAG: Keyword/None: execution[#Var Attribute#]; name=execution // ON_GLOBALVAR_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_GLOBALVAR_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // ON_GLOBALVAR: End completions struct _S { @#^ON_INIT^# init() // ON_INIT: Begin completions // ON_INIT_ENABLED-DAG: Keyword/None: abi[#Constructor Attribute#]; name=abi -// ON_INIT_ENABLED-DAG: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // ON_INIT_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_INIT_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // ON_INIT: End completions @#^ON_PROPERTY^# var foo // ON_PROPERTY: Begin completions // ON_PROPERTY_ENABLED-DAG: Keyword/None: abi[#Var Attribute#]; name=abi -// ON_PROPERTY_ENABLED-DAG: Keyword/None: execution[#Var Attribute#]; name=execution // ON_PROPERTY_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_PROPERTY_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // ON_PROPERTY: End completions @#^ON_SUBSCR^# subscript // ON_SUBSCR: Begin completions // ON_SUBSCR_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi -// ON_SUBSCR_ENABLED-DAG: Keyword/None: execution[#Declaration Attribute#]; name=execution // ON_SUBSCR_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_SUBSCR_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // ON_SUBSCR: End completions @#^ON_METHOD^# private func foo() // ON_METHOD: Begin completions // ON_METHOD_ENABLED-DAG: Keyword/None: abi[#Func Attribute#]; name=abi -// ON_METHOD_ENABLED-DAG: Keyword/None: execution[#Func Attribute#]; name=execution // ON_METHOD_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_METHOD_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // ON_METHOD: End completions func bar(@#^ON_PARAM_1?check=ON_PARAM^#) // ON_PARAM: Begin completions // ON_PARAM_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_PARAM_ENABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // ON_PARAM_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_PARAM_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // ON_PARAM: End completions func bar( @@ -122,9 +101,7 @@ struct _S { @#^ON_MEMBER_LAST^# // ON_MEMBER_LAST: Begin completions // ON_MEMBER_LAST_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi -// ON_MEMBER_LAST_ENABLED-DAG: Keyword/None: execution[#Declaration Attribute#]; name=execution // ON_MEMBER_LAST_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_MEMBER_LAST_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // ON_MEMBER_LAST: End completions } @@ -136,9 +113,7 @@ func takeClosure(_: () -> Void) { // IN_CLOSURE: Begin completions // FIXME: Not valid in this position (but CompletionLookup can't tell that) // IN_CLOSURE_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi -// IN_CLOSURE_ENABLED-DAG: Keyword/None: execution[#Declaration Attribute#]; name=execution // IN_CLOSURE_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// IN_CLOSURE_DISABLED-NOT: Keyword/None: execution[#{{.*}} Attribute#]; name=execution // IN_CLOSURE: End completions @#^KEYWORD_INDEPENDENT_1?check=KEYWORD_LAST^# @@ -154,7 +129,5 @@ func dummy2() {} // KEYWORD_LAST: Begin completions // KEYWORD_LAST_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi -// KEYWORD_LAST_ENABLED-DAG: Keyword/None: execution[#Declaration Attribute#]; name=execution // KEYWORD_LAST_DISABLED-NOT: Keyword/None: abi[#Declaration Attribute#]; name=abi -// KEYWORD_LAST_DISABLED-NOT: Keyword/None: execution[#Declaration Attribute#]; name=execution // KEYWORD_LAST: End completions diff --git a/test/IRGen/addressable.swift b/test/IRGen/addressable.swift new file mode 100644 index 00000000000..b764aa50a57 --- /dev/null +++ b/test/IRGen/addressable.swift @@ -0,0 +1,34 @@ +// RUN: %target-swift-frontend -module-name A -emit-ir -primary-file %s \ +// RUN: -enable-experimental-feature LifetimeDependence \ +// RUN: -enable-experimental-feature AddressableTypes \ +// RUN: | %FileCheck %s + +// REQUIRES: swift_feature_AddressableTypes +// REQUIRES: swift_feature_LifetimeDependence + +public struct NE: ~Escapable {} + +@_addressableForDependencies +public struct Holder {} + +@_silgen_name("holder_NE") +@lifetime(borrow holder) +func getNE(_ holder: Holder) -> NE + +@_silgen_name("holder_mut_NE") +@lifetime(&holder) +func getMutNE(_ holder: inout Holder) -> NE + +// The parameter cannot be 'nocapture'. +// +// CHECK-LABEL: define{{.*}} swiftcc void @"$s1A17testAddressableInyAA2NEVAA6HolderVF"(ptr noalias %0) +public func testAddressableIn(_ holder: Holder) -> NE { + getNE(holder) +} + +// The parameter cannot be 'nocapture'. +// +// CHECK-LABEL: define{{.*}} swiftcc void @"$s1A20testAddressableInoutyAA2NEVAA6HolderVzF"(ptr %0) +public func testAddressableInout(_ holder: inout Holder) -> NE { + getMutNE(&holder) +} diff --git a/test/IRGen/conformance_path_with_concrete_steps.swift b/test/IRGen/conformance_path_with_concrete_steps.swift new file mode 100644 index 00000000000..63978cbd5d2 --- /dev/null +++ b/test/IRGen/conformance_path_with_concrete_steps.swift @@ -0,0 +1,29 @@ +// RUN: %target-swift-frontend -emit-ir %s + +public protocol P1 { + associatedtype A: P3 +} + +public protocol P2: P1 where A.B == G { + associatedtype C where C == A.B.C +} + +public protocol P3 { + associatedtype B: P4 +} + +public protocol P4: P5 {} + +public protocol P5 { + associatedtype C: P6 +} + +public protocol P6 { + func foo() +} + +public struct G: P4 {} + +public func f(_: T, c: T.C) { + return c.foo() +} diff --git a/test/IRGen/coroutine_accessors.swift b/test/IRGen/coroutine_accessors.swift index c6aee7180e4..48152378da6 100644 --- a/test/IRGen/coroutine_accessors.swift +++ b/test/IRGen/coroutine_accessors.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-emit-irgen \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -enable-experimental-feature CoroutineAccessors \ // RUN: | %IRGenFileCheck %s @@ -97,7 +98,7 @@ public var irm: Int { // CHECK-SAME: ptr @"$s19coroutine_accessors1SV3irmSivyTwc", // CHECK-SAME: ptr [[ALLOCATOR]], // CHECK-SAME: ptr [[FRAME]], -// CHECK-SAME: ptr @"$s19coroutine_accessors1SVSiIetMIgYy_TC", +// CHECK-SAME: $s19coroutine_accessors1SVSiIetMIgYy_TC // CHECK-SAME: ptr @_swift_coro_alloc, // CHECK-SAME: ptr @_swift_coro_dealloc // CHECK-SAME: ) @@ -134,7 +135,7 @@ public var irm: Int { // CHECK-SAME: ptr @"$s19coroutine_accessors1SV3irmSivxTwc", // CHECK-SAME: ptr [[ALLOCATOR]], // CHECK-SAME: ptr [[FRAME]], -// CHECK-SAME: ptr @"$s19coroutine_accessors1SVSiIetMIlYl_TC", +// CHECK-SAME: $s19coroutine_accessors1SVSiIetMIlYl_TC // CHECK-SAME: ptr @_swift_coro_alloc, // CHECK-SAME: ptr @_swift_coro_dealloc // CHECK-SAME: ) diff --git a/test/IRGen/coroutine_accessors_backdeploy_async_56.swift b/test/IRGen/coroutine_accessors_backdeploy_async_56.swift index ba87170e7e8..5ad8a79ac62 100644 --- a/test/IRGen/coroutine_accessors_backdeploy_async_56.swift +++ b/test/IRGen/coroutine_accessors_backdeploy_async_56.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-emit-irgen \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -module-name backdep \ // RUN: -target %target-swift-5.6-abi-triple \ // RUN: -Onone \ diff --git a/test/IRGen/coroutine_accessors_backdeploy_async_57.swift b/test/IRGen/coroutine_accessors_backdeploy_async_57.swift index c128ab2b31a..117c310ef3a 100644 --- a/test/IRGen/coroutine_accessors_backdeploy_async_57.swift +++ b/test/IRGen/coroutine_accessors_backdeploy_async_57.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-emit-irgen \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -module-name backdep \ // RUN: -target %target-swift-5.7-abi-triple \ // RUN: -Onone \ diff --git a/test/IRGen/coroutine_accessors_popless.swift b/test/IRGen/coroutine_accessors_popless.swift index b702457b039..38db9436ea8 100644 --- a/test/IRGen/coroutine_accessors_popless.swift +++ b/test/IRGen/coroutine_accessors_popless.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-emit-irgen \ // RUN: %s \ // RUN: -Onone \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -enable-experimental-feature CoroutineAccessors \ // RUN: -enable-arm64-corocc \ // RUN: -enable-x86_64-corocc \ diff --git a/test/IRGen/default_override.sil b/test/IRGen/default_override.sil index ccf09349c6c..88c4fe6e4cb 100644 --- a/test/IRGen/default_override.sil +++ b/test/IRGen/default_override.sil @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -module-name Library \ // RUN: -emit-ir \ // RUN: -enable-experimental-feature CoroutineAccessors \ diff --git a/test/IRGen/integer_conversion.swift b/test/IRGen/integer_conversion.swift deleted file mode 100644 index 9d671a82074..00000000000 --- a/test/IRGen/integer_conversion.swift +++ /dev/null @@ -1,26 +0,0 @@ -// RUN: %target-swift-frontend -primary-file %s -emit-ir | %FileCheck %s -// RUN: %target-swift-frontend -primary-file %s -O -emit-ir | %FileCheck %s -// REQUIRES: CPU=x86_64 || CPU=arm64 - -// https://github.com/swiftlang/swift/issues/78501 -public struct PcgRandom { - private var state: UInt64 = 0; - - // CHECK-LABEL: define{{.*}}swiftcc i32 @"$s18integer_conversion9PcgRandomV6next32s6UInt32VyF" - public mutating func next32() -> UInt32 { - // CHECK-NOT: sSUss17FixedWidthIntegerRzrlEyxqd__cSzRd__lufC - // CHECK-NOT: sSZss17FixedWidthIntegerRzrlEyxqd__cSzRd__lufC - // CHECK: ret i32 - let oldstate : UInt64 = state - state = oldstate &* 6364136223846793005 &+ 1; - let shifted = oldstate >> 18 - let xor = shifted ^ oldstate - let xorshifted64 = xor >> 27 - let xorshifted = UInt32((xorshifted64 << 32) >> 32) - let rot : UInt32 = UInt32(oldstate >> 59) - let nrot : UInt32 = UInt32(bitPattern: -Int32(rot)) - return (xorshifted >> rot) | (xorshifted << (nrot & 31)) - } - - init() {} -} diff --git a/test/IRGen/isolated_conformance.swift b/test/IRGen/isolated_conformance.swift index 6240933188e..6900cf90573 100644 --- a/test/IRGen/isolated_conformance.swift +++ b/test/IRGen/isolated_conformance.swift @@ -1,8 +1,7 @@ -// RUN: %target-swift-frontend -primary-file %s -emit-ir -swift-version 6 -enable-experimental-feature IsolatedConformances | %FileCheck %s -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -primary-file %s -emit-ir -swift-version 6 | %FileCheck %s -DINT=i%target-ptrsize // REQUIRES: PTRSIZE=64 // REQUIRES: concurrency -// REQUIRES: swift_feature_IsolatedConformances // UNSUPPORTED: CPU=arm64e protocol P { diff --git a/test/IRGen/run-coroutine_accessors.swift b/test/IRGen/run-coroutine_accessors.swift index acebfd04896..2f0b9f57894 100644 --- a/test/IRGen/run-coroutine_accessors.swift +++ b/test/IRGen/run-coroutine_accessors.swift @@ -94,8 +94,6 @@ // UNSUPPORTED: wasm // UNSUPPORTED: OS=wasi // UNSUPPORTED: CPU=wasm32 -// TODO: CoroutineAccessors: Enable on Windows. -// UNSUPPORTED: OS=windows-msvc // REQUIRES: swift_feature_CoroutineAccessors diff --git a/test/Interop/Cxx/class/access/private-fileid-irgen.swift b/test/Interop/Cxx/class/access/private-fileid-irgen.swift index 2a16ed4ca1c..d7c30f6c989 100644 --- a/test/Interop/Cxx/class/access/private-fileid-irgen.swift +++ b/test/Interop/Cxx/class/access/private-fileid-irgen.swift @@ -1,6 +1,7 @@ //--- blessed.swift // RUN: split-file %s %t // RUN: %target-swift-frontend -emit-ir -module-name main %t/blessed.swift -I %S/Inputs -cxx-interoperability-mode=default -Onone | %FileCheck %s +// RUN: %target-swift-frontend -emit-ir -module-name main %t/blessed.swift -I %S/Inputs -cxx-interoperability-mode=default -Onone -g | %FileCheck %s import NonPublic diff --git a/test/Interop/Cxx/class/constructors-diagnostics.swift b/test/Interop/Cxx/class/constructors-diagnostics.swift index 7b61769752d..aac01480660 100644 --- a/test/Interop/Cxx/class/constructors-diagnostics.swift +++ b/test/Interop/Cxx/class/constructors-diagnostics.swift @@ -1,8 +1,7 @@ -// RUN: %target-swift-frontend -typecheck -verify -I %S/Inputs %s -cxx-interoperability-mode=default -enable-experimental-feature CXXForeignReferenceTypeInitializers -disable-availability-checking -verify-additional-file %S/Inputs/constructors.h -Xcc -Wno-nullability-completeness +// RUN: %target-swift-frontend -typecheck -verify -I %S/Inputs %s -cxx-interoperability-mode=default -disable-availability-checking -verify-additional-file %S/Inputs/constructors.h -Xcc -Wno-nullability-completeness -// This test uses -verify-additional-file, which do not work well on Windows: +// This test uses -verify-additional-file, which do not work well on Windows. // UNSUPPORTED: OS=windows-msvc -// REQUIRES: swift_feature_CXXForeignReferenceTypeInitializers import Constructors diff --git a/test/Interop/Cxx/class/constructors-executable.swift b/test/Interop/Cxx/class/constructors-executable.swift index fc0142d4ce1..d28898159cb 100644 --- a/test/Interop/Cxx/class/constructors-executable.swift +++ b/test/Interop/Cxx/class/constructors-executable.swift @@ -1,7 +1,6 @@ -// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -cxx-interoperability-mode=default -enable-experimental-feature CXXForeignReferenceTypeInitializers -Xfrontend -disable-availability-checking) +// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -cxx-interoperability-mode=default -Xfrontend -disable-availability-checking) // // REQUIRES: executable_test -// REQUIRES: swift_feature_CXXForeignReferenceTypeInitializers import Constructors import CxxStdlib diff --git a/test/Interop/Cxx/foreign-reference/Inputs/inheritance.h b/test/Interop/Cxx/foreign-reference/Inputs/inheritance.h index bbe1d12ba2c..91db728a6e4 100644 --- a/test/Interop/Cxx/foreign-reference/Inputs/inheritance.h +++ b/test/Interop/Cxx/foreign-reference/Inputs/inheritance.h @@ -109,7 +109,7 @@ __attribute__((swift_attr("release:immortal"))) ImmortalRefType {}; ImmortalRefType *returnImmortalRefType() { return new ImmortalRefType(); }; struct DerivedFromImmortalRefType : ImmortalRefType {}; -DerivedFromImmortalRefType *returnDerivedFromImmortalRefType() { +DerivedFromImmortalRefType *returnDerivedFromImmortalRefType() { // expected-warning {{'returnDerivedFromImmortalRefType' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}} return new DerivedFromImmortalRefType(); }; @@ -138,7 +138,7 @@ DerivedFromValueTypeAndAnnotated *returnDerivedFromValueTypeAndAnnotated() { // } struct DerivedFromRefType final : RefType {}; -DerivedFromRefType *returnDerivedFromRefType() { +DerivedFromRefType *returnDerivedFromRefType() { // expected-warning {{'returnDerivedFromRefType' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}} return new DerivedFromRefType(); } @@ -209,7 +209,7 @@ __attribute__((swift_attr("release:RCRelease"))) RefType {}; RefType *returnRefType() { return new RefType(); }; // expected-warning {{'returnRefType' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENC}} struct DerivedFromRefType final : RefType {}; -DerivedFromRefType *returnDerivedFromRefType() { // TODO: rdar://145098078 Missing SWIFT_RETURNS_(UN)RETAINED annotation warning should trigger for inferred foreeign reference types as well +DerivedFromRefType *returnDerivedFromRefType() { // expected-warning {{'returnDerivedFromRefType' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}} return new DerivedFromRefType(); }; } // namespace BasicInheritanceExample @@ -236,7 +236,7 @@ DerivedFromBaseRef1AndBaseRef2 *returnDerivedFromBaseRef1AndBaseRef2() { }; struct DerivedFromBaseRef3 : BaseRef3 {}; -DerivedFromBaseRef3 *returnDerivedFromBaseRef3() { +DerivedFromBaseRef3 *returnDerivedFromBaseRef3() { // expected-warning {{'returnDerivedFromBaseRef3' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}} return new DerivedFromBaseRef3(); }; } // namespace MultipleInheritanceExample1 @@ -312,7 +312,7 @@ __attribute__((swift_attr("release:samerelease"))) B2 {}; // expected-error {{m struct D : B1, B2 {}; // expected-error {{multiple functions 'sameretain' found; there must be exactly one retain function for reference type 'D'}} // expected-error@-1 {{multiple functions 'samerelease' found; there must be exactly one release function for reference type 'D'}} -D *returnD() { return new D(); }; +D *returnD() { return new D(); }; // expected-warning {{'returnD' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}} } // namespace OverloadedRetainRelease void sameretain(OverloadedRetainRelease::B1 *v) {} @@ -339,7 +339,7 @@ struct BVirtual : virtual A {}; struct CVirtual : virtual A {}; struct VirtualDiamond : BVirtual, CVirtual {}; -VirtualDiamond *returnVirtualDiamond() { return new VirtualDiamond(); }; +VirtualDiamond *returnVirtualDiamond() { return new VirtualDiamond(); }; // expected-warning {{'returnVirtualDiamond' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}} } // namespace RefTypeDiamondInheritance void retainA(RefTypeDiamondInheritance::A *a) {}; @@ -355,7 +355,7 @@ __attribute__((swift_attr("release:releaseB"))) B : A {}; struct C : A {}; struct Diamond : B, C {}; -Diamond *returnDiamond() { return new Diamond(); }; +Diamond *returnDiamond() { return new Diamond(); }; // expected-warning {{'returnDiamond' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}} } // namespace NonRefTypeDiamondInheritance @@ -384,7 +384,7 @@ private: }; class Forest : public IntrusiveRefCountedTemplate {}; -Forest *returnForest() { return new Forest(); }; +Forest *returnForest() { return new Forest(); }; // expected-warning {{'returnForest' should be annotated with either SWIFT_RETURNS_RETAINED or SWIFT_RETURNS_UNRETAINED as it is returning a SWIFT_SHARED_REFERENCE}} } // namespace InheritingTemplatedRefType void forestRetain(InheritingTemplatedRefType::IntrusiveRefCountedTemplate< diff --git a/test/Interop/Cxx/foreign-reference/no-ctor-errors.swift b/test/Interop/Cxx/foreign-reference/cxx-frt-ctor-callable.swift similarity index 61% rename from test/Interop/Cxx/foreign-reference/no-ctor-errors.swift rename to test/Interop/Cxx/foreign-reference/cxx-frt-ctor-callable.swift index b8155509e8f..a90f4f28f36 100644 --- a/test/Interop/Cxx/foreign-reference/no-ctor-errors.swift +++ b/test/Interop/Cxx/foreign-reference/cxx-frt-ctor-callable.swift @@ -1,6 +1,6 @@ // RUN: rm -rf %t // RUN: split-file %s %t -// RUN: not %target-swift-frontend -typecheck -I %t/Inputs %t/test.swift -enable-experimental-cxx-interop 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -typecheck -verify -I %t/Inputs %t/test.swift -enable-experimental-cxx-interop -disable-availability-checking //--- Inputs/module.modulemap module Test { @@ -21,5 +21,4 @@ HasCtor { import Test -// CHECK: error: 'HasCtor' cannot be constructed because it has no accessible initializers -let x = HasCtor(42) \ No newline at end of file +let x = HasCtor(42) diff --git a/test/Interop/Cxx/foreign-reference/move-only-module-interface.swift b/test/Interop/Cxx/foreign-reference/move-only-module-interface.swift index 82389914336..f20f81ac32e 100644 --- a/test/Interop/Cxx/foreign-reference/move-only-module-interface.swift +++ b/test/Interop/Cxx/foreign-reference/move-only-module-interface.swift @@ -1,7 +1,7 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=MoveOnly -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s // CHECK: class MoveOnly { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> MoveOnly @@ -9,14 +9,14 @@ // CHECK-NOT: func moveIntoResult(_ x: MoveOnly) -> MoveOnly // CHECK: class HasMoveOnlyChild { -// CHECK-NOT: init +// CHECK: init // CHECK-NOT: var child: MoveOnly // CHECK: class func create() -> HasMoveOnlyChild // CHECK: } // CHECK-NOT: func moveIntoResult(_ x: HasMoveOnlyChild) -> HasMoveOnlyChild // CHECK: class PrivateCopyCtor { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> PrivateCopyCtor @@ -24,7 +24,7 @@ // CHECK-NOT: func moveIntoResult(_ x: PrivateCopyCtor) -> PrivateCopyCtor // CHECK: class BadCopyCtor { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> BadCopyCtor diff --git a/test/Interop/Cxx/foreign-reference/pod-module-interface.swift b/test/Interop/Cxx/foreign-reference/pod-module-interface.swift index 5e3f950aaff..984ac99257c 100644 --- a/test/Interop/Cxx/foreign-reference/pod-module-interface.swift +++ b/test/Interop/Cxx/foreign-reference/pod-module-interface.swift @@ -1,7 +1,7 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=POD -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s // CHECK: class Empty { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> Empty @@ -12,14 +12,14 @@ // CHECK-NOT: func passThroughByValue(_ x: Empty) -> Empty // CHECK: class MultipleAttrs { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> MultipleAttrs // CHECK: } // CHECK: class IntPair { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: func instancePassThroughByRef(_ ref: IntPair) -> IntPair @@ -33,7 +33,7 @@ // CHECK: func passThroughByRef(_ x: IntPair) -> IntPair // CHECK: class RefHoldingPair { -// CHECK-NOT: init +// CHECK: init // CHECK-NOT: pair // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 @@ -42,7 +42,7 @@ // CHECK: } // CHECK: class RefHoldingPairRef { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> RefHoldingPairRef @@ -51,7 +51,7 @@ // CHECK: } // CHECK: class RefHoldingPairPtr { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> RefHoldingPairPtr @@ -60,7 +60,6 @@ // CHECK: } // CHECK: struct ValueHoldingPair { -// CHECK-NOT: init // CHECK-NOT: pair // CHECK: init() // CHECK: func test() -> Int32 @@ -77,7 +76,7 @@ // CHECK: } // CHECK: class BigType { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> BigType diff --git a/test/Interop/Cxx/foreign-reference/singleton-module-interface.swift b/test/Interop/Cxx/foreign-reference/singleton-module-interface.swift index 83a1e95d763..bca9a093f2d 100644 --- a/test/Interop/Cxx/foreign-reference/singleton-module-interface.swift +++ b/test/Interop/Cxx/foreign-reference/singleton-module-interface.swift @@ -1,7 +1,7 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=Singleton -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s // CHECK: class DeletedDtor { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> DeletedDtor @@ -10,7 +10,7 @@ // CHECK: func mutateIt(_ x: DeletedDtor) // CHECK: class PrivateDtor { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> PrivateDtor @@ -19,7 +19,7 @@ // CHECK: func mutateIt(_ x: PrivateDtor) // CHECK: class DeletedSpecialMembers { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> DeletedSpecialMembers @@ -28,7 +28,7 @@ // CHECK: func mutateIt(_ x: DeletedSpecialMembers) // CHECK: class PrivateSpecialMembers { -// CHECK-NOT: init +// CHECK: init // CHECK: func test() -> Int32 // CHECK: func testMutable() -> Int32 // CHECK: class func create() -> PrivateSpecialMembers diff --git a/test/Interop/Cxx/operators/Inputs/non-member-inline.h b/test/Interop/Cxx/operators/Inputs/non-member-inline.h index 96f087e879f..04109aae4f5 100644 --- a/test/Interop/Cxx/operators/Inputs/non-member-inline.h +++ b/test/Interop/Cxx/operators/Inputs/non-member-inline.h @@ -94,6 +94,29 @@ inline bool operator==(const ClassWithOperatorEqualsParamUnnamed &, return false; } +struct RValueArithmetic { + int value; +}; + +RValueArithmetic operator+(const RValueArithmetic &lhs, + RValueArithmetic &&rhs) { + return {lhs.value + rhs.value}; +} + +struct LValueAndRValueArithmetic { + int value; +}; + +LValueAndRValueArithmetic operator+(const LValueAndRValueArithmetic &lhs, + const LValueAndRValueArithmetic &rhs) { + return {lhs.value + rhs.value}; +} + +LValueAndRValueArithmetic operator+(const LValueAndRValueArithmetic &lhs, + LValueAndRValueArithmetic &&rhs) { + return {lhs.value + rhs.value}; +} + // Make sure that we don't crash on templated operators template struct S {}; template S operator+(S lhs, S rhs); diff --git a/test/Interop/Cxx/operators/non-member-inline-module-interface.swift b/test/Interop/Cxx/operators/non-member-inline-module-interface.swift index 84036976f1f..222fb5adaf4 100644 --- a/test/Interop/Cxx/operators/non-member-inline-module-interface.swift +++ b/test/Interop/Cxx/operators/non-member-inline-module-interface.swift @@ -19,3 +19,7 @@ // CHECK: func && (lhs: LoadableBoolWrapper, rhs: LoadableBoolWrapper) -> LoadableBoolWrapper // CHECK-NEXT: func || (lhs: LoadableBoolWrapper, rhs: LoadableBoolWrapper) -> LoadableBoolWrapper + +// CHECK-NOT: func + (lhs: RValueArithmetic + +// CHECK: func + (lhs: LValueAndRValueArithmetic, rhs: LValueAndRValueArithmetic) -> LValueAndRValueArithmetic diff --git a/test/Interop/Cxx/operators/non-member-inline-typechecker.swift b/test/Interop/Cxx/operators/non-member-inline-typechecker.swift index bc02abab94c..ed61c93a13d 100644 --- a/test/Interop/Cxx/operators/non-member-inline-typechecker.swift +++ b/test/Interop/Cxx/operators/non-member-inline-typechecker.swift @@ -37,3 +37,11 @@ var rhsBool = LoadableBoolWrapper(value: false) let resultAmpAmp = lhsBool && rhsBool let resultPipePipe = lhsBool && rhsBool + +let lhsRValue = RValueArithmetic(value: 123) +let rhsRValue = RValueArithmetic(value: 146) +let resultRValue = lhsRValue + rhsRValue // expected-error {{binary operator '+' cannot be applied to two 'RValueArithmetic' operands}} + +let lhsLRValue = LValueAndRValueArithmetic(value: 123) +let rhsLRValue = LValueAndRValueArithmetic(value: 146) +let resultLRValue = lhsLRValue + rhsLRValue diff --git a/test/Interop/Cxx/operators/non-member-inline.swift b/test/Interop/Cxx/operators/non-member-inline.swift index 6b6bcb6b6cc..ee763941b70 100644 --- a/test/Interop/Cxx/operators/non-member-inline.swift +++ b/test/Interop/Cxx/operators/non-member-inline.swift @@ -193,4 +193,11 @@ OperatorsTestSuite.test("UnnamedParameterInOperator.equal") { expectFalse(lhs == rhs) } +OperatorsTestSuite.test("LValueAndRValueArithmetic.+") { + let lhs = LValueAndRValueArithmetic(value: 123) + let rhs = LValueAndRValueArithmetic(value: 146) + + expectEqual(269, (lhs + rhs).value) +} + runAllTests() diff --git a/test/Interop/Cxx/symbolic-imports/print-symbolic-module-interface.swift b/test/Interop/Cxx/symbolic-imports/print-symbolic-module-interface.swift index 21fdcbefd8c..a52236c5182 100644 --- a/test/Interop/Cxx/symbolic-imports/print-symbolic-module-interface.swift +++ b/test/Interop/Cxx/symbolic-imports/print-symbolic-module-interface.swift @@ -119,6 +119,7 @@ struct NonCopyable { // CHECK-NEXT: } // CHECK-NEXT: } // CHECK: class MyImmortal { +// CHECK-NEXT: init // CHECK-NEXT: func foo() // CHECK-NEXT: } // CHECK-NEXT: struct NonCopyable { diff --git a/test/Interop/Cxx/templates/function-template-silgen.swift b/test/Interop/Cxx/templates/function-template-silgen.swift index 5a0a93f63b6..9b93e7b6212 100644 --- a/test/Interop/Cxx/templates/function-template-silgen.swift +++ b/test/Interop/Cxx/templates/function-template-silgen.swift @@ -16,8 +16,10 @@ import FunctionTemplates // CHECK: [[ADD_TWO_FN:%.*]] = function_ref @{{_Z18addMixedTypeParamsIiiET_S0_T0_|\?\?\$addMixedTypeParams@HH@@YAHHH@Z}} : $@convention(c) (Int32, Int32) -> Int32 // CHECK: [[C:%.*]] = apply [[ADD_TWO_FN]]([[A]], [[B]]) : $@convention(c) (Int32, Int32) -> Int32 +// CHECK: [[C_32_ADDR:%.*]] = alloc_stack $Int32 +// CHECK: [[C_32:%.*]] = load [[C_32_ADDR]] : $*Int32 // CHECK: [[ADD_FN:%.*]] = function_ref @{{_Z17addSameTypeParamsIiET_S0_S0_|\?\?\$addSameTypeParams@H@@YAHHH@Z}} : $@convention(c) (Int32, Int32) -> Int32 -// CHECK: [[OUT:%.*]] = apply [[ADD_FN]]([[B]], [[C_32:%.*]]) : $@convention(c) (Int32, Int32) -> Int32 +// CHECK: [[OUT:%.*]] = apply [[ADD_FN]]([[B]], [[C_32]]) : $@convention(c) (Int32, Int32) -> Int32 // CHECK: return [[OUT]] : $Int32 // CHECK-LABEL: end sil function '$s4main4test1xs5Int32VAE_tF' diff --git a/test/Interpreter/coroutine_accessors_default_implementations.swift b/test/Interpreter/coroutine_accessors_default_implementations.swift index 49551c13123..ecd7b2bb973 100644 --- a/test/Interpreter/coroutine_accessors_default_implementations.swift +++ b/test/Interpreter/coroutine_accessors_default_implementations.swift @@ -5,6 +5,7 @@ // RUN: %target-swift-frontend \ // RUN: %t/Library.swift \ // RUN: %t/LibImpl.underscored.swift \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -emit-module \ // RUN: -module-name Library \ // RUN: -parse-as-library \ @@ -13,6 +14,7 @@ // RUN: %target-swift-frontend \ // RUN: %t/Executable.swift \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -c \ // RUN: -parse-as-library \ // RUN: -module-name Executable \ @@ -22,6 +24,7 @@ // RUN: %target-build-swift-dylib(%t/%target-library-name(Library)) \ // RUN: %t/Library.swift \ // RUN: %t/LibImpl.nonunderscored.swift \ +// RUN: -Xfrontend -enable-callee-allocated-coro-abi \ // RUN: -emit-module \ // RUN: -enable-library-evolution \ // RUN: -enable-experimental-feature CoroutineAccessors \ diff --git a/test/Interpreter/coroutine_accessors_old_abi_nounwind.swift b/test/Interpreter/coroutine_accessors_old_abi_nounwind.swift new file mode 100644 index 00000000000..79cad8f0a3c --- /dev/null +++ b/test/Interpreter/coroutine_accessors_old_abi_nounwind.swift @@ -0,0 +1,41 @@ +// RUN: %target-run-simple-swift(-Xfrontend -disable-callee-allocated-coro-abi -enable-experimental-feature CoroutineAccessors) | %FileCheck %s + +// REQUIRES: swift_feature_CoroutineAccessors +// REQUIRES: executable_test + +struct AirOr : Error { +} + +struct Thrower { + mutating func doit() throws { + throw AirOr() + } +} + +struct S { + var _thrower = Thrower() + var thrower: Thrower { + read { + yield _thrower + } + modify { + // CHECK: first + print("first") + yield &_thrower + // CHECK: also ran + print("also ran") + } + } +} + +func doit() { + do { + var s = S() + try s.thrower.doit() + } catch let error { + // CHECK: AirOr + print(error) + } +} + +doit() diff --git a/test/Misc/serialized-diagnostics-prettyprint.swift b/test/Misc/serialized-diagnostics-prettyprint.swift index d0e0cab3040..80bc62521e1 100644 --- a/test/Misc/serialized-diagnostics-prettyprint.swift +++ b/test/Misc/serialized-diagnostics-prettyprint.swift @@ -1,14 +1,32 @@ -// RUN: rm -f %t.* +// RUN: %empty-directory(%t) +// RUN: split-file %s %t -// Test swift executable -// RUN: %target-swift-frontend -typecheck -serialize-diagnostics-path %t.dia %s -verify -// RUN: c-index-test -read-diagnostics %t.dia > %t.deserialized_diagnostics.txt 2>&1 -// RUN: %FileCheck --input-file=%t.deserialized_diagnostics.txt %s +// RUN: %target-swift-frontend -emit-module %t/Lib.swift -module-name Lib -emit-module-path %t/Lib.swiftmodule +// RUN: %target-swift-frontend -typecheck -I %t -serialize-diagnostics-path %t/diags.dia %t/Client.swift -verify +// RUN: c-index-test -read-diagnostics %t/diags.dia > %t/diags.deserialized_diagnostics.txt 2>&1 +// RUN: %FileCheck --input-file=%t/diags.deserialized_diagnostics.txt %t/Client.swift -var x = String.init // expected-error{{ambiguous use of 'init'}} -// CHECK: {{.*[/\\]}}serialized-diagnostics-prettyprint.swift:[[@LINE-1]]:16: error: ambiguous use of 'init' +//--- Lib.swift -// CHECK: Swift.String.init:2:19: note: found this candidate -// CHECK: CONTENTS OF FILE Swift.String.init: -// CHECK: struct String { -// CHECK: public init(_ content: Substring.UnicodeScalarView) +public struct S { + public init(a: String) {} + @discardableResult // We use @discardableResult here to give this init a distinct line number in the pretty print. + public init(b: Int) {} +} + +//--- Client.swift + +import Lib + +var x = S.init // expected-error {{ambiguous use of 'init'}} +// CHECK: {{.*[/\\]}}Client.swift:[[@LINE-1]]:11: error: ambiguous use of 'init' + +// FIXME: Currently we can't check for 'init(a:)' because c-index-test is +// keying the source file contents on the buffer identifier, so we end up +// with duplicated 'init(b:)'. + +// CHECK: Lib.S.init:3:10: note: found this candidate +// CHECK: CONTENTS OF FILE Lib.S.init: +// CHECK: struct S { +// CHECK-NEXT: @discardableResult +// CHECK-NEXT: public init(b: Int) diff --git a/test/Misc/stats_dir_profiler.swift b/test/Misc/stats_dir_profiler.swift index 96d294fbbfd..6976ee9d896 100644 --- a/test/Misc/stats_dir_profiler.swift +++ b/test/Misc/stats_dir_profiler.swift @@ -4,11 +4,14 @@ // RUN: %target-swiftc_driver -o %t/main -module-name main -stats-output-dir %t/stats-events %s -profile-stats-events -Xfrontend -fine-grained-timers // RUN: %target-swiftc_driver -o %t/main -module-name main -stats-output-dir %t/stats-entities %s -profile-stats-entities -Xfrontend -fine-grained-timers -// Need to use %long-tmp because in Windows the * may expand to a path longer -// than 260 characters. // RUN: %FileCheck -check-prefix=EVENTS -input-file %long-tmp/stats-events/*.dir/Time.User.events %s // RUN: %FileCheck -check-prefix=ENTITIES -input-file %long-tmp/stats-entities/*.dir/Time.User.entities %s +// It's good enough to run the test on macOS and linux. +// It takes very long on Windows because the * may expand to a path longer than 260 characters. + +// REQUIRES: OS=macosx || OS=linux-gnu + // EVENTS: {{perform-sema;.*;typecheck-decl.* [0-9]+}} // ENTITIES: {{perform-sema;.*;TypeCheckFunctionBodyRequest bar\(\);typecheck-stmt.* [0-9]+}} diff --git a/test/ModuleInterface/attrs.swift b/test/ModuleInterface/attrs.swift index 022ac15ff4a..974f9d209bd 100644 --- a/test/ModuleInterface/attrs.swift +++ b/test/ModuleInterface/attrs.swift @@ -1,7 +1,6 @@ // RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name attrs \ // RUN: -emit-private-module-interface-path %t.private.swiftinterface \ -// RUN: -enable-experimental-feature ABIAttribute \ -// RUN: -enable-experimental-feature ExecutionAttribute +// RUN: -enable-experimental-feature ABIAttribute // RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name attrs // RUN: %target-swift-typecheck-module-from-interface(%t.private.swiftinterface) -module-name attrs @@ -10,7 +9,6 @@ // RUN: %FileCheck %s --check-prefixes CHECK,PRIVATE-CHECK --input-file %t.private.swiftinterface // REQUIRES: swift_feature_ABIAttribute -// REQUIRES: swift_feature_ExecutionAttribute // CHECK: @_transparent public func glass() -> Swift.Int { return 0 }{{$}} @_transparent public func glass() -> Int { return 0 } @@ -87,13 +85,13 @@ public struct MutatingTest { @abi(func abiSpiFunc()) @_spi(spiGroup) public func abiSpiFunc() {} -@execution(concurrent) +@concurrent public func testExecutionConcurrent() async {} -// CHECK: @execution(concurrent) public func testExecutionConcurrent() async +// CHECK: @concurrent public func testExecutionConcurrent() async -@execution(caller) +nonisolated(nonsending) public func testExecutionCaller() async {} -// CHECK: @execution(caller) public func testExecutionCaller() async +// CHECK: nonisolated(nonsending) public func testExecutionCaller() async // CHECK-NOT: @extensible // CHECK: public enum TestExtensible @@ -102,3 +100,12 @@ public enum TestExtensible { case a case b } + +public struct TestPlacementOfAttrsAndSpecifiers { + // CHECK: public func test1(_: sending @autoclosure () -> T) + public func test1(_: sending @autoclosure () -> T) {} + // CHECK: public func test2(_: borrowing @autoclosure () -> T) + public func test2(_: borrowing @autoclosure () -> T) {} + // CHECK: public func test3(_: inout () async -> T) + public func test3(_: inout () async -> T) {} +} diff --git a/test/ModuleInterface/execution_attr.swift b/test/ModuleInterface/execution_attr.swift deleted file mode 100644 index d3b2e4ada0a..00000000000 --- a/test/ModuleInterface/execution_attr.swift +++ /dev/null @@ -1,66 +0,0 @@ -// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name execution_attr -enable-experimental-feature ExecutionAttribute -// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name execution_attr - -// RUN: %FileCheck %s --input-file %t.swiftinterface - -// REQUIRES: swift_feature_ExecutionAttribute - -public struct Test { - // CHECK: #if compiler(>=5.3) && $ExecutionAttribute - // CHECK-NEXT: @execution(caller) public init() async - // CHECK-NEXT: #else - // CHECK-NEXT: public init() async - // CHECK-NEXT: #endif - @execution(caller) - public init() async { - } - - // CHECK: #if compiler(>=5.3) && $ExecutionAttribute - // CHECK-NEXT: @execution(concurrent) public func test() async - // CHECK-NEXT: #else - // CHECK-NEXT: public func test() async - // CHECK-NEXT: #endif - @execution(concurrent) - public func test() async { - } - - // CHECK: #if compiler(>=5.3) && $ExecutionAttribute - // CHECK-NEXT: public func other(_: @execution(caller) () async -> Swift.Void) - // CHECK-NEXT: #else - // CHECK-NEXT: public func other(_: () async -> Swift.Void) - // CHECK-NEXT: #endif - public func other(_: @execution(caller) () async -> Void) {} - - // CHECK: #if compiler(>=5.3) && $ExecutionAttribute - // CHECK-NEXT: @execution(caller) public var testOnVar: Swift.Int { - // CHECK-NEXT: get async - // CHECK-NEXT: } - // CHECK-NEXT: #else - // CHECK-NEXT: public var testOnVar: Swift.Int { - // CHECK-NEXT: get async - // CHECK-NEXT: } - // CHECK-NEXT: #endif - @execution(caller) - public var testOnVar: Int { - get async { - 42 - } - } - - // CHECK: #if compiler(>=5.3) && $ExecutionAttribute - // CHECK-NEXT: @execution(caller) public subscript(onSubscript _: Swift.Int) -> Swift.Bool { - // CHECK-NEXT: get async - // CHECK-NEXT: } - // CHECK-NEXT: #else - // CHECK-NEXT: public subscript(onSubscript _: Swift.Int) -> Swift.Bool { - // CHECK-NEXT: get async - // CHECK-NEXT: } - // CHECK-NEXT: #endif - @execution(caller) - public subscript(onSubscript _: Int) -> Bool { - get async { - false - } - } -} - diff --git a/test/ModuleInterface/execution_behavior_attrs.swift b/test/ModuleInterface/execution_behavior_attrs.swift new file mode 100644 index 00000000000..a592110dd5f --- /dev/null +++ b/test/ModuleInterface/execution_behavior_attrs.swift @@ -0,0 +1,116 @@ +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name execution_attr +// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name execution_attr + +// RUN: %FileCheck %s --input-file %t.swiftinterface + +// REQUIRES: concurrency + +public struct TestWithAttrs { + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: public func test(_: nonisolated(nonsending) @escaping () async -> Swift.Void) + // CHECK-NEXT: #endif + public func test(_: nonisolated(nonsending) @escaping () async -> Void) {} + + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: public func withInOut(fn: nonisolated(nonsending) inout () async -> Swift.Void) + // CHECK-NEXT: #endif + public func withInOut(fn: nonisolated(nonsending) inout () async -> Void) {} +} + +public struct Test { + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: nonisolated(nonsending) public init() async + // CHECK-NEXT: #endif + nonisolated(nonsending) + public init() async { + } + + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: @concurrent public init(something: Swift.Int) async + // CHECK-NEXT: #endif + @concurrent + public init(something: Int) async { + } + + // CHECK-NOT: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK: public init(noExplicit: Swift.String) async + // CHECK-NOT: #endif + public init(noExplicit: String) async { + } + + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: @concurrent public func test() async + // CHECK-NEXT: #endif + @concurrent + public func test() async { + } + + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: public func other(_: nonisolated(nonsending) () async -> Swift.Void) + // CHECK-NEXT: #endif + public func other(_: nonisolated(nonsending) () async -> Void) {} + + // CHECK-NOT: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK: public func concurrentResult(_: () async -> Swift.Void) -> (Swift.Int) async -> Swift.Void + // CHECK-NOT: #endif + public func concurrentResult(_: () async -> Void) -> @concurrent (Int) async -> Void {} + + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: public func nestedPositions1(_: Swift.Array<[Swift.String : nonisolated(nonsending) (Swift.Int) async -> Swift.Void]>) + // CHECK-NEXT: #endif + public func nestedPositions1(_: Array<[String: nonisolated(nonsending) (Int) async -> Void]>) {} + + // CHECK-NOT: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK: public func nestedPositions2(_: Swift.Array<[Swift.String : (Swift.Int) async -> Swift.Void]>) + // CHECK-NOT: #endif + public func nestedPositions2(_: Array<[String: @concurrent (Int) async -> Void]>) {} + + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: nonisolated(nonsending) public var testOnVar: Swift.Int { + // CHECK-NEXT: get async + // CHECK-NEXT: } + // CHECK-NEXT: #endif + nonisolated(nonsending) + public var testOnVar: Int { + get async { + 42 + } + } + + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: @concurrent public var testOnVarConcurrent: Swift.Int { + // CHECK-NEXT: get async + // CHECK-NEXT: } + // CHECK-NEXT: #endif + @concurrent + public var testOnVarConcurrent: Int { + get async { + 42 + } + } + + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: nonisolated(nonsending) public subscript(onSubscript _: Swift.Int) -> Swift.Bool { + // CHECK-NEXT: get async + // CHECK-NEXT: } + // CHECK-NEXT: #endif + nonisolated(nonsending) + public subscript(onSubscript _: Int) -> Bool { + get async { + false + } + } + + // CHECK: #if compiler(>=5.3) && $AsyncExecutionBehaviorAttributes + // CHECK-NEXT: @concurrent public subscript(concurrentOnSubscript _: Swift.Int) -> Swift.Bool { + // CHECK-NEXT: get async + // CHECK-NEXT: } + // CHECK-NEXT: #endif + @concurrent + public subscript(concurrentOnSubscript _: Int) -> Bool { + get async { + false + } + } +} + diff --git a/test/ModuleInterface/isolated_conformance.swift b/test/ModuleInterface/isolated_conformance.swift index 23d93fcdd45..9ed21b8b67e 100644 --- a/test/ModuleInterface/isolated_conformance.swift +++ b/test/ModuleInterface/isolated_conformance.swift @@ -1,6 +1,5 @@ -// RUN: %target-swift-frontend -typecheck -swift-version 6 -enable-library-evolution -module-name isolated_conformance -enable-experimental-feature IsolatedConformances -emit-module-interface-path - %s | %FileCheck %s +// RUN: %target-swift-frontend -typecheck -swift-version 6 -enable-library-evolution -module-name isolated_conformance -emit-module-interface-path - %s | %FileCheck %s -// REQUIRES: swift_feature_IsolatedConformances // REQUIRES: concurrency public protocol MyProtocol { diff --git a/test/ModuleInterface/value_generics.swift b/test/ModuleInterface/value_generics.swift index 4c46447e0dc..e7b21c69304 100644 --- a/test/ModuleInterface/value_generics.swift +++ b/test/ModuleInterface/value_generics.swift @@ -14,6 +14,8 @@ public struct Slab { public var count: Int { N } + + public init() {} } // CHECK: public func usesGenericSlab(_: ValueGeneric.Slab) @@ -24,3 +26,27 @@ public func usesConcreteSlab(_: Slab) {} // CHECK: public func usesNegativeSlab(_: ValueGeneric.Slab) public func usesNegativeSlab(_: Slab) {} + +// CHECK: $ValueGenericsNameLookup +@inlinable +public func test() -> Int { + // CHECK: Slab.N + Slab.N +} + +// CHECK: $ValueGenericsNameLookup +@inlinable +public func test2() -> Int { + // CHECK: type(of: Slab()).N + type(of: Slab()).N +} + +// CHECK: $ValueGenericsNameLookup +@inlinable +public func test3() { + { + print(123) + print(123) + print(Slab.N) + }() +} diff --git a/test/Parse/execution.swift b/test/Parse/execution.swift deleted file mode 100644 index 18d5a9e51ea..00000000000 --- a/test/Parse/execution.swift +++ /dev/null @@ -1,56 +0,0 @@ -// RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-experimental-feature ExecutionAttribute - -// REQUIRES: concurrency -// REQUIRES: swift_feature_ExecutionAttribute - -typealias F = @execution(concurrent) () async -> Void - -typealias E = @execution(concurrent) () -> Void -// expected-error@-1 {{cannot use '@execution' on non-async function type}} - -func test1(_: @execution(caller) (Int...) async -> Void) {} -func test2(_: @execution(concurrent) (Int...) async -> Void) {} - -func test_err1_concurrent(_: @execution(concurrent) @MainActor () async -> Void) {} -// expected-error@-1 {{cannot use '@execution' because function type is isolated to a global actor 'MainActor'}} - -func test_err1_caller(_: @execution(caller) @MainActor () async -> Void) {} -// expected-error@-1 {{cannot use '@execution' because function type is isolated to a global actor 'MainActor'}} - -func test_err2_concurrent(_: @execution(concurrent) @isolated(any) () async -> Void) {} -// expected-error@-1 {{cannot use '@execution' together with @isolated(any)}} - -func test_err2_caller(_: @execution(caller) @isolated(any) () async -> Void) {} -// expected-error@-1 {{cannot use '@execution' together with @isolated(any)}} - -func test_err3_concurrent(_: @execution(concurrent) (isolated (any Actor)?) async -> Void) {} -// expected-error@-1 {{cannot use '@execution' together with an isolated parameter}} - -func test_err3_caller(_: @execution(caller) (isolated (any Actor)?) async -> Void) {} -// expected-error@-1 {{cannot use '@execution' together with an isolated parameter}} - -func test_err4(_: @execution (Int) -> Void) {} -// expected-error@-1 {{expected 'concurrent' or 'caller' as the execution behavior}} -// expected-error@-2 {{expected parameter type following ':'}} - -func test_err5(_: @execution( () async -> Void) {} -// expected-error@-1 {{expected 'concurrent' or 'caller' as the execution behavior}} -// expected-note@-2 {{to match this opening '('}} -// expected-error@-3 {{expected ')' after execution behavior}} - -func test_err6(_: @execution(hello) () async -> Void) {} -// expected-error@-1 {{expected 'concurrent' or 'caller' as the execution behavior}} - -func test_err7(_: @execution(hello () async -> Void) {} -// expected-error@-1 {{expected 'concurrent' or 'caller' as the execution behavior}} -// expected-note@-2 {{to match this opening '('}} -// expected-error@-3 {{expected ')' after execution behavior}} - -func test_err8(_: @execution(concurrent) Int) {} // expected-error {{attribute does not apply to type}} - -do { - let _ = [@execution(caller) () async -> Void]() - let _ = [@execution(caller) () -> Void]() - // expected-error@-1 {{cannot use '@execution' on non-async function type}} -} - diff --git a/test/Parse/execution_behavior_attrs.swift b/test/Parse/execution_behavior_attrs.swift new file mode 100644 index 00000000000..6397a35b8e7 --- /dev/null +++ b/test/Parse/execution_behavior_attrs.swift @@ -0,0 +1,86 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking + +// REQUIRES: concurrency + +typealias F = @concurrent () async -> Void + +typealias E = @concurrent () -> Void +// expected-error@-1 {{cannot use '@concurrent' on non-async function type}} + +func test1(_: nonisolated(nonsending) (Int...) async -> Void) {} +func test2(_: @concurrent (Int...) async -> Void) {} + +func test_err1_concurrent(_: @concurrent @MainActor () async -> Void) {} +// expected-error@-1 {{cannot use '@concurrent' because function type is isolated to a global actor 'MainActor'}} + +func test_err1_caller(_: nonisolated(nonsending) @MainActor () async -> Void) {} +// expected-error@-1 {{cannot use 'nonisolated(nonsending)' because function type is isolated to a global actor 'MainActor'}} + +func test_err2_concurrent(_: @concurrent @isolated(any) () async -> Void) {} +// expected-error@-1 {{cannot use '@concurrent' together with @isolated(any)}} + +func test_err2_caller(_: nonisolated(nonsending) @isolated(any) () async -> Void) {} +// expected-error@-1 {{cannot use 'nonisolated(nonsending)' together with @isolated(any)}} + +func test_err3_concurrent(_: @concurrent (isolated (any Actor)?) async -> Void) {} +// expected-error@-1 {{cannot use '@concurrent' together with an isolated parameter}} + +func test_err3_caller(_: nonisolated(nonsending) (isolated (any Actor)?) async -> Void) {} +// expected-error@-1 {{cannot use 'nonisolated(nonsending)' together with an isolated parameter}} + +func test_err4(_: nonisolated (Int) -> Void) {} +// expected-error@-1 {{expected 'nonsending' in modifier}} +// expected-error@-2 {{expected '{' in body of function declaration}} +// expected-warning@-3 {{extraneous whitespace between attribute name and '('; this is an error in the Swift 6 language mode}} +// expected-error@-4 {{consecutive statements on a line must be separated by ';'}} +// expected-error@-5 {{expected expression}} + +func test_err5(_: nonisolated( () async -> Void) {} +// expected-error@-1 {{expected 'nonsending' in modifier}} + +func test_err6(_: nonisolated(hello) () async -> Void) {} +// expected-error@-1 {{expected 'nonsending' in modifier}} +// expected-error@-2 {{cannot have more than one parameter list}} +// expected-error@-3 {{cannot find type 'hello' in scope}} +// expected-error@-4 {{onsecutive statements on a line must be separated by ';'}} +// expected-error@-5 {{expected expression}} + +func test_err7(_: nonisolated(hello () async -> Void) {} +// expected-error@-1 {{expected 'nonsending' in modifier}} +// expected-error@-2 {{cannot find type 'hello' in scope}} + +func test_err8(_: @concurrent Int) {} // expected-error {{attribute does not apply to type}} + +do { + let _ = [nonisolated(nonsending) () async -> Void]() + let _ = [nonisolated(nonsending) () -> Void]() + // expected-error@-1 {{cannot use 'nonisolated(nonsending)' on non-async function type}} +} + +protocol P {} + +struct S : nonisolated + P { // Ok +} + +do { + func nonisolated() {} + + // `nonisolated` is parsed as a function call + nonisolated // expected-error {{function is unused}} + (42) // expected-warning {{integer literal is unused}} + + let _: nonisolated // expected-error {{cannot find type 'nonisolated' in scope}} + (Int) async -> Void // expected-error {{expected member name or initializer call after type name}} + // expected-note@-1 {{use '.self' to reference the type object}} + // expected-warning@-2 {{expression of type '((Int) async -> Void).Type' is unused}} + + _ = [nonisolated()] +} + +do { + func nonisolated(_: Int) -> Int { 42 } + + nonisolated(0) // expected-warning {{result of call to 'nonisolated' is unused}} + print("hello") +} diff --git a/test/SILGen/copy_expr.swift b/test/SILGen/copy_expr.swift index 1cb51175f87..2469df79f25 100644 --- a/test/SILGen/copy_expr.swift +++ b/test/SILGen/copy_expr.swift @@ -439,3 +439,9 @@ func testCallMethodOnAddressOnlyInOutCopy(_ x: inout T) { _ = (copy x).computedK _ = (copy x).consumingComputedK } + +struct Trivial: BitwiseCopyable { var x: Int } + +func copyTrivial(x: inout Trivial) -> Trivial { + return copy x +} diff --git a/test/SILGen/coroutine_accessors.swift b/test/SILGen/coroutine_accessors.swift index a06a224cecd..a9c0edefc6f 100644 --- a/test/SILGen/coroutine_accessors.swift +++ b/test/SILGen/coroutine_accessors.swift @@ -1,11 +1,13 @@ -// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ +// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -enable-library-evolution \ // RUN: -enable-experimental-feature CoroutineAccessors \ // RUN: | %FileCheck %s --check-prefixes=CHECK,CHECK-NOUNWIND -// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ +// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -enable-library-evolution \ // RUN: -enable-experimental-feature CoroutineAccessors \ // RUN: -enable-experimental-feature CoroutineAccessorsUnwindOnCallerError \ diff --git a/test/SILGen/coroutine_accessors_new_abi.swift b/test/SILGen/coroutine_accessors_new_abi.swift new file mode 100644 index 00000000000..c97de4b49a7 --- /dev/null +++ b/test/SILGen/coroutine_accessors_new_abi.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ +// RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ +// RUN: -enable-library-evolution \ +// RUN: -enable-experimental-feature CoroutineAccessors \ +// RUN: | %FileCheck %s --check-prefixes=CHECK,CHECK-NORMAL + +// REQUIRES: swift_feature_CoroutineAccessors + +// CHECK: yield_once_2 + +struct S { + +var one: Int = 1 +var i: Int { + read { + yield one + } +} + +} + diff --git a/test/SILGen/coroutine_accessors_old_abi.swift b/test/SILGen/coroutine_accessors_old_abi.swift new file mode 100644 index 00000000000..cd7450e8535 --- /dev/null +++ b/test/SILGen/coroutine_accessors_old_abi.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ +// RUN: %s \ +// RUN: -disable-callee-allocated-coro-abi \ +// RUN: -enable-library-evolution \ +// RUN: -enable-experimental-feature CoroutineAccessors \ +// RUN: | %FileCheck %s --check-prefixes=CHECK,CHECK-NORMAL + +// REQUIRES: swift_feature_CoroutineAccessors + +// CHECK: yield_once +// CHECK-NOT: yield_once_2 + +struct S { + +var zero: Int = 0 +var i: Int { + read { + yield zero + } +} + +} diff --git a/test/SILGen/coroutine_accessors_skip.swift b/test/SILGen/coroutine_accessors_skip.swift index d5717302be9..a057ac92e12 100644 --- a/test/SILGen/coroutine_accessors_skip.swift +++ b/test/SILGen/coroutine_accessors_skip.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-emit-silgen \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -experimental-skip-non-inlinable-function-bodies \ // RUN: -enable-library-evolution \ // RUN: -enable-experimental-feature CoroutineAccessors \ @@ -7,6 +8,7 @@ // RUN: %target-swift-emit-silgen \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -experimental-skip-non-inlinable-function-bodies \ // RUN: -enable-library-evolution \ // RUN: -enable-experimental-feature CoroutineAccessors \ diff --git a/test/SILGen/default_override.swift b/test/SILGen/default_override.swift index f2d74d2bf6b..f3db5b64847 100644 --- a/test/SILGen/default_override.swift +++ b/test/SILGen/default_override.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-emit-silgen \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -enable-library-evolution \ // RUN: -enable-experimental-feature BuiltinModule \ // RUN: -enable-experimental-feature CoroutineAccessors \ @@ -7,6 +8,7 @@ // RUN: %target-swift-emit-silgen \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -enable-experimental-feature BuiltinModule \ // RUN: -enable-experimental-feature CoroutineAccessors \ // RUN: | %FileCheck %s --check-prefix=CHECK-FRAGILE diff --git a/test/SILGen/execution_attr.swift b/test/SILGen/execution_attr.swift index a5682dede74..06336c713cf 100644 --- a/test/SILGen/execution_attr.swift +++ b/test/SILGen/execution_attr.swift @@ -1,8 +1,7 @@ -// RUN: %target-swift-emit-silgen %s -enable-experimental-feature ExecutionAttribute | %FileCheck -check-prefix CHECK -check-prefix DISABLED %s -// RUN: %target-swift-emit-silgen %s -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution | %FileCheck -check-prefix CHECK -check-prefix ENABLED %s +// RUN: %target-swift-emit-silgen %s | %FileCheck -check-prefix CHECK -check-prefix DISABLED %s +// RUN: %target-swift-emit-silgen %s -enable-experimental-feature AsyncCallerExecution | %FileCheck -check-prefix CHECK -check-prefix ENABLED %s // REQUIRES: concurrency -// REQUIRES: swift_feature_ExecutionAttribute // REQUIRES: swift_feature_AsyncCallerExecution // Validate that both with and without the experimental flag we properly codegen @@ -11,30 +10,30 @@ // CHECK-LABEL: // executionCaller() // CHECK-NEXT: // Isolation: caller_isolation_inheriting // CHECK-NEXT: sil hidden [ossa] @$s14execution_attr0A6CalleryyYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> () { -@execution(caller) +nonisolated(nonsending) func executionCaller() async {} // CHECK-LABEL: // executionConcurrent() // CHECK: // Isolation: nonisolated // CHECK: sil hidden [ossa] @$s14execution_attr0A10ConcurrentyyYaF : $@convention(thin) @async () -> () { -@execution(concurrent) +@concurrent func executionConcurrent() async {} // DISABLED: sil hidden [ossa] @$s14execution_attr0A15CallerParameteryyyyYaYCXEYaF : $@convention(thin) @async (@guaranteed @noescape @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()) -> () { // ENABLED: sil hidden [ossa] @$s14execution_attr0A15CallerParameteryyyyYaYCXEYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed @noescape @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> ()) -> () { // CHECK: } // end sil function '$s14execution_attr0A15CallerParameteryyyyYaYCXEYaF' -func executionCallerParameter(_ x: @execution(caller) () async -> ()) async { +func executionCallerParameter(_ x: nonisolated(nonsending) () async -> ()) async { await x() } // DISABLED-LABEL: sil hidden [ossa] @$s14execution_attr0A19ConcurrentParameteryyyyYaXEYaF : $@convention(thin) @async (@guaranteed @noescape @async @callee_guaranteed () -> ()) -> () { // ENABLED-LABEL: sil hidden [ossa] @$s14execution_attr0A19ConcurrentParameteryyyyYaXEYaF : $@convention(thin) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional, @guaranteed @noescape @async @callee_guaranteed () -> ()) -> () { -func executionConcurrentParameter(_ x: @execution(concurrent) () async -> ()) async { +func executionConcurrentParameter(_ x: @concurrent () async -> ()) async { await x() } struct S { - let field: @execution(caller) () async -> () + let field: nonisolated(nonsending) () async -> () } // DISABLED: sil hidden [ossa] @$s14execution_attr0A11CallerFieldyyAA1SVYaF : $@convention(thin) @async (@guaranteed S) -> () { @@ -61,5 +60,5 @@ extension S { // CHECK-LABEL: // S.executionCallerFieldMethod(_:) // CHECK: // Isolation: unspecified // CHECK: sil hidden [ossa] @$s14execution_attr1SV0A17CallerFieldMethodyyyyYaYCXEF : $@convention(method) (@guaranteed @noescape @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Optional) -> (), @guaranteed S) -> () { - func executionCallerFieldMethod(_ x: @execution(caller) () async -> ()) {} + func executionCallerFieldMethod(_ x: nonisolated(nonsending) () async -> ()) {} } diff --git a/test/SILGen/read_requirements.swift b/test/SILGen/read_requirements.swift index dee6d8bb788..658eab21078 100644 --- a/test/SILGen/read_requirements.swift +++ b/test/SILGen/read_requirements.swift @@ -1,11 +1,13 @@ -// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ +// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -enable-library-evolution \ // RUN: -enable-experimental-feature CoroutineAccessors \ // RUN: | %FileCheck %s --check-prefixes=CHECK,CHECK-NORMAL,CHECK-%target-abi-stability -// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ +// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types \ // RUN: %s \ +// RUN: -enable-callee-allocated-coro-abi \ // RUN: -enable-library-evolution \ // RUN: -enable-experimental-feature CoroutineAccessors \ // RUN: -enable-experimental-feature CoroutineAccessorsUnwindOnCallerError \ diff --git a/test/SILOptimizer/Inputs/constant_evaluable.swift b/test/SILOptimizer/Inputs/constant_evaluable.swift index d5a7a5d7aa0..a9755a56683 100644 --- a/test/SILOptimizer/Inputs/constant_evaluable.swift +++ b/test/SILOptimizer/Inputs/constant_evaluable.swift @@ -174,7 +174,8 @@ internal func interpretIntTruncations() -> Int8 { internal func testInvalidIntTruncations(a: Int32) -> Int8 { return Int8(a) // CHECK: note: {{.*}}: Not enough bits to represent the passed value - // CHECK: note: operation traps + // CHECK: note: operation performed during this call traps + // CHECK: function_ref @$sSZss17FixedWidthIntegerRzrlEyxqd__cSzRd__lufC } @_semantics("test_driver") @@ -219,7 +220,8 @@ internal func interpretSingedUnsignedConversions() -> UInt32 { internal func testInvalidSingedUnsignedConversions(a: Int64) -> UInt64 { return UInt64(a) // CHECK: note: {{.*}}: Negative value is not representable - // CHECK: note: operation traps + // CHECK: note: operation performed during this call traps + // CHECK: function_ref @$sSUss17FixedWidthIntegerRzrlEyxqd__cSzRd__lufC } @_semantics("test_driver") diff --git a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift index 03dd91cbf35..bd53c23a60c 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift @@ -71,7 +71,8 @@ internal func interpretIntTruncations() -> Int16 { internal func testInvalidIntTruncations(a: Int64) -> Int8 { return Int8(a) // CHECK: note: {{.*}} Not enough bits to represent the passed value - // CHECK: note: operation traps + // CHECK: note: operation performed during this call traps + // CHECK: function_ref @$sSZss17FixedWidthIntegerRzrlEyxqd__cSzRd__lufC } @_semantics("test_driver") diff --git a/test/SILOptimizer/constant_propagation_casts_ossa.sil b/test/SILOptimizer/constant_propagation_casts_ossa.sil index c2ce3a3cfc8..875d0c9c7ab 100644 --- a/test/SILOptimizer/constant_propagation_casts_ossa.sil +++ b/test/SILOptimizer/constant_propagation_casts_ossa.sil @@ -1,6 +1,5 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -diagnostic-constant-propagation -enable-experimental-feature IsolatedConformances | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -diagnostic-constant-propagation | %FileCheck %s -// REQUIRES: swift_feature_IsolatedConformances // REQUIRES: concurrency sil_stage canonical diff --git a/test/SILOptimizer/copy-to-borrow-optimization.sil b/test/SILOptimizer/copy-to-borrow-optimization.sil index ca692c9a7da..3f27a8cf4f2 100644 --- a/test/SILOptimizer/copy-to-borrow-optimization.sil +++ b/test/SILOptimizer/copy-to-borrow-optimization.sil @@ -1,4 +1,5 @@ // RUN: %target-sil-opt -copy-to-borrow-optimization %s | %FileCheck %s +// REQUIRES: macosx sil_stage canonical @@ -2220,3 +2221,42 @@ sil [ossa] @keep_yield2ed_copy : $@convention(thin) () -> () { %retval = tuple () return %retval : $() } + +// CHECK-LABEL: sil [ossa] @borrowed_from_forward1 : {{.*}} { +// CHECK-NOT: copy_value +// CHECK-LABEL: } // end sil function 'borrowed_from_forward1' +sil [ossa] @borrowed_from_forward1 : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + br bb1(%0) + +bb1(%1 : @guaranteed $C): + %2 = borrowed %1 from (%0) + %copy = copy_value %2 + %useC = function_ref @useC : $@convention(thin) (@guaranteed C) -> () + apply %useC(%copy) : $@convention(thin) (@guaranteed C) -> () + destroy_value %copy + %retval = tuple () + return %retval : $() +} + +// CHECK-LABEL: sil [ossa] @borrowed_from_forward2 : {{.*}} { +// CHECK-NOT: copy_value +// CHECK-LABEL: } // end sil function 'borrowed_from_forward2' +sil [ossa] @borrowed_from_forward2 : $@convention(thin) (@guaranteed Array) -> () { +bb0(%0 : @guaranteed $Array): + %1 = struct_extract %0, #Array._buffer + %2 = struct_extract %1, #_ArrayBuffer._storage + %3 = struct_extract %2, #_BridgeStorage.rawValue + %4 = unchecked_ref_cast %3 to $__ContiguousArrayStorageBase + br bb1(%4) + +bb1(%6 : @guaranteed $__ContiguousArrayStorageBase): + %7 = borrowed %6 from (%0) + %8 = copy_value %7 + %9 = begin_borrow %8 + %10 = ref_element_addr [immutable] %9, #__ContiguousArrayStorageBase.countAndCapacity + end_borrow %9 + destroy_value %8 + %13 = tuple () + return %13 +} diff --git a/test/SILOptimizer/dead_code_elimination_ossa.sil b/test/SILOptimizer/dead_code_elimination_ossa.sil index 1c6b5398904..15523cf24fd 100644 --- a/test/SILOptimizer/dead_code_elimination_ossa.sil +++ b/test/SILOptimizer/dead_code_elimination_ossa.sil @@ -15,12 +15,18 @@ class C {} sil @getC : $@convention(thin) () -> @owned C sil @barrier : $@convention(thin) () -> () +sil @initC : $@convention(thin) (Builtin.Word) -> (@owned C, Builtin.RawPointer) +sil [readnone] @finalC : $@convention(thin) (@owned C) -> @owned C struct CAndBit { var c: C var bit: Int1 } +struct Bool { + var _value: Builtin.Int1 +} + struct MO: ~Copyable { var x: Int deinit @@ -507,3 +513,28 @@ bb0(%0 : @owned $Outer): return %6 : $() } +// CHECK-LABEL: sil [ossa] @dont_remove_lifetime_end_of_escaping_value +// CHECK: [[FINALIZE:%[^,]+]] = function_ref @finalC +// CHECK: apply [[FINALIZE]] +// CHECK-LABEL: } // end sil function 'dont_remove_lifetime_end_of_escaping_value' +sil [ossa] @dont_remove_lifetime_end_of_escaping_value : $@convention(thin) (Bool) -> () { +bb0(%0 : $Bool): + %1 = integer_literal $Builtin.Word, 1 + %2 = function_ref @initC : $@convention(thin) (Builtin.Word) -> (@owned C, Builtin.RawPointer) + %3 = apply %2(%1) : $@convention(thin) (Builtin.Word) -> (@owned C, Builtin.RawPointer) + (%4, %5) = destructure_tuple %3 + %6 = mark_dependence %5 on %4 + %7 = pointer_to_address %6 to [strict] $*Bool + store %0 to [trivial] %7 + br bb1 + +bb1: + %13 = function_ref @finalC : $@convention(thin) (@owned C) -> @owned C + %14 = apply %13(%4) : $@convention(thin) (@owned C) -> @owned C + destroy_value %14 + br bb2 + +bb2: + %17 = tuple () + return %17 +} diff --git a/test/SILOptimizer/devirtualize_coroutine_accessors.sil b/test/SILOptimizer/devirtualize_coroutine_accessors.sil index a8ba4bc806a..1e7dfecff94 100644 --- a/test/SILOptimizer/devirtualize_coroutine_accessors.sil +++ b/test/SILOptimizer/devirtualize_coroutine_accessors.sil @@ -1,5 +1,6 @@ // RUN: %target-sil-opt \ // RUN: -devirtualizer \ +// RUN: -enable-callee-allocated-coro-abi\ // RUN: %s \ // RUN: -enable-sil-verify-all \ // RUN: -enable-experimental-feature CoroutineAccessors \ diff --git a/test/SILOptimizer/isolated_conformances.swift b/test/SILOptimizer/isolated_conformances.swift index 68d0af64a90..2c5e66d0942 100644 --- a/test/SILOptimizer/isolated_conformances.swift +++ b/test/SILOptimizer/isolated_conformances.swift @@ -1,14 +1,13 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -parse-as-library -O %s -enable-experimental-feature IsolatedConformances -o %t/a.out +// RUN: %target-build-swift -parse-as-library -O %s -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s --check-prefix=CHECK-OUTPUT -// RUN: %target-build-swift -parse-as-library -O %s -enable-experimental-feature IsolatedConformances -Xllvm -sil-disable-pass=function-signature-opts -emit-sil | %FileCheck %s +// RUN: %target-build-swift -parse-as-library -O %s -Xllvm -sil-disable-pass=function-signature-opts -emit-sil | %FileCheck %s // REQUIRES: concurrency // REQUIRES: executable_test // REQUIRES: concurrency_runtime -// REQUIRES: swift_feature_IsolatedConformances // REQUIRES: OS=macosx || OS=linux-gnu // UNSUPPORTED: back_deployment_runtime diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_scope_fixup.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_scope_fixup.swift index cbcf3ff5789..5e5abdbd7c5 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_scope_fixup.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_scope_fixup.swift @@ -40,27 +40,40 @@ struct View : ~Escapable { } } -struct MutableView : ~Copyable, ~Escapable { - let ptr: UnsafeRawBufferPointer +struct NCMutableContainer : ~Copyable { + let ptr: UnsafeMutableRawBufferPointer let c: Int - @lifetime(borrow ptr) - init(_ ptr: UnsafeRawBufferPointer, _ c: Int) { + init(_ ptr: UnsafeMutableRawBufferPointer, _ c: Int) { self.ptr = ptr self.c = c } - @lifetime(copy otherBV) - init(_ otherBV: borrowing View) { - self.ptr = otherBV.ptr - self.c = otherBV.c +} + +struct MutableView : ~Copyable, ~Escapable { + let ptr: UnsafeMutableRawBufferPointer + @lifetime(borrow ptr) + init(_ ptr: UnsafeMutableRawBufferPointer) { + self.ptr = ptr } - init(_ k: borrowing NCContainer) { + @lifetime(copy otherBV) + init(_ otherBV: borrowing MutableView) { + self.ptr = otherBV.ptr + } + init(_ k: borrowing NCMutableContainer) { self.ptr = k.ptr - self.c = k.c + } +} + +extension MutableView { + @lifetime(&self) + mutating public func update() -> Self { + return unsafe MutableView(ptr) } } func use(_ o : borrowing View) {} func mutate(_ x: inout NCContainer) { } +func mutate(_ x: inout NCMutableContainer) { } func mutate(_ x: inout View) { } func consume(_ o : consuming View) {} func use(_ o : borrowing MutableView) {} @@ -125,9 +138,9 @@ func test3(_ a: Array) { } } -func test4(_ a: Array) { - a.withUnsafeBytes { - var x = NCContainer($0, a.count) +func test4(_ a: inout Array) { + a.withUnsafeMutableBytes { + var x = NCMutableContainer($0, $0.count) mutate(&x) let view = MutableView(x) use(view) @@ -180,11 +193,11 @@ func test7(_ a: UnsafeRawBufferPointer) { mutate(&x) } -func test8(_ a: Array) { - a.withUnsafeBytes { - var x = View($0, a.count) +func test8(_ a: inout Array) { + a.withUnsafeMutableBytes { + var x = View(UnsafeRawBufferPointer(start: $0.baseAddress!, count: $0.count), $0.count) mutate(&x) - let view = MutableView(x) + let view = MutableView($0) use(view) consume(view) } @@ -245,3 +258,29 @@ func testPointeeDependenceOnMutablePointer(p: UnsafePointer) { _ = ptr.pointee _ = ptr } + +// CHECK-LABEL: sil hidden @$s31lifetime_dependence_scope_fixup16testReassignment1bySw_tF : $@convention(thin) (UnsafeMutableRawBufferPointer) -> () { +// CHECK: bb0(%0 : $UnsafeMutableRawBufferPointer): +// CHECK: [[VAR:%.*]] = alloc_stack [lexical] [var_decl] $MutableView, var, name "span", type $MutableView +// CHECK: apply %{{.*}}(%0, %{{.*}}) : $@convention(method) (UnsafeMutableRawBufferPointer, @thin MutableView.Type) -> @lifetime(borrow 0) @owned MutableView +// CHECK: [[ACCESS1:%.*]] = begin_access [modify] [static] [[VAR]] : $*MutableView +// CHECK: apply %{{.*}}(%{{.*}}) : $@convention(method) (@inout MutableView) -> @lifetime(borrow 0) @owned MutableView +// CHECK: [[LD1:%.*]] = load %{{.*}} : $*MutableView +// CHECK: apply %{{.*}}([[LD1]]) : $@convention(thin) (@guaranteed MutableView) -> () +// CHECK: end_access [[ACCESS1]] : $*MutableView +// CHECK: [[ACCESS2:%.*]] = begin_access [modify] [static] [[VAR]] : $*MutableView +// CHECK: apply %{{.*}}(%{{.*}}) : $@convention(method) (@inout MutableView) -> @lifetime(borrow 0) @owned MutableView +// CHECK: [[LD2:%.*]] = load %{{.*}} : $*MutableView +// CHECK: apply %{{.*}}([[LD2]]) : $@convention(thin) (@guaranteed MutableView) -> () +// CHECK: end_access [[ACCESS2]] : $*MutableView +// CHECK: destroy_addr [[VAR]] : $*MutableView +// CHECK-LABEL: } // end sil function '$s31lifetime_dependence_scope_fixup16testReassignment1bySw_tF' +func testReassignment(b: UnsafeMutableRawBufferPointer) { + var span = MutableView(b) + + var sub = span.update() + use(sub) + + sub = span.update() + use(sub) +} diff --git a/test/SILOptimizer/lifetime_dependence/local_var_util.sil b/test/SILOptimizer/lifetime_dependence/local_var_util.sil new file mode 100644 index 00000000000..3215b3770ad --- /dev/null +++ b/test/SILOptimizer/lifetime_dependence/local_var_util.sil @@ -0,0 +1,134 @@ +// RUN: %target-sil-opt -test-runner %s \ +// RUN: -enable-experimental-feature LifetimeDependence \ +// RUN: -o /dev/null 2>&1 | %FileCheck %s + +// REQUIRES: swift_in_compiler +// REQUIRES: swift_feature_LifetimeDependence + +sil_stage raw + +import Builtin +import Swift +import SwiftShims + +struct NE: ~Escapable {} + +sil @makeDepNE : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE + +// CHECK-LABEL: testNEInitNoEscape: local_variable_reachable_uses with: %1, @instruction +// CHECK: ### Access map: +// CHECK-NEXT: Access map for: %{{.*}} = alloc_box ${ var NE }, var, name "self" +// CHECK-NEXT: assign: true, store destroy_value %{{.*}} : ${ var NE } +// CHECK-NEXT: assign: false, escape %{{.*}} = address_to_pointer %{{.*}} : $*NE to $Builtin.RawPointer +// CHECK-NEXT: assign: true, beginAccess %{{.*}} = begin_access [modify] [unknown] %{{.*}} : $*NE +// CHECK-NEXT: assign: false, load %{{.*}} = load [copy] %{{.*}} : $*NE +// CHECK-NEXT: assign: true, beginAccess %{{.*}} = begin_access [modify] [unknown] %{{.*}} : $*NE +// CHECK-NEXT: ### Modify: %{{.*}} = begin_access [modify] [unknown] %4 : $*NE +// CHECK-NEXT: ### Reachable access: +// CHECK-NEXT: load %{{.*}} = load [copy] %{{.*}} : $*NE +// CHECK-NEXT: testNEInitNoEscape: local_variable_reachable_uses with: %1, @instruction + +// CHECK-LABEL: testNEInitNoEscape: local_variable_reaching_assignments with: %1, @instruction +// CHECK: ### Instruction: end_access %{{.*}} : $*NE +// CHECK-NEXT: ### Reachable assignments: +// CHECK-NEXT: beginAccess %21 = begin_access [modify] [unknown] %4 : $*NE +// CHECK-NEXT: testNEInitNoEscape: local_variable_reaching_assignments with: %1, @instruction +sil [ossa] @testNEInitNoEscape : $@convention(thin) (@inout NE) -> @lifetime(borrow 0) @owned NE { +bb0(%0 : $*NE): + %1 = alloc_box ${ var NE }, var, name "self" + %2 = mark_uninitialized [rootself] %1 + %3 = begin_borrow [lexical] [var_decl] %2 + %4 = project_box %3, 0 + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + br bb3 + +bb3: + %8 = alloc_stack $NE + + %9 = begin_access [modify] [unknown] %0 + + %10 = function_ref @makeDepNE : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE + %11 = apply %10(%8, %9) : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE + + mark_dependence_addr [unresolved] %8 on %9 + end_access %9 + + %14 = load [take] %8 + + specify_test "local_variable_reachable_uses %1 @instruction" + %15 = begin_access [modify] [unknown] %4 + assign %14 to %15 + end_access %15 + dealloc_stack %8 + %19 = load [copy] %4 + + %21 = load [take] %8 + %22 = begin_access [modify] [unknown] %4 + assign %21 to %22 + specify_test "local_variable_reaching_assignments %1 @instruction" + end_access %22 + %escape = address_to_pointer %4 : $*NE to $Builtin.RawPointer + end_borrow %3 + destroy_value %2 + return %19 +} + +// CHECK-LABEL: testNEInitEscape: local_variable_reaching_assignments with: %1, @instruction +// CHECK: ### Instruction: %{{.*}} = load [take] %{{.*}} : $*NE +// CHECK-NEXT: !!! Reaching escape +// CHECK-NEXT: testNEInitEscape: local_variable_reaching_assignments with: %1, @instruction + +// CHECK-LABEL: testNEInitEscape: local_variable_reachable_uses with: %1, @instruction +// CHECK: ### Access map: +// CHECK-NEXT: Access map for: %{{.*}} = alloc_box ${ var NE }, var, name "self" +// CHECK-NEXT: assign: true, store destroy_value %{{.*}} : ${ var NE } +// CHECK-NEXT: assign: false, load %{{.*}} = load [copy] %{{.*}} : $*NE +// CHECK-NEXT: assign: true, beginAccess %{{.*}} = begin_access [modify] [unknown] %{{.*}} : $*NE +// CHECK-NEXT: assign: false, escape %6 = address_to_pointer %4 : $*NE to $Builtin.RawPointer +// CHECK-NEXT: ### Modify: %{{.*}} = begin_access [modify] [unknown] %4 : $*NE +// CHECK-NEXT: !!! Reachable escape +// CHECK-NEXT: testNEInitEscape: local_variable_reachable_uses with: %1, @instruction +sil [ossa] @testNEInitEscape : $@convention(thin) (@inout NE) -> @lifetime(borrow 0) @owned NE { +bb0(%0 : $*NE): + %1 = alloc_box ${ var NE }, var, name "self" + %2 = mark_uninitialized [rootself] %1 + %3 = begin_borrow [lexical] [var_decl] %2 + %4 = project_box %3, 0 + cond_br undef, bb1, bb2 + +bb1: + %escape = address_to_pointer %4 : $*NE to $Builtin.RawPointer + br bb3 + +bb2: + br bb3 + +bb3: + %8 = alloc_stack $NE + + %9 = begin_access [modify] [unknown] %0 + + %10 = function_ref @makeDepNE : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE + %11 = apply %10(%8, %9) : $@convention(thin) (@inout NE) -> @lifetime(borrow address_for_deps 0) @out NE + + mark_dependence_addr [unresolved] %8 on %9 + end_access %9 + + specify_test "local_variable_reaching_assignments %1 @instruction" + %14 = load [take] %8 + + specify_test "local_variable_reachable_uses %1 @instruction" + %15 = begin_access [modify] [unknown] %4 + assign %14 to %15 + end_access %15 + dealloc_stack %8 + %19 = load [copy] %4 + end_borrow %3 + destroy_value %2 + return %19 +} diff --git a/test/SILOptimizer/lifetime_dependence/scope_fixup.sil b/test/SILOptimizer/lifetime_dependence/scope_fixup.sil index 4637f0fe8de..c12d872fe8a 100644 --- a/test/SILOptimizer/lifetime_dependence/scope_fixup.sil +++ b/test/SILOptimizer/lifetime_dependence/scope_fixup.sil @@ -43,6 +43,10 @@ struct TrivialHolder { var pointer: UnsafeRawPointer } +struct A { + init() +} + struct Holder { var object: AnyObject } @@ -59,6 +63,9 @@ sil [ossa] @Wrapper_init : $@convention(method) (@owned NE, @thin Wrapper.Type) sil [ossa] @NCContainer_ne_read : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed NE +sil @yieldRawSpan : $@yield_once @convention(method) (@in_guaranteed A) -> @lifetime(borrow address_for_deps 0) @yields @guaranteed RawSpan +sil @useRawSpan : $@convention(thin) (@guaranteed RawSpan) -> () + // NCContainer.wrapper._read: // var wrapper: Wrapper { // _read { @@ -237,3 +244,35 @@ bb0(%0 : @guaranteed $Holder): %99 = tuple () return %99 : $() } + +// FIXME: extend temporary lifetimes for coroutine access. +// +// CHECK-LABEL: sil [ossa] @testYieldSpan : $@convention(thin) (@in A) -> () { +// CHECK: alloc_stack [lexical] [var_decl] $A +// CHECK: (%{{.*}}, %{{.*}}) = begin_apply %{{.*}}(%{{.*}}) : $@yield_once @convention(method) (@in_guaranteed A) -> @lifetime(borrow address_for_deps 0) @yields @guaranteed RawSpan +// CHECK: [[MD:%.*]] = mark_dependence [unresolved] +// CHECK: [[CP:%.*]] = copy_value [[MD]] +// CHECK: apply %{{.*}}([[CP]]) : $@convention(thin) (@guaranteed RawSpan) -> () +// CHECK: destroy_value [[CP]] +// CHECK: end_apply +// CHECK: destroy_addr +// CHECK-LABEL: } // end sil function 'testYieldSpan' +sil [ossa] @testYieldSpan : $@convention(thin) (@in A) -> () { +bb0(%0 : $*A): + %1 = alloc_stack [lexical] [var_decl] $A + copy_addr [take] %0 to [init] %1 + + %3 = function_ref @yieldRawSpan : $@yield_once @convention(method) (@in_guaranteed A) -> @lifetime(borrow address_for_deps 0) @yields @guaranteed RawSpan + (%4, %5) = begin_apply %3(%1) : $@yield_once @convention(method) (@in_guaranteed A) -> @lifetime(borrow address_for_deps 0) @yields @guaranteed RawSpan + %6 = mark_dependence [unresolved] %4 on %5 + %7 = copy_value %6 + %8 = end_apply %5 as $() + + %9 = function_ref @useRawSpan : $@convention(thin) (@guaranteed RawSpan) -> () + %10 = apply %9(%7) : $@convention(thin) (@guaranteed RawSpan) -> () + destroy_value %7 + destroy_addr %1 + dealloc_stack %1 + %14 = tuple () + return %14 +} diff --git a/test/SILOptimizer/lifetime_dependence/specialize.sil b/test/SILOptimizer/lifetime_dependence/specialize.sil index 8574cfed4b7..16374f39d06 100644 --- a/test/SILOptimizer/lifetime_dependence/specialize.sil +++ b/test/SILOptimizer/lifetime_dependence/specialize.sil @@ -11,6 +11,8 @@ // REQUIRES: swift_feature_AddressableParameters // REQUIRES: swift_feature_AddressableTypes +// REQUIRES: PTRSIZE=64 + // Test the SIL representation for lifetime dependence scope fixup. sil_stage raw diff --git a/test/SILOptimizer/optimize_copy_block.swift b/test/SILOptimizer/optimize_copy_block.swift new file mode 100644 index 00000000000..59ea1a51ac9 --- /dev/null +++ b/test/SILOptimizer/optimize_copy_block.swift @@ -0,0 +1,39 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %target-swift-frontend -I %t %t/test.swift -O -emit-sil | %FileCheck %s + +// REQUIRES: objc_interop + +//--- module.modulemap + +module CModule { + header "c-header.h" + export * +} + + +//--- c-header.h + +@import Foundation; + +@interface TestClass : NSObject +- (void)callHandlerInline: (NS_NOESCAPE _Nonnull dispatch_block_t)block; +@end + + +//--- test.swift + +import CModule + +@objc @implementation +extension TestClass { + // CHECK-LABEL: sil private [thunk] @$sSo9TestClassC4testE17callHandlerInlineyyyyXEFTo : + // CHECK-NOT: copy_block + // CHECK: apply %0 + // CHECK-NOT: destroy_value + // CHECK: } // end sil function '$sSo9TestClassC4testE17callHandlerInlineyyyyXEFTo' + func callHandlerInline(_ handler: () -> Void) { + handler() + } +} + diff --git a/test/SILOptimizer/simplify_copy_block.sil b/test/SILOptimizer/simplify_copy_block.sil new file mode 100644 index 00000000000..9111c091217 --- /dev/null +++ b/test/SILOptimizer/simplify_copy_block.sil @@ -0,0 +1,59 @@ +// RUN: %target-sil-opt %s -simplification -simplify-instruction=copy_block | %FileCheck %s + +// REQUIRES: objc_interop + +sil_stage canonical + +import Swift +import SwiftShims +import Builtin + +// CHECK-LABEL: sil [ossa] @remove_copy_block : +// CHECK-NOT: copy_block +// CHECK: apply %0 +// CHECK-NOT: destroy_value +// CHECK: } // end sil function 'remove_copy_block' +sil [ossa] @remove_copy_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> () { +bb0(%0 : @unowned $@convention(block) @noescape () -> ()): + %2 = copy_block %0 + %3 = begin_borrow [lexical] %2 + %4 = apply %3() : $@convention(block) @noescape () -> () + end_borrow %3 + destroy_value %2 + %7 = tuple () + return %7 +} + +// CHECK-LABEL: sil [ossa] @dont_remove_copied_block : +// CHECK: copy_block +// CHECK: } // end sil function 'dont_remove_copied_block' +sil [ossa] @dont_remove_copied_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> () { +bb0(%0 : @unowned $@convention(block) @noescape () -> ()): + %2 = copy_block %0 + %3 = begin_borrow [lexical] %2 + %4 = apply %3() : $@convention(block) @noescape () -> () + %5 = copy_value %3 + fix_lifetime %5 + destroy_value %5 + end_borrow %3 + destroy_value %2 + %7 = tuple () + return %7 +} + +sil [ossa] @use_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> () + +// CHECK-LABEL: sil [ossa] @dont_remove_escaping_block : +// CHECK: copy_block +// CHECK: } // end sil function 'dont_remove_escaping_block' +sil [ossa] @dont_remove_escaping_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> () { +bb0(%0 : @unowned $@convention(block) @noescape () -> ()): + %2 = copy_block %0 + %3 = begin_borrow [lexical] %2 + %4 = function_ref @use_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> () + %5 = apply %4(%3) : $@convention(thin) (@convention(block) @noescape () -> ()) -> () + end_borrow %3 + destroy_value %2 + %7 = tuple () + return %7 +} diff --git a/test/SILOptimizer/simplify_unconditional_check_cast.sil b/test/SILOptimizer/simplify_unconditional_check_cast.sil index 21134ec91a0..1b667b2e60d 100644 --- a/test/SILOptimizer/simplify_unconditional_check_cast.sil +++ b/test/SILOptimizer/simplify_unconditional_check_cast.sil @@ -1,6 +1,5 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -simplification -simplify-instruction=unconditional_checked_cast -enable-experimental-feature IsolatedConformances | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplification -simplify-instruction=unconditional_checked_cast | %FileCheck %s -// REQUIRES: swift_feature_IsolatedConformances // REQUIRES: concurrency import Swift diff --git a/test/ScanDependencies/serialized_imports.swift b/test/ScanDependencies/serialized_imports.swift new file mode 100644 index 00000000000..1b62894eb60 --- /dev/null +++ b/test/ScanDependencies/serialized_imports.swift @@ -0,0 +1,29 @@ +// REQUIRES: objc_interop +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/module-cache) + +// Run the scanner once, emitting the serialized scanner cache +// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -Rdependency-scan-cache -serialize-dependency-scan-cache -load-dependency-scan-cache -dependency-scan-cache-path %t/cache.moddepcache -module-cache-path %t/module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -enable-cross-import-overlays 2>&1 | %FileCheck %s -check-prefix CHECK-REMARK-SAVE +// RUN: llvm-bcanalyzer --dump %t/cache.moddepcache > %t/cache.moddepcache.initial.dump.txt + +// Run the scanner again, but now re-using previously-serialized cache +// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -Rdependency-scan-cache -serialize-dependency-scan-cache -load-dependency-scan-cache -dependency-scan-cache-path %t/cache.moddepcache -module-cache-path %t/module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -enable-cross-import-overlays 2>&1 | %FileCheck %s -check-prefix CHECK-REMARK-LOAD +// RUN: llvm-bcanalyzer --dump %t/cache.moddepcache > %t/cache.moddepcache.dump.txt + +// Ensure that the initial scan, and the secondary scan which just re-used the initial scan's results report +// the same number of import statement nodes, ensuring that serialization-deserialization did not affect +// the import sets +// +// RUN: grep "IMPORT_STATEMENT_NODE" %t/cache.moddepcache.initial.dump.txt | wc -l > %t/initial_scan_import_count.txt +// RUN: grep "IMPORT_STATEMENT_NODE" %t/cache.moddepcache.dump.txt | wc -l > %t/secondary_scan_import_count.txt +// RUN: diff %t/initial_scan_import_count.txt %t/secondary_scan_import_count.txt + +// CHECK-REMARK-SAVE: remark: Incremental module scan: Failed to load module scanning dependency cache from: +// CHECK-REMARK-SAVE: remark: Incremental module scan: Serializing module scanning dependency cache to: +// CHECK-REMARK-LOAD: remark: Incremental module scan: Re-using serialized module scanning dependency cache from: + +import E + + + + diff --git a/test/Sema/availability_scopes.swift b/test/Sema/availability_scopes.swift index b3a3ea56964..8042beeb145 100644 --- a/test/Sema/availability_scopes.swift +++ b/test/Sema/availability_scopes.swift @@ -253,19 +253,19 @@ extension SomeClass { // CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeClass // CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=extension.SomeClass -// CHECK-NEXT: {{^}} (decl version=51 unavailable=macOS decl=functionWithStmtConditionsInUnavailableExt() +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=functionWithStmtConditionsInUnavailableExt() // CHECK-NEXT: {{^}} (condition_following_availability version=52 unavailable=macOS // CHECK-NEXT: {{^}} (condition_following_availability version=53 unavailable=macOS // CHECK-NEXT: {{^}} (if_then version=53 unavailable=macOS // CHECK-NEXT: {{^}} (condition_following_availability version=54 unavailable=macOS // CHECK-NEXT: {{^}} (if_then version=54 unavailable=macOS // CHECK-NEXT: {{^}} (condition_following_availability version=55 unavailable=macOS -// CHECK-NEXT: {{^}} (decl version=55 unavailable=macOS decl=funcInGuardElse() +// CHECK-NEXT: {{^}} (decl version=54 unavailable=macOS decl=funcInGuardElse() // CHECK-NEXT: {{^}} (guard_fallthrough version=55 unavailable=macOS // CHECK-NEXT: {{^}} (condition_following_availability version=56 unavailable=macOS // CHECK-NEXT: {{^}} (guard_fallthrough version=56 unavailable=macOS -// CHECK-NEXT: {{^}} (decl version=57 unavailable=macOS decl=funcInInnerIfElse() -// CHECK-NEXT: {{^}} (decl version=53 unavailable=macOS decl=funcInOuterIfElse() +// CHECK-NEXT: {{^}} (decl version=53 unavailable=macOS decl=funcInInnerIfElse() +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=funcInOuterIfElse() @available(OSX, unavailable) extension SomeClass { @available(OSX 51, *) @@ -401,7 +401,7 @@ extension SomeEnum { // CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeEnum // CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=extension.SomeEnum // CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=availableMacOS_52 -// CHECK-NEXT: {{^}} (decl version=52 unavailable=macOS decl=availableMacOS_52 +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=availableMacOS_52 // CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=neverAvailable() @available(macOS, unavailable) @@ -418,10 +418,25 @@ extension SomeEnum { // CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOSAndIntroduced() -@available(macOS, unavailable, introduced: 52) +@available(macOS, unavailable) +@available(macOS, introduced: 52) func unavailableOnMacOSAndIntroduced() { } +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=introducedOnMacOSAndUnavailable() + +@available(macOS, introduced: 53) +@available(macOS, unavailable) +func introducedOnMacOSAndUnavailable() { +} + + +// CHECK-NEXT: {{^}} (decl version=50 unavailable=macOS decl=unavailableOnMacOSAndIntroducedSameAttr() + +@available(macOS, unavailable, introduced: 54) +func unavailableOnMacOSAndIntroducedSameAttr() { +} + // CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=NeverAvailable // CHECK-NEXT: {{^}} (decl version=50 unavailable=* decl=unavailableOnMacOS() diff --git a/test/Sema/fixits-derived-conformances.swift b/test/Sema/fixits-derived-conformances.swift index 1a748b2949c..ae47347f770 100644 --- a/test/Sema/fixits-derived-conformances.swift +++ b/test/Sema/fixits-derived-conformances.swift @@ -17,5 +17,5 @@ extension GenericStruct: @retroactive Equatable { } extension Enum: @retroactive CaseIterable { } // expected-error@-1 {{extension outside of file declaring enum 'Enum' prevents automatic synthesis of 'allCases' for protocol 'CaseIterable'}} -// expected-note@-2 {{add stubs for conformance}}{{44-44=\n public static var allCases: [Enum]\n}} +// expected-note@-2 {{add stubs for conformance}}{{44-44=\n public static let allCases: [Enum]\n}} diff --git a/test/Sema/property_wrapper_availability.swift b/test/Sema/property_wrapper_availability.swift index daf3ab27713..030be36469e 100644 --- a/test/Sema/property_wrapper_availability.swift +++ b/test/Sema/property_wrapper_availability.swift @@ -99,7 +99,7 @@ struct UnavailableStruct { @UnavailableWrapper var unavailableInferred = S() @WrappedValueUnavailableOnMacOS var unavailableWrappedValue: S - @WrappedValueAvailable51 var wrappedValueAavailable51: S // expected-error {{'wrappedValue' is only available in macOS 51 or newer}} + @WrappedValueAvailable51 var wrappedValueAavailable51: S } @available(macOS, unavailable) @@ -117,7 +117,7 @@ struct UnavailableOnMacOSStruct { @UnavailableWrapper var unavailableInferred = S() @WrappedValueUnavailableOnMacOS var unavailableWrappedValue: S - @WrappedValueAvailable51 var wrappedValueAavailable51: S // expected-error {{'wrappedValue' is only available in macOS 51 or newer}} + @WrappedValueAvailable51 var wrappedValueAavailable51: S } func alwaysAvailableFunc( // expected-note 4 {{add @available attribute to enclosing global function}} @@ -160,14 +160,14 @@ func unavailableFunc( @DeprecatedWrapper _ deprecated: S, @UnavailableWrapper _ unavailable: S, @WrappedValueUnavailableOnMacOS _ unavailableWrappedValue: S, - @WrappedValueAvailable51 _ wrappedValueAavailable51: S // expected-error {{'wrappedValue' is only available in macOS 51 or newer}} + @WrappedValueAvailable51 _ wrappedValueAavailable51: S ) { @AlwaysAvailableWrapper var alwaysAvailableLocal = S() @Available51Wrapper var available51Local = S() @DeprecatedWrapper var deprecatedLocal = S() @UnavailableWrapper var unavailableLocal = S() @WrappedValueUnavailableOnMacOS var unavailableWrappedValueLocal = S() - @WrappedValueAvailable51 var wrappedValueAavailable51 = S() // expected-error {{'wrappedValue' is only available in macOS 51 or newer}} + @WrappedValueAvailable51 var wrappedValueAavailable51 = S() } @available(macOS, unavailable) @@ -177,12 +177,12 @@ func unavailableOnMacOSFunc( @DeprecatedWrapper _ deprecated: S, @UnavailableWrapper _ unavailable: S, @WrappedValueUnavailableOnMacOS _ unavailableWrappedValue: S, - @WrappedValueAvailable51 _ wrappedValueAavailable51: S // expected-error {{'wrappedValue' is only available in macOS 51 or newer}} + @WrappedValueAvailable51 _ wrappedValueAavailable51: S ) { @AlwaysAvailableWrapper var alwaysAvailableLocal = S() @Available51Wrapper var available51Local = S() @DeprecatedWrapper var deprecatedLocal = S() @UnavailableWrapper var unavailableLocal = S() @WrappedValueUnavailableOnMacOS var unavailableWrappedValueLocal = S() - @WrappedValueAvailable51 var wrappedValueAavailable51 = S() // expected-error {{'wrappedValue' is only available in macOS 51 or newer}} + @WrappedValueAvailable51 var wrappedValueAavailable51 = S() } diff --git a/test/Sema/value_generics.swift b/test/Sema/value_generics.swift index 0f5d38b9dce..38d17ef39fc 100644 --- a/test/Sema/value_generics.swift +++ b/test/Sema/value_generics.swift @@ -119,3 +119,71 @@ func testC4(with c: C) { } struct D {} // expected-error {{non-protocol, non-class type 'Int' cannot be used within a protocol-constrained type}} + +struct E { // expected-note {{'b' previously declared here}} + static var b: Int { // expected-error {{invalid redeclaration of 'b'}} + // expected-note@-1 {{'b' declared here}} + 123 + } + + let b: String // expected-note {{'b' previously declared here}} + // expected-note@-1 {{'b' declared here}} + + func b() {} // expected-error {{invalid redeclaration of 'b()'}} + // expected-note@-1 {{'b' declared here}} + + func dumb() -> Int { + Self.b // OK + } + + static func dumb2() -> Int { + Self.b // OK + } +} + +func testE1() -> Int { + E.b // OK +} + +func testE2() -> Int { + E.A // expected-error {{type 'E' has no member 'A'}} +} + +func testE3(_: E.Type) -> Int { + E.b // OK +} + +func testShadowing(_: A) { + var a: String { + "123" + } + + let x: String = a // OK + let y: Int = a // expected-error {{cannot convert value of type 'String' to specified type 'Int'}} + + print(a) // OK + print(a.self) // OK +} + +class F {} +class G: F<3> {} + +func hello() -> Int { + G.n // OK +} + +func testTypeOf() -> Int { + let x = E(b: "") + return type(of: x).b // OK +} + +func testTypeOf2(_: E.Type) -> Int { + let x = E(b: "") + return type(of: x).b // OK +} + +struct H { // expected-note {{'I' previously declared here}} + struct I {} // expected-error {{invalid redeclaration of 'I'}} +} + +typealias J = E.b // expected-error {{static property 'b' is not a member type of 'E'}} diff --git a/test/Serialization/Inputs/caller_inheriting_isolation.swift b/test/Serialization/Inputs/caller_inheriting_isolation.swift index cd2bd31fc92..b249da80dbc 100644 --- a/test/Serialization/Inputs/caller_inheriting_isolation.swift +++ b/test/Serialization/Inputs/caller_inheriting_isolation.swift @@ -2,22 +2,22 @@ public func unspecifiedAsync(_ t: T) async { } -@execution(caller) +nonisolated(nonsending) public func unspecifiedAsyncCaller(_ t: T) async { } -@execution(concurrent) +@concurrent public func unspecifiedAsyncConcurrent(_ t: T) async { } nonisolated public func nonisolatedAsync(_ t: T) async { } -@execution(caller) -nonisolated public func nonisolatedAsyncCaller(_ t: T) async { +nonisolated(nonsending) +public func nonisolatedAsyncCaller(_ t: T) async { } -@execution(concurrent) +@concurrent nonisolated public func nonisolatedAsyncConcurrent(_ t: T) async { } @@ -26,22 +26,22 @@ public struct S { public func unspecifiedAsync(_ t: T) async { } - @execution(caller) + nonisolated(nonsending) public func unspecifiedAsyncCaller(_ t: T) async { } - @execution(concurrent) + @concurrent public func unspecifiedAsyncConcurrent(_ t: T) async { } nonisolated public func nonisolatedAsync(_ t: T) async { } - @execution(caller) - nonisolated public func nonisolatedAsyncCaller(_ t: T) async { + nonisolated(nonsending) + public func nonisolatedAsyncCaller(_ t: T) async { } - @execution(concurrent) + @concurrent nonisolated public func nonisolatedAsyncConcurrent(_ t: T) async { } } diff --git a/test/Serialization/Inputs/def_isolated_conformance.swift b/test/Serialization/Inputs/def_isolated_conformance.swift index a339a12a777..f908f1eac16 100644 --- a/test/Serialization/Inputs/def_isolated_conformance.swift +++ b/test/Serialization/Inputs/def_isolated_conformance.swift @@ -8,3 +8,8 @@ public class MyClass { } extension MyClass: @MainActor MyProtocol { @MainActor public func f() { } } + +public protocol OtherProtocol { +} + +extension MyClass: OtherProtocol { } diff --git a/test/Serialization/caller_isolation_inherit.swift b/test/Serialization/caller_isolation_inherit.swift index d58f749dd0b..58612b16901 100644 --- a/test/Serialization/caller_isolation_inherit.swift +++ b/test/Serialization/caller_isolation_inherit.swift @@ -1,13 +1,12 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution -emit-module-path %t/WithFeature.swiftmodule -module-name WithFeature %S/Inputs/caller_inheriting_isolation.swift -swift-version 6 -// RUN: %target-swift-frontend -enable-experimental-feature ExecutionAttribute -emit-module-path %t/WithoutFeature.swiftmodule -module-name WithoutFeature %S/Inputs/caller_inheriting_isolation.swift -swift-version 6 +// RUN: %target-swift-frontend -enable-experimental-feature AsyncCallerExecution -emit-module-path %t/WithFeature.swiftmodule -module-name WithFeature %S/Inputs/caller_inheriting_isolation.swift -swift-version 6 +// RUN: %target-swift-frontend -emit-module-path %t/WithoutFeature.swiftmodule -module-name WithoutFeature %S/Inputs/caller_inheriting_isolation.swift -swift-version 6 // RUN: %target-swift-frontend -module-name main -I %t %s -emit-sil -o - | %FileCheck %s -// RUN: %target-swift-frontend -enable-experimental-feature ExecutionAttribute -enable-experimental-feature AsyncCallerExecution -module-name main -I %t %s -emit-sil -verify -swift-version 6 +// RUN: %target-swift-frontend -enable-experimental-feature AsyncCallerExecution -module-name main -I %t %s -emit-sil -verify -swift-version 6 // REQUIRES: asserts -// REQUIRES: swift_feature_ExecutionAttribute // REQUIRES: swift_feature_AsyncCallerExecution import WithFeature diff --git a/test/Serialization/isolated_conformance.swift b/test/Serialization/isolated_conformance.swift index beeb48aa106..d7e7701313c 100644 --- a/test/Serialization/isolated_conformance.swift +++ b/test/Serialization/isolated_conformance.swift @@ -1,16 +1,18 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -target %target-swift-5.1-abi-triple -swift-version 6 -enable-experimental-feature IsolatedConformances -o %t/def_isolated_conformance.swiftmodule %S/Inputs/def_isolated_conformance.swift +// RUN: %target-swift-frontend -emit-module -target %target-swift-5.1-abi-triple -swift-version 6 -o %t/def_isolated_conformance.swiftmodule %S/Inputs/def_isolated_conformance.swift -default-isolation=MainActor -// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 -enable-experimental-feature IsolatedConformances %s -I %t +// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 %s -I %t -// REQUIRES: swift_feature_IsolatedConformances // REQUIRES: concurrency import def_isolated_conformance func acceptMyProtocol(_: some MyProtocol) { } +func acceptOtherProtocol(_: some MyProtocol) { } nonisolated func f(mc: MyClass) { acceptMyProtocol(mc) // expected-error@-1{{main actor-isolated conformance of 'MyClass' to 'MyProtocol' cannot be used in nonisolated context}} + acceptOtherProtocol(mc) + // expected-error@-1{{main actor-isolated conformance of 'MyClass' to 'MyProtocol' cannot be used in nonisolated context}} } diff --git a/test/SourceKit/InterfaceGen/Inputs/module.modulemap b/test/SourceKit/InterfaceGen/Inputs/module.modulemap new file mode 100644 index 00000000000..2db9ba676aa --- /dev/null +++ b/test/SourceKit/InterfaceGen/Inputs/module.modulemap @@ -0,0 +1,4 @@ +module UsesCXX20 { + header "uses-cxx20.h" + export * +} diff --git a/test/SourceKit/InterfaceGen/Inputs/uses-cxx20.h b/test/SourceKit/InterfaceGen/Inputs/uses-cxx20.h new file mode 100644 index 00000000000..9370c7270cc --- /dev/null +++ b/test/SourceKit/InterfaceGen/Inputs/uses-cxx20.h @@ -0,0 +1,3 @@ +#include + +using MySpanOfInt = std::span; diff --git a/test/SourceKit/InterfaceGen/gen_clang_cxx_module_with_cxx20.swift b/test/SourceKit/InterfaceGen/gen_clang_cxx_module_with_cxx20.swift new file mode 100644 index 00000000000..dc0758966df --- /dev/null +++ b/test/SourceKit/InterfaceGen/gen_clang_cxx_module_with_cxx20.swift @@ -0,0 +1,10 @@ +// First, check that we error out for older C++ standards. +// RUN: not %sourcekitd-test -req=interface-gen -module UsesCXX20 -- -cxx-interoperability-mode=default -Xfrontend -disable-implicit-concurrency-module-import -Xfrontend -disable-implicit-string-processing-module-import -I %S/Inputs -target %target-triple -sdk %sdk + +// Now make sure we emit the correct interface for std::span if C++20 is enabled. +// RUN: %sourcekitd-test -req=interface-gen -module UsesCXX20 -- -cxx-interoperability-mode=default -Xfrontend -disable-implicit-concurrency-module-import -Xfrontend -disable-implicit-string-processing-module-import -I %S/Inputs -target %target-triple -sdk %sdk -Xcc -std=c++20 | %FileCheck %s + +// FIXME: older libstdc++ version on Swift CI does not support C++20 +// UNSUPPORTED: OS=linux-gnu + +// CHECK: public typealias MySpanOfInt = std.span< diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/AvailabilityFilter.swift b/test/SymbolGraph/Symbols/Mixins/Availability/AvailabilityFilter.swift new file mode 100644 index 00000000000..d09bca0c8ff --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/AvailabilityFilter.swift @@ -0,0 +1,63 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix DEFAULT + +// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix DEFAULT + +// Now checking the allowlist behavior... + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -symbol-graph-allow-availability-platforms macOS,iOS +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix ALLOWLIST + +// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t -allow-availability-platforms macOS,iOS +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix ALLOWLIST + +// Now checking the blocklist behavior... + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -symbol-graph-block-availability-platforms macOS,iOS +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix BLOCKLIST + +// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t -block-availability-platforms macOS,iOS +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix BLOCKLIST + +// Now test to ensure an empty allow list filters out all availability... + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -symbol-graph-allow-availability-platforms "" +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix EMPTY + +// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t -allow-availability-platforms "" +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix EMPTY + +// REQUIRES: OS=macosx + +@available(macOS 11.0, iOS 15.0, watchOS 15.0, *) +public struct S {} + +/// Ensure that regardless of platforms being removed, that universal availability info, +/// like unconditional deprecation, still lands in the symbol graph. +@available(*, deprecated) +public class C {} + +// DEFAULT-DAG: macOS +// DEFAULT-DAG: iOS +// DEFAULT-DAG: watchOS +// DEFAULT-DAG: "isUnconditionallyDeprecated":{{ ?}}true + +// ALLOWLIST-NOT: watchOS +// ALLOWLIST-DAG: macOS +// ALLOWLIST-DAG: iOS +// ALLOWLIST-DAG: "isUnconditionallyDeprecated":{{ ?}}true + +// BLOCKLIST-NOT: macOS +// BLOCKLIST-NOT: iOS +// BLOCKLIST-DAG: watchOS +// BLOCKLIST-DAG: "isUnconditionallyDeprecated":{{ ?}}true + +// EMPTY-NOT: macOS +// EMPTY-NOT: iOS +// EMPTY-NOT: watchOS +// EMPTY-DAG: "isUnconditionallyDeprecated":{{ ?}}true diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Full/Function.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Full/Function.swift index 2363c1d7ded..2c6fdfcf6ba 100644 --- a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Full/Function.swift +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Full/Function.swift @@ -41,12 +41,12 @@ public func foo(f: @escaping () -> (), ext int: Int = 2, s: S) where S: Seque // FOO-NEXT: "spelling": ": " // FOO-NEXT: }, // FOO-NEXT: { -// FOO-NEXT: "kind": "keyword", -// FOO-NEXT: "spelling": "@escaping" +// FOO-NEXT: "kind": "attribute", +// FOO-NEXT: "spelling": "@escaping " // FOO-NEXT: }, // FOO-NEXT: { // FOO-NEXT: "kind": "text", -// FOO-NEXT: "spelling": " () -> (), " +// FOO-NEXT: "spelling": "() -> (), " // FOO-NEXT: }, // FOO-NEXT: { // FOO-NEXT: "kind": "externalParam", diff --git a/test/TBD/coroutine_accessors.swift b/test/TBD/coroutine_accessors.swift index cc2270cc0c3..2f557b2adb8 100644 --- a/test/TBD/coroutine_accessors.swift +++ b/test/TBD/coroutine_accessors.swift @@ -1,5 +1,6 @@ // RUN: %target-build-swift-dylib(%t/%target-library-name(thing)) \ // RUN: %s \ +// RUN: -Xfrontend -enable-callee-allocated-coro-abi \ // RUN: -emit-tbd \ // RUN: -Xfrontend -validate-tbd-against-ir=all \ // RUN: -enable-library-evolution \ diff --git a/test/abi/macOS/arm64/concurrency.swift b/test/abi/macOS/arm64/concurrency.swift index f447472f26a..3edce6c17f1 100644 --- a/test/abi/macOS/arm64/concurrency.swift +++ b/test/abi/macOS/arm64/concurrency.swift @@ -388,18 +388,21 @@ Added: _$ss33withTaskPriorityEscalationHandler9operation02onC9Escalated9isolatio Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvgZ Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvpZMV Added: _swift_task_getCurrentTaskName -Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYacntF -Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYacntF -Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKcntFZ -Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKcntFZ -Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKcntF -Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKcntF -Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYacntF -Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYacntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKcntF +// startSynchronously Added: _swift_task_startSynchronously +Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF Added: _swift_task_invokeSwiftIsIsolatingCurrentContext Added: _swift_task_isIsolatingCurrentContext diff --git a/test/abi/macOS/arm64/stdlib.swift b/test/abi/macOS/arm64/stdlib.swift index 705f7d13e41..69929431401 100644 --- a/test/abi/macOS/arm64/stdlib.swift +++ b/test/abi/macOS/arm64/stdlib.swift @@ -814,6 +814,133 @@ Added: _$ss7RawSpanVMa Added: _$ss7RawSpanVMn Added: _$ss7RawSpanVN +// SE-0464 UTF8Span +Added: _$sSS7copyingSSs8UTF8SpanV_tcfC +Added: _$sSS8utf8Spans04UTF8B0Vvg +Added: _$sSS8utf8Spans04UTF8B0VvpMV +Added: _$sSs8utf8Spans04UTF8B0Vvg +Added: _$sSs8utf8Spans04UTF8B0VvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11byteOffsetsSnySiGvM +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11byteOffsetsSnySiGvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11byteOffsetsSnySiGvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11byteOffsetsSnySiGvs +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11descriptionSSvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11descriptionSSvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV2eeoiySbAF_AFtFZ +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV11descriptionSSvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV11descriptionSSvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV15truncatedScalarAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV20overlongEncodingByteAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV22surrogateCodePointByteAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV26unexpectedContinuationByteAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV32invalidNonSurrogateCodePointByteAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValueAHSgs5UInt8V_tcfC +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValues5UInt8VvM +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValues5UInt8Vvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValues5UInt8VvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValues5UInt8Vvs +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVMa +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVMn +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVN +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSHsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSHsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSQsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSQsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSYsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSYsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVs0D0sMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVs0D0sWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVs23CustomStringConvertiblesMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVs23CustomStringConvertiblesWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4hash4intoys6HasherVz_tF +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4kindAF4KindVvM +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4kindAF4KindVvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4kindAF4KindVvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4kindAF4KindVvs +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV9hashValueSivg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV9hashValueSivpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVMa +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVMn +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVN +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVSHsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVSHsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVSQsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVSQsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVs0D0sMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVs0D0sWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVs23CustomStringConvertiblesMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVs23CustomStringConvertiblesWP +Added: _$ss7UnicodeO4UTF8O15_checkAllErrorsySayAD15ValidationErrorVGxSTRzs5UInt8V7ElementRtzlFZ +Added: _$ss8UTF8SpanV9unchecked12isKnownASCIIABs0B0Vys5UInt8VG_SbtcfC +Added: _$ss8UTF8SpanV10_countMasks6UInt64VvpZMV +Added: _$ss8UTF8SpanV10_flagsMasks6UInt64VvpZMV +Added: _$ss8UTF8SpanV10isKnownNFCSbvpMV +Added: _$ss8UTF8SpanV10validatingABs0B0Vys5UInt8VG_ts7UnicodeO0A0O15ValidationErrorVYKcfC +Added: _$ss8UTF8SpanV11checkForNFC10quickCheckS2b_tF +Added: _$ss8UTF8SpanV12isKnownASCIISbvpMV +Added: _$ss8UTF8SpanV13checkForASCIISbyF +Added: _$ss8UTF8SpanV14_countAndFlagss6UInt64VvM +Added: _$ss8UTF8SpanV14_countAndFlagss6UInt64Vvg +Added: _$ss8UTF8SpanV14_countAndFlagss6UInt64VvpMV +Added: _$ss8UTF8SpanV14_countAndFlagss6UInt64Vvs +Added: _$ss8UTF8SpanV17CharacterIteratorV11skipForward2byS2i_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV11skipForwardSiyF +Added: _$ss8UTF8SpanV17CharacterIteratorV21currentCodeUnitOffsetSivg +Added: _$ss8UTF8SpanV17CharacterIteratorV21currentCodeUnitOffsetSivpMV +Added: _$ss8UTF8SpanV17CharacterIteratorV4nextSJSgyF +Added: _$ss8UTF8SpanV17CharacterIteratorV5reset20roundingForwardsFromySi_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV5reset21roundingBackwardsFromySi_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV5reset11toUncheckedySi_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV6prefixAByF +Added: _$ss8UTF8SpanV17CharacterIteratorV6suffixAByF +Added: _$ss8UTF8SpanV17CharacterIteratorV8previousSJSgyF +Added: _$ss8UTF8SpanV17CharacterIteratorV8skipBack2byS2i_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV8skipBackSiyF +Added: _$ss8UTF8SpanV17CharacterIteratorV9codeUnitsABvg +Added: _$ss8UTF8SpanV17CharacterIteratorV9codeUnitsABvpMV +Added: _$ss8UTF8SpanV17CharacterIteratorVMa +Added: _$ss8UTF8SpanV17CharacterIteratorVMn +Added: _$ss8UTF8SpanV17CharacterIteratorVN +Added: _$ss8UTF8SpanV17CharacterIteratorVyAdBcfC +Added: _$ss8UTF8SpanV18_unsafeBaseAddressSVSgvM +Added: _$ss8UTF8SpanV18_unsafeBaseAddressSVSgvg +Added: _$ss8UTF8SpanV18_unsafeBaseAddressSVSgvpMV +Added: _$ss8UTF8SpanV18_unsafeBaseAddressSVSgvs +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV11skipForward2byS2i_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV11skipForwardSiyF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV21currentCodeUnitOffsetSivg +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV21currentCodeUnitOffsetSivpMV +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV4nexts0C0O0D0VSgyF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV5reset20roundingForwardsFromySi_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV5reset21roundingBackwardsFromySi_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV5reset11toUncheckedySi_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV6prefixAByF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV6suffixAByF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV8previouss0C0O0D0VSgyF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV8skipBack2byS2i_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV8skipBackSiyF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV9codeUnitsABvg +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV9codeUnitsABvpMV +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorVMa +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorVMn +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorVN +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorVyAdBcfC +Added: _$ss8UTF8SpanV21isCanonicallyLessThanySbABF +Added: _$ss8UTF8SpanV21makeCharacterIteratorAB0dE0VyF +Added: _$ss8UTF8SpanV23isCanonicallyEquivalent2toSbAB_tF +Added: _$ss8UTF8SpanV25makeUnicodeScalarIteratorAB0deF0VyF +Added: _$ss8UTF8SpanV4spans0B0Vys5UInt8VGvg +Added: _$ss8UTF8SpanV4spans0B0Vys5UInt8VGvpMV +Added: _$ss8UTF8SpanV5countSivpMV +Added: _$ss8UTF8SpanV7_nfcBits6UInt64VvpZMV +Added: _$ss8UTF8SpanV7isEmptySbvg +Added: _$ss8UTF8SpanV7isEmptySbvpMV +Added: _$ss8UTF8SpanV9_asciiBits6UInt64VvpZMV +Added: _$ss8UTF8SpanVMa +Added: _$ss8UTF8SpanVMn +Added: _$ss8UTF8SpanVN + + // SE-0467 MutableSpan and MutableRawSpan Added: _$ss11MutableSpanVMa Added: _$ss11MutableSpanVMn @@ -846,6 +973,8 @@ Added: _$sSs8UTF8ViewV4spans4SpanVys5UInt8VGvpMV Added: _$sSa11mutableSpans07MutableB0VyxGvr Added: _$ss10ArraySliceV11mutableSpans07MutableD0VyxGvr Added: _$ss15ContiguousArrayV11mutableSpans07MutableD0VyxGvr +Added: _$ss11InlineArrayVsRi__rlE11mutableSpans07MutableD0Vyq_Gvr +Added: _$ss15CollectionOfOneV11mutableSpans07MutableE0VyxGvr // _SwiftifyInfo enum for _SwiftifyImports macro Added: _$ss13_SwiftifyExprO5paramyABSicABmFWC @@ -886,9 +1015,6 @@ Added: _$ss11InlineArrayVMa // InlineArray nominal type descriptor Added: _$ss11InlineArrayVMn -// InlineArray.count property descriptor -Added: _$ss11InlineArrayVsRi__rlE5countSivpZMV - // InlineArray._storage _read accessor Added: _$ss11InlineArrayVsRi__rlE8_storagexq_BVvr diff --git a/test/abi/macOS/x86_64/concurrency.swift b/test/abi/macOS/x86_64/concurrency.swift index 219205d7204..d76d0b421ca 100644 --- a/test/abi/macOS/x86_64/concurrency.swift +++ b/test/abi/macOS/x86_64/concurrency.swift @@ -388,18 +388,21 @@ Added: _$ss33withTaskPriorityEscalationHandler9operation02onC9Escalated9isolatio Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvgZ Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvpZMV Added: _swift_task_getCurrentTaskName -Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYacntF -Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYacntF -Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKcntFZ -Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKcntFZ -Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKcntF -Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKcntF -Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYacntF -Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYacntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKcntF +// startSynchronously Added: _swift_task_startSynchronously +Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF Added: _swift_task_invokeSwiftIsIsolatingCurrentContext Added: _swift_task_isIsolatingCurrentContext diff --git a/test/abi/macOS/x86_64/stdlib.swift b/test/abi/macOS/x86_64/stdlib.swift index 86cf882fc7a..46e8d53798e 100644 --- a/test/abi/macOS/x86_64/stdlib.swift +++ b/test/abi/macOS/x86_64/stdlib.swift @@ -815,6 +815,133 @@ Added: _$ss7RawSpanVMa Added: _$ss7RawSpanVMn Added: _$ss7RawSpanVN +// SE-0464 UTF8Span +Added: _$sSS7copyingSSs8UTF8SpanV_tcfC +Added: _$sSS8utf8Spans04UTF8B0Vvg +Added: _$sSS8utf8Spans04UTF8B0VvpMV +Added: _$sSs8utf8Spans04UTF8B0Vvg +Added: _$sSs8utf8Spans04UTF8B0VvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11byteOffsetsSnySiGvM +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11byteOffsetsSnySiGvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11byteOffsetsSnySiGvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11byteOffsetsSnySiGvs +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11descriptionSSvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV11descriptionSSvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV2eeoiySbAF_AFtFZ +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV11descriptionSSvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV11descriptionSSvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV15truncatedScalarAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV20overlongEncodingByteAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV22surrogateCodePointByteAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV26unexpectedContinuationByteAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV32invalidNonSurrogateCodePointByteAHvpZMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValueAHSgs5UInt8V_tcfC +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValues5UInt8VvM +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValues5UInt8Vvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValues5UInt8VvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindV8rawValues5UInt8Vvs +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVMa +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVMn +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVN +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSHsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSHsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSQsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSQsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSYsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVSYsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVs0D0sMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVs0D0sWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVs23CustomStringConvertiblesMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4KindVs23CustomStringConvertiblesWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4hash4intoys6HasherVz_tF +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4kindAF4KindVvM +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4kindAF4KindVvg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4kindAF4KindVvpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV4kindAF4KindVvs +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV9hashValueSivg +Added: _$ss7UnicodeO4UTF8O15ValidationErrorV9hashValueSivpMV +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVMa +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVMn +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVN +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVSHsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVSHsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVSQsMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVSQsWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVs0D0sMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVs0D0sWP +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVs23CustomStringConvertiblesMc +Added: _$ss7UnicodeO4UTF8O15ValidationErrorVs23CustomStringConvertiblesWP +Added: _$ss7UnicodeO4UTF8O15_checkAllErrorsySayAD15ValidationErrorVGxSTRzs5UInt8V7ElementRtzlFZ +Added: _$ss8UTF8SpanV9unchecked12isKnownASCIIABs0B0Vys5UInt8VG_SbtcfC +Added: _$ss8UTF8SpanV10_countMasks6UInt64VvpZMV +Added: _$ss8UTF8SpanV10_flagsMasks6UInt64VvpZMV +Added: _$ss8UTF8SpanV10isKnownNFCSbvpMV +Added: _$ss8UTF8SpanV10validatingABs0B0Vys5UInt8VG_ts7UnicodeO0A0O15ValidationErrorVYKcfC +Added: _$ss8UTF8SpanV11checkForNFC10quickCheckS2b_tF +Added: _$ss8UTF8SpanV12isKnownASCIISbvpMV +Added: _$ss8UTF8SpanV13checkForASCIISbyF +Added: _$ss8UTF8SpanV14_countAndFlagss6UInt64VvM +Added: _$ss8UTF8SpanV14_countAndFlagss6UInt64Vvg +Added: _$ss8UTF8SpanV14_countAndFlagss6UInt64VvpMV +Added: _$ss8UTF8SpanV14_countAndFlagss6UInt64Vvs +Added: _$ss8UTF8SpanV17CharacterIteratorV11skipForward2byS2i_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV11skipForwardSiyF +Added: _$ss8UTF8SpanV17CharacterIteratorV21currentCodeUnitOffsetSivg +Added: _$ss8UTF8SpanV17CharacterIteratorV21currentCodeUnitOffsetSivpMV +Added: _$ss8UTF8SpanV17CharacterIteratorV4nextSJSgyF +Added: _$ss8UTF8SpanV17CharacterIteratorV5reset20roundingForwardsFromySi_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV5reset21roundingBackwardsFromySi_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV5reset11toUncheckedySi_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV6prefixAByF +Added: _$ss8UTF8SpanV17CharacterIteratorV6suffixAByF +Added: _$ss8UTF8SpanV17CharacterIteratorV8previousSJSgyF +Added: _$ss8UTF8SpanV17CharacterIteratorV8skipBack2byS2i_tF +Added: _$ss8UTF8SpanV17CharacterIteratorV8skipBackSiyF +Added: _$ss8UTF8SpanV17CharacterIteratorV9codeUnitsABvg +Added: _$ss8UTF8SpanV17CharacterIteratorV9codeUnitsABvpMV +Added: _$ss8UTF8SpanV17CharacterIteratorVMa +Added: _$ss8UTF8SpanV17CharacterIteratorVMn +Added: _$ss8UTF8SpanV17CharacterIteratorVN +Added: _$ss8UTF8SpanV17CharacterIteratorVyAdBcfC +Added: _$ss8UTF8SpanV18_unsafeBaseAddressSVSgvM +Added: _$ss8UTF8SpanV18_unsafeBaseAddressSVSgvg +Added: _$ss8UTF8SpanV18_unsafeBaseAddressSVSgvpMV +Added: _$ss8UTF8SpanV18_unsafeBaseAddressSVSgvs +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV11skipForward2byS2i_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV11skipForwardSiyF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV21currentCodeUnitOffsetSivg +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV21currentCodeUnitOffsetSivpMV +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV4nexts0C0O0D0VSgyF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV5reset20roundingForwardsFromySi_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV5reset21roundingBackwardsFromySi_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV5reset11toUncheckedySi_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV6prefixAByF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV6suffixAByF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV8previouss0C0O0D0VSgyF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV8skipBack2byS2i_tF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV8skipBackSiyF +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV9codeUnitsABvg +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorV9codeUnitsABvpMV +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorVMa +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorVMn +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorVN +Added: _$ss8UTF8SpanV21UnicodeScalarIteratorVyAdBcfC +Added: _$ss8UTF8SpanV21isCanonicallyLessThanySbABF +Added: _$ss8UTF8SpanV21makeCharacterIteratorAB0dE0VyF +Added: _$ss8UTF8SpanV23isCanonicallyEquivalent2toSbAB_tF +Added: _$ss8UTF8SpanV25makeUnicodeScalarIteratorAB0deF0VyF +Added: _$ss8UTF8SpanV4spans0B0Vys5UInt8VGvg +Added: _$ss8UTF8SpanV4spans0B0Vys5UInt8VGvpMV +Added: _$ss8UTF8SpanV5countSivpMV +Added: _$ss8UTF8SpanV7_nfcBits6UInt64VvpZMV +Added: _$ss8UTF8SpanV7isEmptySbvg +Added: _$ss8UTF8SpanV7isEmptySbvpMV +Added: _$ss8UTF8SpanV9_asciiBits6UInt64VvpZMV +Added: _$ss8UTF8SpanVMa +Added: _$ss8UTF8SpanVMn +Added: _$ss8UTF8SpanVN + + // SE-0467 MutableSpan and MutableRawSpan Added: _$ss11MutableSpanVMa Added: _$ss11MutableSpanVMn @@ -847,6 +974,8 @@ Added: _$sSs8UTF8ViewV4spans4SpanVys5UInt8VGvpMV Added: _$sSa11mutableSpans07MutableB0VyxGvr Added: _$ss10ArraySliceV11mutableSpans07MutableD0VyxGvr Added: _$ss15ContiguousArrayV11mutableSpans07MutableD0VyxGvr +Added: _$ss11InlineArrayVsRi__rlE11mutableSpans07MutableD0Vyq_Gvr +Added: _$ss15CollectionOfOneV11mutableSpans07MutableE0VyxGvr // _SwiftifyInfo enum for _SwiftifyImports macro Added: _$ss13_SwiftifyExprO5paramyABSicABmFWC @@ -887,9 +1016,6 @@ Added: _$ss11InlineArrayVMa // InlineArray nominal type descriptor Added: _$ss11InlineArrayVMn -// InlineArray.count property descriptor -Added: _$ss11InlineArrayVsRi__rlE5countSivpZMV - // InlineArray._storage _read accessor Added: _$ss11InlineArrayVsRi__rlE8_storagexq_BVvr diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift index 1409d75bf0f..96be8a8d291 100644 --- a/test/attr/attr_abi.swift +++ b/test/attr/attr_abi.swift @@ -1,9 +1,8 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-feature Extern -enable-experimental-feature ABIAttribute -enable-experimental-feature AddressableParameters -enable-experimental-feature NoImplicitCopy -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature StrictMemorySafety -enable-experimental-feature LifetimeDependence -enable-experimental-feature CImplementation -enable-experimental-feature ExecutionAttribute -import-bridging-header %S/Inputs/attr_abi.h -parse-as-library -Rabi-inference -debugger-support +// RUN: %target-typecheck-verify-swift -enable-experimental-feature Extern -enable-experimental-feature ABIAttribute -enable-experimental-feature AddressableParameters -enable-experimental-feature NoImplicitCopy -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature StrictMemorySafety -enable-experimental-feature LifetimeDependence -enable-experimental-feature CImplementation -import-bridging-header %S/Inputs/attr_abi.h -parse-as-library -Rabi-inference -debugger-support // REQUIRES: swift_feature_ABIAttribute // REQUIRES: swift_feature_AddressableParameters // REQUIRES: swift_feature_CImplementation -// REQUIRES: swift_feature_ExecutionAttribute // REQUIRES: swift_feature_Extern // REQUIRES: swift_feature_LifetimeDependence // REQUIRES: swift_feature_NoImplicitCopy @@ -948,7 +947,7 @@ func addressableTest( _ e: @MainActor () -> AnyObject, _ f: (isolated MainActor) -> AnyObject, _ g: @isolated(any) () -> AnyObject, // expected-error {{parameter 'g' type '@isolated(any) () -> AnyObject' in '@abi' should match '() -> AnyObject'}} - _ h: @execution(caller) () async -> AnyObject, + _ h: nonisolated(nonsending) () async -> AnyObject, _ i: () -> AnyObject, // expected-error {{parameter 'i' type '() -> AnyObject' in '@abi' should match '@isolated(any) () -> AnyObject'}} _ j: () async -> Void, _ k: () -> Void, // expected-error {{parameter 'k' type '() -> Void' in '@abi' should match '() async -> Void'}} @@ -1232,41 +1231,41 @@ nonisolated func isolation5() {} @abi(func isolation7(_: some Actor)) func isolation7(_: isolated some Actor) {} -@abi(@execution(concurrent) func isolation8() async) -@execution(concurrent) func isolation8() async {} +@abi(@concurrent func isolation8() async) +@concurrent func isolation8() async {} @abi(func isolation9() async) -@execution(concurrent) func isolation9() async {} +@concurrent func isolation9() async {} -@abi(@execution(concurrent) func isolation10() async) +@abi(@concurrent func isolation10() async) func isolation10() async {} @abi(nonisolated func isolation11() async) -@execution(concurrent) func isolation11() async {} +@concurrent func isolation11() async {} -@abi(@execution(concurrent) func isolation12() async) +@abi(@concurrent func isolation12() async) nonisolated func isolation12() async {} -@abi(@execution(caller) func isolation13() async) -@execution(caller) func isolation13() async {} +@abi(nonisolated(nonsending) func isolation13() async) +nonisolated(nonsending) func isolation13() async {} @abi(func isolation14() async) -@execution(caller) func isolation14() async {} +nonisolated(nonsending) func isolation14() async {} -@abi(@execution(caller) func isolation15() async) +@abi(nonisolated(nonsending) func isolation15() async) func isolation15() async {} @abi(nonisolated func isolation16() async) -@execution(caller) func isolation16() async {} +nonisolated(nonsending) func isolation16() async {} -@abi(@execution(caller) func isolation17() async) +@abi(nonisolated(nonsending) func isolation17() async) nonisolated func isolation17() async {} -@abi(@execution(caller) func isolation18() async) -@execution(concurrent) func isolation18() async {} +@abi(nonisolated(nonsending) func isolation18() async) +@concurrent func isolation18() async {} -@abi(@execution(concurrent) func isolation19() async) -@execution(caller) func isolation19() async {} +@abi(@concurrent func isolation19() async) +nonisolated(nonsending) func isolation19() async {} // NSCopying - see attr/attr_abi_objc.swift diff --git a/test/attr/attr_availability_transitive_osx.swift b/test/attr/attr_availability_transitive_osx.swift index b0a17ee2f3b..7187ca6c26e 100644 --- a/test/attr/attr_availability_transitive_osx.swift +++ b/test/attr/attr_availability_transitive_osx.swift @@ -87,8 +87,7 @@ func never_available_func( ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -105,8 +104,7 @@ func osx_func( ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -159,7 +157,7 @@ var never_var: ( ) = ( always(), never(), // expected-error {{'never()' is unavailable}} - osx_future(), // expected-error {{'osx_future()' is only available in macOS 99 or newer}} + osx_future(), osx(), osx_ios(), osx_extension() @@ -176,7 +174,7 @@ var osx_var: ( ) = ( always(), never(), // expected-error {{'never()' is unavailable}} - osx_future(), // expected-error {{'osx_future()' is only available in macOS 99 or newer}} + osx_future(), osx(), osx_ios(), osx_extension() @@ -218,7 +216,7 @@ struct AlwaysAvailableContainer { // expected-note 2 {{add @available attribute struct NeverAvailableContainer { // expected-note 2 {{'NeverAvailableContainer' has been explicitly marked unavailable here}} let always_var: AlwaysAvailable = always() let never_var: NeverAvailable = never() // expected-error {{'never()' is unavailable}} - let osx_future_var: OSXFutureAvailable = osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} + let osx_future_var: OSXFutureAvailable = osx_future() let osx_var: OSXUnavailable = osx() let osx_ios_var: MultiPlatformUnavailable = osx_ios() let osx_extension_var: OSXAppExtensionsUnavailable = osx_extension() @@ -228,7 +226,7 @@ struct NeverAvailableContainer { // expected-note 2 {{'NeverAvailableContainer' struct OSXUnavailableContainer { // expected-note 2 {{'OSXUnavailableContainer' has been explicitly marked unavailable here}} let always_var: AlwaysAvailable = always() let never_var: NeverAvailable = never() // expected-error {{'never()' is unavailable}} - let osx_future_var: OSXFutureAvailable = osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} + let osx_future_var: OSXFutureAvailable = osx_future() let osx_var: OSXUnavailable = osx() let osx_ios_var: MultiPlatformUnavailable = osx_ios() let osx_extension_var: OSXAppExtensionsUnavailable = osx_extension() @@ -301,8 +299,7 @@ extension ExtendMe { ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -319,8 +316,7 @@ extension ExtendMe { ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -337,15 +333,14 @@ extension ExtendMe { ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() } @available(OSXApplicationExtension, unavailable) - func never_available_extension_osx_app_extension_method( // expected-note {{add @available attribute to enclosing instance method}} + func never_available_extension_osx_app_extension_method( _: AlwaysAvailable, _: NeverAvailable, _: OSXFutureAvailable, @@ -355,8 +350,7 @@ extension ExtendMe { ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -380,8 +374,7 @@ extension ExtendMe { ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -398,8 +391,7 @@ extension ExtendMe { ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -416,15 +408,14 @@ extension ExtendMe { ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() } @available(OSXApplicationExtension, unavailable) - func osx_extension_osx_app_extension_method( // expected-note {{add @available attribute to enclosing instance method}} + func osx_extension_osx_app_extension_method( _: AlwaysAvailable, _: NeverAvailable, _: OSXFutureAvailable, @@ -434,8 +425,7 @@ extension ExtendMe { ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -477,8 +467,7 @@ extension ExtendMe { // expected-note * {{add @available attribute to enclosing ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -495,8 +484,7 @@ extension ExtendMe { // expected-note * {{add @available attribute to enclosing ) { always() never() // expected-error {{'never()' is unavailable}} - osx_future() // expected-error {{'osx_future()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + osx_future() osx() osx_ios() osx_extension() @@ -534,7 +522,7 @@ func available_func_call_extension_methods(_ e: ExtendMe) { // expected-note {{a } @available(OSX, obsoleted: 10.9) -struct OSXObsoleted {} // expected-note 2 {{'OSXObsoleted' was obsoleted in macOS 10.9}} +struct OSXObsoleted {} // expected-note 4 {{'OSXObsoleted' was obsoleted in macOS 10.9}} @available(OSX, unavailable) @available(OSX, introduced: 99) @@ -561,17 +549,46 @@ func osx_unavailable_func( OSXUnavailableAndIntroducedInFutureSameAttribute, OSXIntroducedInFutureAndUnavailable ) { - // FIXME: [availability] Stop diagnosing potential unavailability or obsoletion in an unavailable context. - _ = OSXFutureAvailable() // expected-error {{'OSXFutureAvailable' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + // FIXME: [availability] Stop diagnosing obsoletion in an unavailable context. + _ = OSXFutureAvailable() _ = OSXObsoleted() // expected-error {{'OSXObsoleted' is unavailable in macOS}} _ = OSXUnavailableAndIntroducedInFuture() _ = OSXUnavailableAndIntroducedInFutureSameAttribute() _ = OSXIntroducedInFutureAndUnavailable() func takesType(_ t: T.Type) {} - takesType(OSXFutureAvailable.self) // expected-error {{'OSXFutureAvailable' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + takesType(OSXFutureAvailable.self) + takesType(OSXObsoleted.self) // expected-error {{'OSXObsoleted' is unavailable in macOS}} + takesType(OSXUnavailableAndIntroducedInFuture.self) + takesType(OSXUnavailableAndIntroducedInFutureSameAttribute.self) + takesType(OSXIntroducedInFutureAndUnavailable.self) + + return (s1, s2, s3, s4, s5) +} + +@available(OSX, unavailable, introduced: 99) +func osx_unavailable_and_introduced_func( + _ s1: OSXFutureAvailable, + _ s2: OSXObsoleted, + _ s3: OSXUnavailableAndIntroducedInFuture, + _ s4: OSXUnavailableAndIntroducedInFutureSameAttribute, + _ s5: OSXIntroducedInFutureAndUnavailable, +) -> ( + OSXFutureAvailable, + OSXObsoleted, + OSXUnavailableAndIntroducedInFuture, + OSXUnavailableAndIntroducedInFutureSameAttribute, + OSXIntroducedInFutureAndUnavailable +) { + // FIXME: [availability] Stop diagnosing obsoletion in an unavailable context. + _ = OSXFutureAvailable() + _ = OSXObsoleted() // expected-error {{'OSXObsoleted' is unavailable in macOS}} + _ = OSXUnavailableAndIntroducedInFuture() + _ = OSXUnavailableAndIntroducedInFutureSameAttribute() + _ = OSXIntroducedInFutureAndUnavailable() + + func takesType(_ t: T.Type) {} + takesType(OSXFutureAvailable.self) takesType(OSXObsoleted.self) // expected-error {{'OSXObsoleted' is unavailable in macOS}} takesType(OSXUnavailableAndIntroducedInFuture.self) takesType(OSXUnavailableAndIntroducedInFutureSameAttribute.self) diff --git a/test/attr/attr_availability_transitive_osx_appext.swift b/test/attr/attr_availability_transitive_osx_appext.swift index 412af439ffe..371b90cbd59 100644 --- a/test/attr/attr_availability_transitive_osx_appext.swift +++ b/test/attr/attr_availability_transitive_osx_appext.swift @@ -386,8 +386,7 @@ func osx_func_call_extension_methods(_ e: ExtendMe) { e.never_available_extension_osx_future_method() // expected-error {{'never_available_extension_osx_future_method()' is unavailable}} e.osx_extension_osx_future_method() - e.osx_app_extension_extension_osx_future_method() // expected-error {{'osx_app_extension_extension_osx_future_method()' is only available in macOS 99 or newer}} - // expected-note@-1 {{add 'if #available' version check}} + e.osx_app_extension_extension_osx_future_method() e.osx_app_extension_extension_never_available_method() // expected-error {{'osx_app_extension_extension_never_available_method()' is unavailable}} e.osx_app_extension_extension_osx_method() e.osx_app_extension_extension_osx_app_extension_method() diff --git a/test/attr/attr_execution.swift b/test/attr/attr_execution.swift deleted file mode 100644 index 9ee107a3821..00000000000 --- a/test/attr/attr_execution.swift +++ /dev/null @@ -1,141 +0,0 @@ -// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple -enable-experimental-feature ExecutionAttribute - -// REQUIRES: concurrency -// REQUIRES: swift_feature_ExecutionAttribute - -@execution(something) func invalidAttr() async {} // expected-error {{unknown option 'something' for attribute 'execution'}} - -@execution(concurrent) @execution(caller) func mutipleAttrs() async {} -// expected-error@-1 {{duplicate attribute}} expected-note@-1 {{attribute already specified here}} - -do { - @execution(caller) struct S {} - // expected-error@-1 {{'@execution(caller)' attribute cannot be applied to this declaration}} - - func f(@execution(caller) param: Int) {} - // expected-error@-1 {{'@execution(caller)' attribute cannot be applied to this declaration}} -} - -@execution(concurrent) func nonAsync1() {} -// expected-error@-1 {{cannot use '@execution' on non-async global function 'nonAsync1()'}} - -@execution(caller) func nonAsync2() {} -// expected-error@-1 {{cannot use '@execution' on non-async global function 'nonAsync2()'}} - -@execution(concurrent) func testGlobal() async {} // Ok - -struct Test { - @execution(concurrent) init() {} - // expected-error@-1 {{cannot use '@execution' on non-async initializer 'init()'}} - - @execution(concurrent) init(async: Void) async {} - - @execution(concurrent) func member() {} - // expected-error@-1 {{cannot use '@execution' on non-async instance method 'member()'}} - - @execution(concurrent) func member() async {} // Ok - - @execution(concurrent) var syncP: Int { - // expected-error@-1 {{cannot use '@execution' on non-async property 'syncP'}} - get {} - } - @execution(concurrent) var asyncP: Int { - get async {} - } - - // expected-error@+1 {{cannot use '@execution' on non-async subscript 'subscript(sync:)'}} - @execution(caller) subscript(sync _: Int) -> Bool { - @execution(concurrent) get { false } - // expected-error@-1 {{@execution(concurrent)' attribute cannot be applied to this declaration}} - @execution(concurrent) set { } - // expected-error@-1 {{@execution(concurrent)' attribute cannot be applied to this declaration}} - } - @execution(caller) subscript(async _: Int) -> Bool { - get async {} - } - - @execution(caller) var storedVar: Int - // expected-error@-1 {{'@execution(caller)' must not be used on stored properties}} - @execution(caller) let storedLet: Int - // expected-error@-1 {{'@execution(caller)' must not be used on stored properties}} -} - -do { - class C { - @execution(caller) deinit {} - // expected-error@-1 {{'@execution(caller)' attribute cannot be applied to this declaration}} - } -} - -do { - @execution(caller) func local() async {} // Ok - - protocol P { - @execution(caller) var syncP: Int { get } - // expected-error@-1 {{cannot use '@execution' on non-async property 'syncP'}} - - @execution(caller) var asyncP: Int { get async } - } -} - -struct TestAttributeCollisions { - @execution(concurrent) nonisolated func testNonIsolated() async {} - - @execution(concurrent) func test(arg: isolated MainActor) async {} - // expected-error@-1 {{cannot use '@execution' on instance method 'test(arg:)' because it has an isolated parameter: 'arg'}} - @execution(concurrent) subscript(test arg: isolated MainActor) -> Int { - // expected-error@-1 {{cannot use '@execution' on subscript 'subscript(test:)' because it has an isolated parameter: 'arg'}} - get async {} - } - - @execution(concurrent) func testIsolationAny(arg: @isolated(any) () -> Void) async {} - // expected-error@-1 {{cannot use '@execution' on instance method 'testIsolationAny(arg:)' because it has a dynamically isolated parameter: 'arg'}} - @execution(concurrent) subscript(testIsolationAny arg: @isolated(any) () -> Void) -> Int { - // expected-error@-1 {{cannot use '@execution' on subscript 'subscript(testIsolationAny:)' because it has a dynamically isolated parameter: 'arg'}} - get async {} - } - - @MainActor @execution(concurrent) func testGlobalActor() async {} - // expected-warning @-1 {{instance method 'testGlobalActor()' has multiple actor-isolation attributes (@MainActor and @execution(concurrent))}} - - @execution(caller) nonisolated func testNonIsolatedCaller() async {} // Ok - @MainActor @execution(caller) func testGlobalActorCaller() async {} - // expected-warning@-1 {{instance method 'testGlobalActorCaller()' has multiple actor-isolation attributes (@MainActor and @execution(caller))}} - @execution(caller) func testCaller(arg: isolated MainActor) async {} - // expected-error@-1 {{cannot use '@execution' on instance method 'testCaller(arg:)' because it has an isolated parameter: 'arg'}} - - @execution(concurrent) @Sendable func test(_: @Sendable () -> Void, _: sending Int) async {} // Ok - @execution(caller) @Sendable func testWithSendableCaller(_: @Sendable () -> Void, _: sending Int) async {} // Ok -} - -@MainActor -protocol P { - func test() async -} - -struct InfersMainActor : P { - @execution(concurrent) func test() async {} -} - -@MainActor -struct IsolatedType { - @execution(concurrent) func test() async {} -} - -_ = { @execution(caller) in // Ok -} - -_ = { @execution(concurrent) in // Ok -} - -_ = { @MainActor @execution(concurrent) in - // expected-error@-1 {{cannot use '@execution' because function type is isolated to a global actor 'MainActor'}} -} - -_ = { @execution(concurrent) () -> Int in - // expected-error@-1 {{'@execution' on non-async closure}} -} - -_ = { @execution(caller) (x: isolated (any Actor)?) in - // expected-error@-1 {{cannot use '@execution' together with an isolated parameter}} -} diff --git a/test/attr/attr_inlinable_available.swift b/test/attr/attr_inlinable_available.swift index 1f948801dc0..4b6cd0e875a 100644 --- a/test/attr/attr_inlinable_available.swift +++ b/test/attr/attr_inlinable_available.swift @@ -321,14 +321,14 @@ public func alwaysUnavailable( ) { defer { _ = AtDeploymentTarget() - _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}} + _ = AfterDeploymentTarget() } _ = NoAvailable() _ = BeforeInliningTarget() _ = AtInliningTarget() _ = BetweenTargets() _ = AtDeploymentTarget() - _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}} + _ = AfterDeploymentTarget() _ = Unavailable() if #available(macOS 11, *) { @@ -568,14 +568,14 @@ public func spiDeployedUseNoAvailable( // expected-note 3 {{add @available attri ) { defer { _ = AtDeploymentTarget() - _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}} + _ = AfterDeploymentTarget() } _ = NoAvailable() _ = BeforeInliningTarget() _ = AtInliningTarget() _ = BetweenTargets() _ = AtDeploymentTarget() - _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}} + _ = AfterDeploymentTarget() _ = Unavailable() if #available(macOS 11, *) { @@ -718,7 +718,7 @@ public func spiDeployedUseNoAvailable( // expected-note 3 {{add @available attri _ = AtInliningTarget() _ = BetweenTargets() _ = AtDeploymentTarget() - _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}} + _ = AfterDeploymentTarget() if #available(macOS 11, *) { _ = AfterDeploymentTarget() @@ -727,7 +727,7 @@ public func spiDeployedUseNoAvailable( // expected-note 3 {{add @available attri return () } -@inlinable public var inlinedNoAvailableGlobalUnavailableSetter: Any { // expected-note {{add @available attribute to enclosing var}} +@inlinable public var inlinedNoAvailableGlobalUnavailableSetter: Any { get { fatalError() } @@ -738,7 +738,7 @@ public func spiDeployedUseNoAvailable( // expected-note 3 {{add @available attri _ = AtInliningTarget() _ = BetweenTargets() _ = AtDeploymentTarget() - _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}} + _ = AfterDeploymentTarget() if #available(macOS 11, *) { _ = AfterDeploymentTarget() @@ -847,7 +847,7 @@ public func defaultArgsUseUnavailable( _: Any = AtInliningTarget.self, _: Any = BetweenTargets.self, _: Any = AtDeploymentTarget.self, - _: Any = AfterDeploymentTarget.self, // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} + _: Any = AfterDeploymentTarget.self, _: Any = Unavailable.self ) {} @@ -909,7 +909,7 @@ public struct PropertyWrapper { public init(_ value: T) { self.wrappedValue = value } } -public struct PublicStruct { // expected-note 21 {{add @available attribute}} +public struct PublicStruct { // expected-note 20 {{add @available attribute}} // Public property declarations are exposed. public var aPublic: NoAvailable, bPublic: BeforeInliningTarget, @@ -961,7 +961,7 @@ public struct PublicStruct { // expected-note 21 {{add @available attribute}} @available(macOS, unavailable) public var fUnavailable: AfterDeploymentTarget { - AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available' version check}} + AfterDeploymentTarget() } // The inferred types of public properties are exposed. @@ -1167,7 +1167,7 @@ public struct UnavailablePublicStruct { cPublicInit: Any = AtInliningTarget(), dPublicInit: Any = BetweenTargets(), ePublicInit: Any = AtDeploymentTarget(), - fPublicInit: Any = AfterDeploymentTarget(), // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} + fPublicInit: Any = AfterDeploymentTarget(), gPublicInit: Any = Unavailable() var aInternal: NoAvailable = .init(), diff --git a/test/attr/execution_behavior_attrs.swift b/test/attr/execution_behavior_attrs.swift new file mode 100644 index 00000000000..90a9a818c0c --- /dev/null +++ b/test/attr/execution_behavior_attrs.swift @@ -0,0 +1,139 @@ +// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple + +// REQUIRES: concurrency + +// FIXME: Bad parser diagnostic on C++ side +nonisolated(something) func invalidAttr() async {} // expected-error {{cannot find 'nonisolated' in scope}} +// expected-error@-1 {{cannot find 'something' in scope}} +// expected-error@-2 {{consecutive statements on a line must be separated by ';'}} + +@concurrent nonisolated(nonsending) func mutipleAttrs() async {} +// expected-error@-1 {{global function 'mutipleAttrs()' has multiple actor-isolation attributes (@concurrent and 'nonisolated(nonsending)')}} + +do { + nonisolated(nonsending) struct S {} + // expected-error@-1 {{'nonisolated(nonsending)' is only applicable to asynchronous functions, initializers, subscripts and computed properties}} + + + func f(nonisolated(nonsending) param: Int) {} + // expected-error@-1 {{expected parameter name followed by ':'}} + // expected-error@-2 {{parameter may have at most one of the 'inout', 'borrowing', or 'consuming' specifiers}} +} + +@concurrent func nonAsync1() {} +// expected-error@-1 {{cannot use @concurrent on non-async global function 'nonAsync1()'}} + +nonisolated(nonsending) func nonAsync2() {} +// expected-error@-1 {{cannot use 'nonisolated(nonsending)' on non-async global function 'nonAsync2()'}} + +@concurrent func testGlobal() async {} // Ok + +struct Test { + @concurrent init() {} + // expected-error@-1 {{cannot use @concurrent on non-async initializer 'init()'}} + + @concurrent init(async: Void) async {} + + @concurrent func member() {} + // expected-error@-1 {{cannot use @concurrent on non-async instance method 'member()'}} + + @concurrent func member() async {} // Ok + + @concurrent var syncP: Int { + // expected-error@-1 {{cannot use @concurrent on non-async property 'syncP'}} + get {} + } + @concurrent var asyncP: Int { + get async {} + } + + // expected-error@+1 {{cannot use 'nonisolated(nonsending)' on non-async subscript 'subscript(sync:)'}} + nonisolated(nonsending) subscript(sync _: Int) -> Bool { + @concurrent get { false } + // expected-error@-1 {{@concurrent' attribute cannot be applied to this declaration}} + @concurrent set { } + // expected-error@-1 {{@concurrent' attribute cannot be applied to this declaration}} + } + nonisolated(nonsending) subscript(async _: Int) -> Bool { + get async {} + } + + // FIXME: Incorrect quotes due to inconsistent DeclAttribute printing between modifiers and attributes + nonisolated(nonsending) var storedVar: Int + // expected-error@-1 {{''nonisolated(nonsending)'' must not be used on stored properties}} + nonisolated(nonsending) let storedLet: Int + // expected-error@-1 {{''nonisolated(nonsending)'' must not be used on stored properties}} +} + +do { + class C { + nonisolated(nonsending) deinit {} + // expected-error@-1 {{'nonisolated(nonsending)' is only applicable to asynchronous functions, initializers, subscripts and computed properties}} + } +} + +do { + nonisolated(nonsending) func local() async {} // Ok + + protocol P { + nonisolated(nonsending) var syncP: Int { get } + // expected-error@-1 {{cannot use 'nonisolated(nonsending)' on non-async property 'syncP'}} + + nonisolated(nonsending) var asyncP: Int { get async } + } +} + +struct TestAttributeCollisions { + @concurrent nonisolated func testNonIsolated() async {} + + @concurrent func test(arg: isolated MainActor) async {} + // expected-error@-1 {{cannot use @concurrent on instance method 'test(arg:)' because it has an isolated parameter: 'arg'}} + @concurrent subscript(test arg: isolated MainActor) -> Int { + // expected-error@-1 {{cannot use @concurrent on subscript 'subscript(test:)' because it has an isolated parameter: 'arg'}} + get async {} + } + + @concurrent func testIsolationAny(arg: @isolated(any) () -> Void) async {} + // expected-error@-1 {{cannot use @concurrent on instance method 'testIsolationAny(arg:)' because it has a dynamically isolated parameter: 'arg'}} + @concurrent subscript(testIsolationAny arg: @isolated(any) () -> Void) -> Int { + // expected-error@-1 {{cannot use @concurrent on subscript 'subscript(testIsolationAny:)' because it has a dynamically isolated parameter: 'arg'}} + get async {} + } + + @MainActor @concurrent func testGlobalActor() async {} + // expected-warning @-1 {{instance method 'testGlobalActor()' has multiple actor-isolation attributes (@MainActor and @concurrent)}} + + nonisolated(nonsending) nonisolated func testNonIsolatedCaller() async {} // expected-error {{duplicate modifier}} expected-note {{modifier already specified here}} + @MainActor nonisolated(nonsending) func testGlobalActorCaller() async {} + // expected-warning@-1 {{instance method 'testGlobalActorCaller()' has multiple actor-isolation attributes (@MainActor and 'nonisolated(nonsending)')}} + nonisolated(nonsending) func testCaller(arg: isolated MainActor) async {} + // expected-error@-1 {{cannot use 'nonisolated(nonsending)' on instance method 'testCaller(arg:)' because it has an isolated parameter: 'arg'}} + + @concurrent @Sendable func test(_: @Sendable () -> Void, _: sending Int) async {} // Ok + @Sendable nonisolated(nonsending) func testWithSendableCaller(_: @Sendable () -> Void, _: sending Int) async {} // Ok +} + +@MainActor +protocol P { + func test() async +} + +struct InfersMainActor : P { + @concurrent func test() async {} +} + +@MainActor +struct IsolatedType { + @concurrent func test() async {} +} + +_ = { @concurrent in // Ok +} + +_ = { @MainActor @concurrent in + // expected-error@-1 {{cannot use @concurrent because function type is isolated to a global actor 'MainActor'}} +} + +_ = { @concurrent () -> Int in + // expected-error@-1 {{@concurrent on non-async closure}} +} diff --git a/test/decl/class/actor/global_actor_conformance.swift b/test/decl/class/actor/global_actor_conformance.swift index edb828b5342..6016b8a7ad6 100644 --- a/test/decl/class/actor/global_actor_conformance.swift +++ b/test/decl/class/actor/global_actor_conformance.swift @@ -32,6 +32,7 @@ protocol P2 { // expected-warning@+1{{conformance of 'C1' to protocol 'P1' crosses into global actor 'GlobalActor'-isolated code and can cause data races}} class C1 : P1, P2 { // expected-note@-1{{turn data races into runtime errors with '@preconcurrency'}} + // expected-note@-2{{isolate this conformance to the global actor 'GlobalActor' with '@GlobalActor'}} typealias Assoc = String @@ -57,6 +58,7 @@ protocol NonIsolatedRequirement { extension OnMain: NonIsolatedRequirement { // expected-note@-1{{turn data races into runtime errors with '@preconcurrency'}} // expected-note@-2{{mark all declarations used in the conformance 'nonisolated'}} + // expected-note@-3{{isolate this conformance to the main actor with '@MainActor'}} // expected-note@+1 {{main actor-isolated instance method 'requirement()' cannot satisfy nonisolated requirement}} func requirement() {} } diff --git a/test/decl/ext/Inputs/objc_implementation.h b/test/decl/ext/Inputs/objc_implementation.h index 62241c1f8d9..f40e8aebbc4 100644 --- a/test/decl/ext/Inputs/objc_implementation.h +++ b/test/decl/ext/Inputs/objc_implementation.h @@ -53,6 +53,7 @@ @property (readonly) int readonlyPropertyFromHeader4; @property (readonly) int readonlyPropertyFromHeader5; @property (readonly) int readonlyPropertyFromHeader6; +@property (readonly) int readonlyPropertyFromHeader7; + (void)classMethod1:(int)param; + (void)classMethod2:(int)param; @@ -84,6 +85,10 @@ @property int categoryPropertyFromHeader2; @property int categoryPropertyFromHeader3; @property int categoryPropertyFromHeader4; +@property int categoryPropertyFromHeader5; + +@property (readonly) int categoryReadonlyPropertyFromHeader1; + @end @@ -118,6 +123,7 @@ - (void)doSomethingAsynchronousWithCompletionHandler:(void (^ _Nonnull)(id _Nullable result, NSError * _Nullable error))completionHandler; - (void)doSomethingElseAsynchronousWithCompletionHandler:(void (^ _Nullable)(id _Nonnull result))completionHandler; - (void)doSomethingFunAndAsynchronousWithCompletionHandler:(void (^ _Nonnull)(id _Nullable result, NSError * _Nullable error))completionHandler; +- (void)doSomethingMissingAndAsynchronousWithCompletionHandler:(void (^ _Nonnull)(id _Nullable result, NSError * _Nullable error))completionHandler; - (void)doSomethingOverloadedWithCompletionHandler:(void (^ _Nonnull)())completionHandler; - (void)doSomethingOverloaded __attribute__((__swift_attr__("@_unavailableFromAsync(message: \"Use async doSomethingOverloaded instead.\")"))); diff --git a/test/decl/ext/objc_implementation.swift b/test/decl/ext/objc_implementation.swift index 8fa6d92f098..ab8f0f0667f 100644 --- a/test/decl/ext/objc_implementation.swift +++ b/test/decl/ext/objc_implementation.swift @@ -5,16 +5,19 @@ protocol EmptySwiftProto {} +// expected-note@+1 {{previously implemented here}} @objc @implementation extension ObjCClass: EmptySwiftProto, EmptyObjCProto { - // expected-note@-1 {{previously implemented here}} - // expected-error@-2 {{extension for main class interface should provide implementation for instance method 'method(fromHeader4:)'}} - // expected-error@-3 {{extension for main class interface should provide implementation for property 'propertyFromHeader9'}} - // FIXME: give better diagnostic expected-error@-4 {{extension for main class interface should provide implementation for property 'propertyFromHeader8'}} - // FIXME: give better diagnostic expected-error@-5 {{extension for main class interface should provide implementation for property 'propertyFromHeader7'}} - // FIXME: give better diagnostic expected-error@-6 {{extension for main class interface should provide implementation for instance method 'method(fromHeader3:)'}} - // expected-error@-7 {{'@objc @implementation' extension cannot add conformance to 'EmptySwiftProto'; add this conformance with an ordinary extension}} - // expected-error@-8 {{'@objc @implementation' extension cannot add conformance to 'EmptyObjCProto'; add this conformance in the Objective-C header}} - // expected-error@-9 {{extension for main class interface should provide implementation for instance method 'extensionMethod(fromHeader2:)'}} + // expected-error@-1 {{'@objc @implementation' extension cannot add conformance to 'EmptySwiftProto'; add this conformance with an ordinary extension}} + // expected-error@-2 {{'@objc @implementation' extension cannot add conformance to 'EmptyObjCProto'; add this conformance in the Objective-C header}} + // expected-error@-3 {{extension for main class interface does not provide all required implementations}} + // expected-note@-4 {{missing instance method 'method(fromHeader4:)'}} {{none}} + // expected-note@-5 {{missing property 'propertyFromHeader9'}} {{none}} + // FIXME: give better diagnostic expected-note@-6 {{missing property 'propertyFromHeader8'}} {{none}} + // FIXME: give better diagnostic expected-note@-7 {{missing property 'propertyFromHeader7'}} {{none}} + // FIXME: give better diagnostic expected-note@-8 {{missing instance method 'method(fromHeader3:)'}} {{none}} + // expected-note@-9 {{missing instance method 'extensionMethod(fromHeader2:)'}} {{none}} + // expected-note@-10 {{missing property 'readonlyPropertyFromHeader7'}} + // expected-note@-11 {{add stubs for missing '@implementation' requirements}} {{77-77=\n @objc(methodFromHeader3:)\n open func method(fromHeader3 param: Int32) {\n <#code#>\n \}\n\n @objc(methodFromHeader4:)\n open func method(fromHeader4 param: Int32) {\n <#code#>\n \}\n\n @objc(propertyFromHeader7)\n open var propertyFromHeader7: Int32\n\n @objc(propertyFromHeader8)\n open var propertyFromHeader8: Int32\n\n @objc(propertyFromHeader9)\n open var propertyFromHeader9: Int32\n\n @objc(readonlyPropertyFromHeader7)\n open let readonlyPropertyFromHeader7: Int32\n\n @objc(extensionMethodFromHeader2:)\n open func extensionMethod(fromHeader2 param: Int32) {\n <#code#>\n \}\n}} func method(fromHeader1: CInt) { // OK, provides an implementation for the header's method. @@ -233,10 +236,14 @@ protocol EmptySwiftProto {} // expected-error@-1 {{property 'rdar122280735' of type '(() -> ()) -> Void' does not match type '(@escaping () -> Void) -> Void' declared by the header}} } +// expected-note@+1 {{'PresentAdditions' previously declared here}} @objc(PresentAdditions) @implementation extension ObjCClass { - // expected-note@-1 {{'PresentAdditions' previously declared here}} - // expected-error@-2 {{extension for category 'PresentAdditions' should provide implementation for instance method 'categoryMethod(fromHeader4:)'}} - // FIXME: give better diagnostic expected-error@-3 {{extension for category 'PresentAdditions' should provide implementation for instance method 'categoryMethod(fromHeader3:)'}} + // expected-error@-1 {{extension for category 'PresentAdditions' does not provide all required implementations}} + // expected-note@-2 {{missing instance method 'categoryMethod(fromHeader4:)'}} {{none}} + // FIXME: give better diagnostic expected-note@-3 {{missing instance method 'categoryMethod(fromHeader3:)'}} {{none}} + // expected-note@-4 {{missing property 'categoryPropertyFromHeader5'}} {{none}} + // expected-note@-5 {{missing property 'categoryReadonlyPropertyFromHeader1'}} {{none}} + // expected-note@-6 {{add stubs for missing '@implementation' requirements}} {{62-62=\n @objc(categoryMethodFromHeader3:)\n open func categoryMethod(fromHeader3 param: Int32) {\n <#code#>\n \}\n\n @objc(categoryMethodFromHeader4:)\n open func categoryMethod(fromHeader4 param: Int32) {\n <#code#>\n \}\n\n @objc(categoryPropertyFromHeader5)\n open var categoryPropertyFromHeader5: Int32 {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n\n @objc(categoryReadonlyPropertyFromHeader1)\n open var categoryReadonlyPropertyFromHeader1: Int32 {\n <#code#>\n \}\n}} func method(fromHeader3: CInt) { // FIXME: should emit expected-DISABLED-error@-1 {{instance method 'method(fromHeader3:)' should be implemented in extension for main class interface, not category 'PresentAdditions'}} @@ -295,7 +302,9 @@ protocol EmptySwiftProto {} } @objc(SwiftNameTests) @implementation extension ObjCClass { - // expected-error@-1 {{extension for category 'SwiftNameTests' should provide implementation for instance method 'methodSwiftName6B()'}} + // expected-error@-1 {{extension for category 'SwiftNameTests' does not provide all required implementations}} + // expected-note@-2 {{missing instance method 'methodSwiftName6B()'}} {{none}} + // expected-note@-3 {{add stub for missing '@implementation' requirement}} {{60-60=\n @objc(methodObjCName6B)\n open func methodSwiftName6B() {\n <#code#>\n \}\n}} func methodSwiftName1() { // expected-error@-1 {{selector 'methodSwiftName1' for instance method 'methodSwiftName1()' not found in header; did you mean 'methodObjCName1'?}} {{3-3=@objc(methodObjCName1) }} @@ -363,6 +372,10 @@ protocol EmptySwiftProto {} } @objc(Effects) @implementation extension ObjCClass { + // expected-error@-1 {{extension for category 'Effects' does not provide all required implementations}} + // expected-note@-2 {{missing instance method 'doSomethingMissingAndAsynchronous()' or 'doSomethingMissingAndAsynchronous(completionHandler:)'}} + // expected-note@-3 {{add stub for missing '@implementation' requirement}} {{53-53=\n @objc(doSomethingMissingAndAsynchronousWithCompletionHandler:)\n open func doSomethingMissingAndAsynchronous() async throws -> Any {\n <#code#>\n \}\n}} + @available(SwiftStdlib 5.1, *) public func doSomethingAsynchronous() async throws -> Any { return self @@ -406,7 +419,9 @@ protocol EmptySwiftProto {} } @objc(Conformance) @implementation extension ObjCClass { - // expected-error@-1 {{extension for category 'Conformance' should provide implementation for instance method 'requiredMethod2()'}} + // expected-error@-1 {{extension for category 'Conformance' does not provide all required implementations}} + // expected-note@-2 {{missing instance method 'requiredMethod2()'}} {{none}} + // expected-note@-3 {{add stub for missing '@implementation' requirement}} {{57-57=\n @objc(requiredMethod2)\n open func requiredMethod2() {\n <#code#>\n \}\n}} // no-error concerning 'optionalMethod2()' func requiredMethod1() {} @@ -437,7 +452,9 @@ protocol EmptySwiftProto {} } @objc(InvalidMembers) @implementation extension ObjCClass { - // expected-error@-1 {{extension for category 'InvalidMembers' should provide implementation for instance method 'unimplementedMember()'}} + // expected-error@-1 {{extension for category 'InvalidMembers' does not provide all required implementations}} + // expected-note@-2 {{missing instance method 'unimplementedMember()'}} {{none}} + // expected-note@-3 {{add stub for missing '@implementation' requirement}} {{60-60=\n @objc(unimplementedMember)\n open func unimplementedMember() {\n <#code#>\n \}\n}} func nonObjCMethod(_: EmptySwiftProto) { // expected-error@-1 {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}} diff --git a/test/decl/ext/objc_implementation_class_extension.swift b/test/decl/ext/objc_implementation_class_extension.swift index 123e459d824..b2db2672e7f 100644 --- a/test/decl/ext/objc_implementation_class_extension.swift +++ b/test/decl/ext/objc_implementation_class_extension.swift @@ -4,12 +4,14 @@ @_implementationOnly import objc_implementation_class_extension_internal @_objcImplementation extension ObjCClass { - // expected-warning@-1 {{extension for main class interface should provide implementation for instance method 'method(fromHeader2:)'}} - // expected-warning@-2 {{extension for main class interface should provide implementation for property 'propertyFromHeader2'}} - // expected-warning@-3 {{extension for main class interface should provide implementation for instance method 'otherModuleExtensionMethod(fromHeader2:)'}} - // expected-warning@-4 {{extension for main class interface should provide implementation for property 'otherModuleExtensionPropertyFromHeader2'}} - // expected-warning@-5 {{extension for main class interface should provide implementation for instance method 'extensionMethod(fromHeader2:)'}} - // expected-warning@-6 {{extension for main class interface should provide implementation for property 'extensionPropertyFromHeader2'}} + // expected-warning@-1 {{extension for main class interface does not provide all required implementations}} + // expected-note@-2 {{missing instance method 'method(fromHeader2:)'}} {{none}} + // expected-note@-3 {{missing property 'propertyFromHeader2'}} {{none}} + // expected-note@-4 {{missing instance method 'otherModuleExtensionMethod(fromHeader2:)'}} {{none}} + // expected-note@-5 {{missing property 'otherModuleExtensionPropertyFromHeader2'}} {{none}} + // expected-note@-6 {{missing instance method 'extensionMethod(fromHeader2:)'}} {{none}} + // expected-note@-7 {{missing property 'extensionPropertyFromHeader2'}} {{none}} + // expected-note@-8 {{add stubs for missing '@implementation' requirements}} {{43-43=\n @objc(methodFromHeader2:)\n open func method(fromHeader2 param: Int32) {\n <#code#>\n \}\n\n @objc(propertyFromHeader2)\n open var propertyFromHeader2: Int32\n\n @objc(otherModuleExtensionMethodFromHeader2:)\n open func otherModuleExtensionMethod(fromHeader2 param: Int32) {\n <#code#>\n \}\n\n @objc(otherModuleExtensionPropertyFromHeader2)\n open var otherModuleExtensionPropertyFromHeader2: Int32\n\n @objc(extensionMethodFromHeader2:)\n open func extensionMethod(fromHeader2 param: Int32) {\n <#code#>\n \}\n\n @objc(extensionPropertyFromHeader2)\n open var extensionPropertyFromHeader2: Int32\n}} @objc func method(fromHeader1: CInt) {} @objc private func method(fromHeader2: CInt) {} diff --git a/test/decl/ext/objc_implementation_conflicts.swift b/test/decl/ext/objc_implementation_conflicts.swift index 561051ade12..400764bd0c4 100644 --- a/test/decl/ext/objc_implementation_conflicts.swift +++ b/test/decl/ext/objc_implementation_conflicts.swift @@ -108,6 +108,8 @@ import objc_implementation_private get { return 1 } } + @objc let readonlyPropertyFromHeader7: CInt + @objc fileprivate var propertyNotFromHeader2: CInt // OK, provides a nonpublic but ObjC-compatible stored property @@ -229,6 +231,17 @@ import objc_implementation_private get { return 1 } set {} } + + @objc var categoryPropertyFromHeader5: CInt { + // OK, provides an implementation with a computed property + get { return 1 } + set {} + } + + @objc var categoryReadonlyPropertyFromHeader1: CInt { + // OK, provides an implementation with a computed property + get { return 1 } + } } @objc class SwiftClass {} diff --git a/test/decl/ext/objc_implementation_early_adopter.swift b/test/decl/ext/objc_implementation_early_adopter.swift index 223860de60e..2ee910d822c 100644 --- a/test/decl/ext/objc_implementation_early_adopter.swift +++ b/test/decl/ext/objc_implementation_early_adopter.swift @@ -5,16 +5,19 @@ protocol EmptySwiftProto {} +// expected-note@+1 {{previously implemented here}} @_objcImplementation extension ObjCClass: EmptySwiftProto, EmptyObjCProto { - // expected-note@-1 {{previously implemented here}} - // expected-warning@-2 {{extension for main class interface should provide implementation for instance method 'method(fromHeader4:)'}} - // expected-warning@-3 {{extension for main class interface should provide implementation for property 'propertyFromHeader9'}} - // FIXME: give better diagnostic expected-warning@-4 {{extension for main class interface should provide implementation for property 'propertyFromHeader8'}} - // FIXME: give better diagnostic expected-warning@-5 {{extension for main class interface should provide implementation for property 'propertyFromHeader7'}} - // FIXME: give better diagnostic expected-warning@-6 {{extension for main class interface should provide implementation for instance method 'method(fromHeader3:)'}} - // expected-warning@-7 {{'@objc @implementation' extension cannot add conformance to 'EmptySwiftProto'; add this conformance with an ordinary extension}} - // expected-warning@-8 {{'@objc @implementation' extension cannot add conformance to 'EmptyObjCProto'; add this conformance in the Objective-C header}} - // expected-warning@-9 {{extension for main class interface should provide implementation for instance method 'extensionMethod(fromHeader2:)'}} + // expected-warning@-1 {{'@objc @implementation' extension cannot add conformance to 'EmptySwiftProto'; add this conformance with an ordinary extension}} + // expected-warning@-2 {{'@objc @implementation' extension cannot add conformance to 'EmptyObjCProto'; add this conformance in the Objective-C header}} + // expected-warning@-3 {{extension for main class interface does not provide all required implementations; this will become an error after adopting '@implementation'}} + // expected-note@-4 {{missing instance method 'method(fromHeader4:)'}} {{none}} + // expected-note@-5 {{missing property 'propertyFromHeader9'}} {{none}} + // FIXME: give better diagnostic expected-note@-6 {{missing property 'propertyFromHeader8'}} {{none}} + // FIXME: give better diagnostic expected-note@-7 {{missing property 'propertyFromHeader7'}} {{none}} + // FIXME: give better diagnostic expected-note@-8 {{missing instance method 'method(fromHeader3:)'}} {{none}} + // expected-note@-9 {{missing instance method 'extensionMethod(fromHeader2:)'}} {{none}} + // expected-note@-10 {{missing property 'readonlyPropertyFromHeader7'}} + // expected-note@-11 {{add stubs for missing '@implementation' requirements}} {{76-76=\n @objc(methodFromHeader3:)\n open func method(fromHeader3 param: Int32) {\n <#code#>\n \}\n\n @objc(methodFromHeader4:)\n open func method(fromHeader4 param: Int32) {\n <#code#>\n \}\n\n @objc(propertyFromHeader7)\n open var propertyFromHeader7: Int32\n\n @objc(propertyFromHeader8)\n open var propertyFromHeader8: Int32\n\n @objc(propertyFromHeader9)\n open var propertyFromHeader9: Int32\n\n @objc(readonlyPropertyFromHeader7)\n open let readonlyPropertyFromHeader7: Int32\n\n @objc(extensionMethodFromHeader2:)\n open func extensionMethod(fromHeader2 param: Int32) {\n <#code#>\n \}\n}} func method(fromHeader1: CInt) { // OK, provides an implementation for the header's method. @@ -233,10 +236,14 @@ protocol EmptySwiftProto {} // expected-warning@-1 {{property 'rdar122280735' of type '(() -> ()) -> Void' does not match type '(@escaping () -> Void) -> Void' declared by the header}} } +// expected-note@+1 {{'PresentAdditions' previously declared here}} @_objcImplementation(PresentAdditions) extension ObjCClass { - // expected-note@-1 {{'PresentAdditions' previously declared here}} - // expected-warning@-2 {{extension for category 'PresentAdditions' should provide implementation for instance method 'categoryMethod(fromHeader4:)'; this will become an error after adopting '@implementation'}} - // FIXME: give better diagnostic expected-warning@-3 {{extension for category 'PresentAdditions' should provide implementation for instance method 'categoryMethod(fromHeader3:)'; this will become an error after adopting '@implementation'}} + // expected-warning@-1 {{extension for category 'PresentAdditions' does not provide all required implementations; this will become an error after adopting '@implementation'}} + // expected-note@-2 {{missing instance method 'categoryMethod(fromHeader4:)'}} {{none}} + // FIXME: give better diagnostic expected-note@-3 {{missing instance method 'categoryMethod(fromHeader3:)'}} {{none}} + // expected-note@-4 {{missing property 'categoryPropertyFromHeader5'}} {{none}} + // expected-note@-5 {{missing property 'categoryReadonlyPropertyFromHeader1'}} {{none}} + // expected-note@-6 {{add stubs for missing '@implementation' requirements}} {{61-61=\n @objc(categoryMethodFromHeader3:)\n open func categoryMethod(fromHeader3 param: Int32) {\n <#code#>\n \}\n\n @objc(categoryMethodFromHeader4:)\n open func categoryMethod(fromHeader4 param: Int32) {\n <#code#>\n \}\n\n @objc(categoryPropertyFromHeader5)\n open var categoryPropertyFromHeader5: Int32 {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n\n @objc(categoryReadonlyPropertyFromHeader1)\n open var categoryReadonlyPropertyFromHeader1: Int32 {\n <#code#>\n \}\n}} func method(fromHeader3: CInt) { // FIXME: should emit expected-DISABLED-error@-1 {{instance method 'method(fromHeader3:)' should be implemented in extension for main class interface, not category 'PresentAdditions'}} @@ -295,7 +302,9 @@ protocol EmptySwiftProto {} } @_objcImplementation(SwiftNameTests) extension ObjCClass { - // expected-warning@-1 {{extension for category 'SwiftNameTests' should provide implementation for instance method 'methodSwiftName6B()'; this will become an error after adopting '@implementation'}} + // expected-warning@-1 {{extension for category 'SwiftNameTests' does not provide all required implementations; this will become an error after adopting '@implementation'}} + // expected-note@-2 {{missing instance method 'methodSwiftName6B()'}} {{none}} + // expected-note@-3 {{add stub for missing '@implementation' requirement}} {{59-59=\n @objc(methodObjCName6B)\n open func methodSwiftName6B() {\n <#code#>\n \}\n}} func methodSwiftName1() { // expected-warning@-1 {{selector 'methodSwiftName1' for instance method 'methodSwiftName1()' not found in header; did you mean 'methodObjCName1'?; this will become an error after adopting '@implementation'}} {{3-3=@objc(methodObjCName1) }} @@ -363,6 +372,10 @@ protocol EmptySwiftProto {} } @_objcImplementation(Effects) extension ObjCClass { + // expected-warning@-1 {{extension for category 'Effects' does not provide all required implementations; this will become an error after adopting '@implementation'}} + // expected-note@-2 {{missing instance method 'doSomethingMissingAndAsynchronous()' or 'doSomethingMissingAndAsynchronous(completionHandler:)'}} + // expected-note@-3 {{add stub for missing '@implementation' requirement}} {{52-52=\n @objc(doSomethingMissingAndAsynchronousWithCompletionHandler:)\n open func doSomethingMissingAndAsynchronous() async throws -> Any {\n <#code#>\n \}\n}} + @available(SwiftStdlib 5.1, *) public func doSomethingAsynchronous() async throws -> Any { return self @@ -406,7 +419,9 @@ protocol EmptySwiftProto {} } @_objcImplementation(Conformance) extension ObjCClass { - // expected-warning@-1 {{extension for category 'Conformance' should provide implementation for instance method 'requiredMethod2()'; this will become an error after adopting '@implementation'}} + // expected-warning@-1 {{extension for category 'Conformance' does not provide all required implementations; this will become an error after adopting '@implementation'}} + // expected-note@-2 {{missing instance method 'requiredMethod2()'}} {{none}} + // expected-note@-3 {{add stub for missing '@implementation' requirement}} {{56-56=\n @objc(requiredMethod2)\n open func requiredMethod2() {\n <#code#>\n \}\n}} // no-error concerning 'optionalMethod2()' func requiredMethod1() {} @@ -437,7 +452,9 @@ protocol EmptySwiftProto {} } @_objcImplementation(InvalidMembers) extension ObjCClass { - // expected-warning@-1 {{extension for category 'InvalidMembers' should provide implementation for instance method 'unimplementedMember()'}} + // expected-warning@-1 {{extension for category 'InvalidMembers' does not provide all required implementations}} + // expected-note@-2 {{missing instance method 'unimplementedMember()'}} {{none}} + // expected-note@-3 {{add stub for missing '@implementation' requirement}} {{59-59=\n @objc(unimplementedMember)\n open func unimplementedMember() {\n <#code#>\n \}\n}} func nonObjCMethod(_: EmptySwiftProto) { // expected-warning@-1 {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}} diff --git a/test/decl/ext/objc_implementation_features.swift b/test/decl/ext/objc_implementation_features.swift index a3230928c5d..3d5343fa2f3 100644 --- a/test/decl/ext/objc_implementation_features.swift +++ b/test/decl/ext/objc_implementation_features.swift @@ -1,14 +1,15 @@ // REQUIRES: objc_interop -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck %s -import-objc-header %S/Inputs/objc_implementation.h 2>&1 | %FileCheck %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource) -typecheck %s -import-objc-header %S/Inputs/objc_implementation.h >%t.txt 2>&1 +// RUN: %FileCheck %s --input-file %t.txt // CHECK-DAG: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: warning: '@_objcImplementation' is deprecated; use '@implementation' instead @_objcImplementation(EmptyCategory) extension ObjCClass {} -// CHECK-DAG: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: warning: extension for main class interface should provide implementation for instance method 'subclassMethod(fromHeader1:)'; this will become an error after adopting '@implementation' +// CHECK-DAG: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: warning: extension for main class interface does not provide all required implementations; this will become an error after adopting '@implementation' // CHECK-NOT: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: warning: '@_objcImplementation' is deprecated; use '@implementation' instead @_objcImplementation extension ObjCSubclass {} -// CHECK-DAG: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: error: extension for main class interface should provide implementation for initializer 'init()'{{$}} +// CHECK-DAG: objc_implementation_features.swift:[[@LINE+2]]:{{[0-9]+}}: error: extension for main class interface does not provide all required implementations{{$}} // CHECK-NOT: objc_implementation_features.swift:[[@LINE+1]]:{{[0-9]+}}: error: 'implementation' attribute is only valid when experimental feature ObjCImplementation is enabled @objc @implementation extension ObjCBasicInitClass {} diff --git a/test/decl/protocol/conforms/fixit_stub.swift b/test/decl/protocol/conforms/fixit_stub.swift index 22c26017e7e..a799b1dc4f5 100644 --- a/test/decl/protocol/conforms/fixit_stub.swift +++ b/test/decl/protocol/conforms/fixit_stub.swift @@ -1,17 +1,20 @@ // RUN: %target-typecheck-verify-swift protocol Protocol1 { - func foo(arg1: Int, arg2: String) -> String // expected-note{{protocol requires function 'foo(arg1:arg2:)' with type '(Int, String) -> String'}} - func bar() throws -> String // expected-note{{protocol requires function 'bar()' with type '() throws -> String'}} - func generic(t: T) // expected-note{{protocol requires function 'generic(t:)' with type ' (t: T) -> ()'}} - init(arg: Int) // expected-note{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'}} - var baz: Int { get } // expected-note{{protocol requires property 'baz' with type 'Int'}} - var baz2: Int { get set } // expected-note{{protocol requires property 'baz2' with type 'Int'}} - subscript(arg: Int) -> String { get } //expected-note{{rotocol requires subscript with type '(Int) -> String'}} - subscript(arg1: Int, arg2: Int) -> String { get set } //expected-note{{protocol requires subscript with type '(Int, Int) -> String'}} + func foo(arg1: Int, arg2: String) -> String // expected-note 2{{protocol requires function 'foo(arg1:arg2:)' with type '(Int, String) -> String'}} + func bar() throws -> String // expected-note 2{{protocol requires function 'bar()' with type '() throws -> String'}} + func generic(t: T) // expected-note 2{{protocol requires function 'generic(t:)' with type ' (t: T) -> ()'}} + init(arg: Int) // expected-note 2{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'}} + var baz: Int { get } // expected-note 2{{protocol requires property 'baz' with type 'Int'}} + var baz2: Int { get set } // expected-note 2{{protocol requires property 'baz2' with type 'Int'}} + subscript(arg: Int) -> String { get } //expected-note 2{{rotocol requires subscript with type '(Int) -> String'}} + subscript(arg1: Int, arg2: Int) -> String { get set } //expected-note 2{{protocol requires subscript with type '(Int, Int) -> String'}} } -class Adopter: Protocol1 { // expected-error{{type 'Adopter' does not conform to protocol 'Protocol1'}} expected-note {{add stubs for conformance}} {{27-27=\n func foo(arg1: Int, arg2: String) -> String {\n <#code#>\n \}\n\n func bar() throws -> String {\n <#code#>\n \}\n\n func generic(t: T) {\n <#code#>\n \}\n\n required init(arg: Int) {\n <#code#>\n \}\n\n var baz: Int\n\n var baz2: Int\n\n subscript(arg: Int) -> String {\n <#code#>\n \}\n\n subscript(arg1: Int, arg2: Int) -> String {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}} +class Adopter: Protocol1 { // expected-error{{type 'Adopter' does not conform to protocol 'Protocol1'}} expected-note {{add stubs for conformance}} {{27-27=\n func foo(arg1: Int, arg2: String) -> String {\n <#code#>\n \}\n\n func bar() throws -> String {\n <#code#>\n \}\n\n func generic(t: T) {\n <#code#>\n \}\n\n required init(arg: Int) {\n <#code#>\n \}\n\n let baz: Int\n\n var baz2: Int\n\n subscript(arg: Int) -> String {\n <#code#>\n \}\n\n subscript(arg1: Int, arg2: Int) -> String {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}} +} + +enum EnumAdopter: Protocol1 { // expected-error{{type 'EnumAdopter' does not conform to protocol 'Protocol1'}} expected-note {{add stubs for conformance}} {{30-30=\n func foo(arg1: Int, arg2: String) -> String {\n <#code#>\n \}\n\n func bar() throws -> String {\n <#code#>\n \}\n\n func generic(t: T) {\n <#code#>\n \}\n\n init(arg: Int) {\n <#code#>\n \}\n\n var baz: Int {\n <#code#>\n \}\n\n var baz2: Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n\n subscript(arg: Int) -> String {\n <#code#>\n \}\n\n subscript(arg1: Int, arg2: Int) -> String {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}} } diff --git a/test/decl/protocol/conforms/fixit_stub_2.swift b/test/decl/protocol/conforms/fixit_stub_2.swift index 35665e29948..1e6f5ecc178 100644 --- a/test/decl/protocol/conforms/fixit_stub_2.swift +++ b/test/decl/protocol/conforms/fixit_stub_2.swift @@ -81,10 +81,10 @@ struct Struct1: MutabilityProto { // expected-error{{type 'Struct1' does not con import fixit_stub_mutability_proto_module -class Class2: ExternalMutabilityProto { // expected-error{{type 'Class2' does not conform to protocol 'ExternalMutabilityProto'}} expected-note{{add stubs for conformance}} {{40-40=\n func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n get {\n <#code#>\n \}\n set(newValue) {\n <#code#>\n \}\n \}\n}} +class Class2: ExternalMutabilityProto { // expected-error{{type 'Class2' does not conform to protocol 'ExternalMutabilityProto'}} expected-note{{add stubs for conformance}} {{40-40=\n func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}} } -struct Struct2: ExternalMutabilityProto { // expected-error{{type 'Struct2' does not conform to protocol 'ExternalMutabilityProto'}} expected-note{{add stubs for conformance}} {{42-42=\n mutating func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n mutating get {\n <#code#>\n \}\n nonmutating set(newValue) {\n <#code#>\n \}\n \}\n}} +struct Struct2: ExternalMutabilityProto { // expected-error{{type 'Struct2' does not conform to protocol 'ExternalMutabilityProto'}} expected-note{{add stubs for conformance}} {{42-42=\n mutating func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n mutating get {\n <#code#>\n \}\n nonmutating set {\n <#code#>\n \}\n \}\n}} } protocol PropertyMutabilityProto { diff --git a/test/decl/protocol/fixits_missing_protocols_in_context.swift b/test/decl/protocol/fixits_missing_protocols_in_context.swift index b0b13d05909..8e995678286 100644 --- a/test/decl/protocol/fixits_missing_protocols_in_context.swift +++ b/test/decl/protocol/fixits_missing_protocols_in_context.swift @@ -13,7 +13,7 @@ class C { func assign() { p = self // expected-error@-1 {{cannot assign value of type 'C' to type '(any P)?'}} - // expected-note@-2 {{add missing conformance to 'P' to class 'C'}} {{-4:8-8=: P}} {{-4:10-10=\n func method() {\n <#code#>\n \}\n\n var property: Int\n}} + // expected-note@-2 {{add missing conformance to 'P' to class 'C'}} {{-4:8-8=: P}} {{-4:10-10=\n func method() {\n <#code#>\n \}\n\n let property: Int\n}} } } @@ -25,7 +25,7 @@ class C1 { func assign() { p = self // expected-error@-1 {{cannot assign value of type 'C1' to type '(any P)?'}} - // expected-note@-2 {{add missing conformance to 'P' to class 'C1'}} {{-4:9-9=: P}} {{-4:11-11=\n var property: Int\n}} + // expected-note@-2 {{add missing conformance to 'P' to class 'C1'}} {{-4:9-9=: P}} {{-4:11-11=\n let property: Int\n}} } func method() {} diff --git a/test/embedded/concurrency-modules.swift b/test/embedded/concurrency-modules.swift index aad6cfd71e2..2fd27c72ad3 100644 --- a/test/embedded/concurrency-modules.swift +++ b/test/embedded/concurrency-modules.swift @@ -8,6 +8,7 @@ // REQUIRES: swift_in_compiler // REQUIRES: executable_test +// REQUIRES: optimized_stdlib // REQUIRES: OS=macosx // REQUIRES: swift_feature_Embedded diff --git a/test/embedded/dependencies-concurrency-custom-executor.swift b/test/embedded/dependencies-concurrency-custom-executor.swift index c5e73a7a1af..40cd66b9eb8 100644 --- a/test/embedded/dependencies-concurrency-custom-executor.swift +++ b/test/embedded/dependencies-concurrency-custom-executor.swift @@ -18,6 +18,7 @@ // DEP: _exit // DEP: _free // DEP: _malloc +// DEP: _memcpy // DEP: _memmove // DEP: _memset // DEP: _memset_s diff --git a/test/embedded/dependencies-concurrency.swift b/test/embedded/dependencies-concurrency.swift index 2c0491b1e7f..1bbba2f7a4a 100644 --- a/test/embedded/dependencies-concurrency.swift +++ b/test/embedded/dependencies-concurrency.swift @@ -18,6 +18,7 @@ // DEP: _exit // DEP: _free // DEP: _malloc +// DEP: _memcpy // DEP: _memmove // DEP: _memset // DEP: _memset_s diff --git a/test/embedded/dependencies-concurrency2.swift b/test/embedded/dependencies-concurrency2.swift index 657a586b50f..191d4fce006 100644 --- a/test/embedded/dependencies-concurrency2.swift +++ b/test/embedded/dependencies-concurrency2.swift @@ -16,6 +16,7 @@ // DEP: _exit // DEP: _free // DEP: _malloc +// DEP: _memcpy // DEP: _memmove // DEP: _memset // DEP: _memset_s diff --git a/test/stdlib/Span/InlineSpanProperties.swift b/test/stdlib/Span/InlineSpanProperties.swift index 1f040bd5296..252d2990e9f 100644 --- a/test/stdlib/Span/InlineSpanProperties.swift +++ b/test/stdlib/Span/InlineSpanProperties.swift @@ -23,7 +23,6 @@ var suite = TestSuite("Span properties backed by inline storage") defer { runAllTests() } suite.test("CollectionOfOne.span property") -.skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" @@ -35,7 +34,6 @@ suite.test("CollectionOfOne.span property") let u = Array(s.utf8) let c = CollectionOfOne(consume s) s = "" - expectCrashLater() let span = c.span expectEqual(span.count, 1) let v = Array(span[0].utf8) @@ -43,7 +41,6 @@ suite.test("CollectionOfOne.span property") } suite.test("CollectionOfOne.span property (simple)") -.skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" @@ -52,7 +49,6 @@ suite.test("CollectionOfOne.span property (simple)") guard #available(SwiftStdlib 6.2, *) else { return } let c = CollectionOfOne(Int.random(in: 0..<100000)) - expectCrashLater() let span = c.span expectEqual(span.count, c.indices.count) expectEqual(span[0], c[0]) @@ -63,7 +59,6 @@ struct Padded: BitwiseCopyable { } suite.test("CollectionOfOne.span stride test") -.skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" @@ -72,14 +67,25 @@ suite.test("CollectionOfOne.span stride test") guard #available(SwiftStdlib 6.2, *) else { return } let c = CollectionOfOne(Padded(storage: (-1, 1))) - expectCrashLater() let span = c.span let bytes = span.bytes expectEqual(bytes.byteCount, MemoryLayout.size(ofValue: c)) } +suite.test("CollectionOfOne.mutableSpan property (simple)") +.require(.stdlib_6_2).code { + guard #available(SwiftStdlib 6.2, *) else { return } + + var c = CollectionOfOne(Int.random(in: 0..<100000)) + expectEqual(c.count, 1) + var span = c.mutableSpan + expectEqual(span.count, 1) + span[0] = Int.random(in: .min..<0) + let r = span[0] + expectEqual(c[0], r) +} + suite.test("InlineArray.span property") -.skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" @@ -89,7 +95,6 @@ suite.test("InlineArray.span property") var s = InlineArray<5, Int>(repeating: 0) s[3] = .random(in: 0..<1000) - expectCrashLater() let span = s.span expectEqual(span.count, s.count) for i in s.indices { @@ -98,7 +103,6 @@ suite.test("InlineArray.span property") } suite.test("InlineArray.span property (String)") -.skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" @@ -108,10 +112,37 @@ suite.test("InlineArray.span property (String)") var s = InlineArray<5, String>(repeating: "0") s[3] = String(Int.random(in: 0..<1000)) - expectCrashLater() let span = s.span expectEqual(span.count, s.count) for i in s.indices { expectEqual(span[i], s[i]) } } + +suite.test("InlineArray.mutableSpan property") +.require(.stdlib_6_2).code +{ + guard #available(SwiftStdlib 6.2, *) else { return } + + var v = InlineArray<5, Int>(repeating: 0) + let c = v.count + var span = v.mutableSpan + expectEqual(span.count, c) + span[3] = Int.random(in: .min..<0) + let r = span[3] + expectEqual(v[3], r) +} + +suite.test("InlineArray.mutableSpan property (String)") +.require(.stdlib_6_2).code +{ + guard #available(SwiftStdlib 6.2, *) else { return } + + var v = InlineArray<5, String>(repeating: "0") + let c = v.count + var span = v.mutableSpan + expectEqual(span.count, c) + span[3] = String(repeating: "0", count: Int.random(in: 100..<500)) + let s = span[3] + expectTrue(s._isIdentical(to: v[3])) +} diff --git a/test/stdlib/Span/MutableRawSpanTests.swift b/test/stdlib/Span/MutableRawSpanTests.swift index 51b9cbcb302..e64558bd0ef 100644 --- a/test/stdlib/Span/MutableRawSpanTests.swift +++ b/test/stdlib/Span/MutableRawSpanTests.swift @@ -36,6 +36,38 @@ suite.test("Basic Initializer") } } +private struct Padded: BitwiseCopyable { + var storage: (Int64, Int8) +} + +suite.test("Initializer from MutableSpan") +.require(.stdlib_6_2).code { + guard #available(SwiftStdlib 6.2, *) else { return } + + var array = [0, 1, 2].map({ Padded(storage: (Int64($0), Int8($0))) }) + array.withUnsafeMutableBufferPointer { + var span = MutableSpan(_unsafeElements: $0) + var rawSpan = MutableRawSpan(_elements: &span) + + expectEqual(rawSpan.byteCount, $0.count * MemoryLayout.stride) + + rawSpan.storeBytes(of: 15, as: Int64.self) + } + expectEqual(array[0].storage.0, 15) + + var slice = array.prefix(1) + slice.withUnsafeMutableBufferPointer { + expectEqual($0.count, 1) + var span = MutableSpan(_unsafeElements: $0) + var rawSpan = MutableRawSpan(_elements: &span) + + expectEqual(rawSpan.byteCount, MemoryLayout.size) + + rawSpan.storeBytes(of: 3, as: Int64.self) + } + expectEqual(slice[0].storage.0, 3) +} + suite.test("isEmpty property") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, diff --git a/test/stdlib/Span/MutableSpanTests.swift b/test/stdlib/Span/MutableSpanTests.swift index 8c962dfdfc9..944c049c5f1 100644 --- a/test/stdlib/Span/MutableSpanTests.swift +++ b/test/stdlib/Span/MutableSpanTests.swift @@ -122,9 +122,30 @@ suite.test("RawSpan from MutableSpan") let span = MutableSpan(_unsafeStart: p, count: c) let bytes = span.bytes expectEqual(bytes.byteCount, count*MemoryLayout.stride) + let v = bytes.unsafeLoad( + fromByteOffset: MemoryLayout.stride, as: Int.self + ) + expectEqual(v, 1) } } +suite.test("MutableRawSpan from MutableSpan") +.require(.stdlib_6_2).code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let count = 4 + var array = Array(0...stride) + bytes.storeBytes(of: 1, as: Int.self) + } + expectEqual(array[0], 1) +} + suite.test("indices property") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, diff --git a/test/stdlib/UTF8EncodingErrorTests.swift b/test/stdlib/UTF8EncodingErrorTests.swift new file mode 100644 index 00000000000..de6893a1eba --- /dev/null +++ b/test/stdlib/UTF8EncodingErrorTests.swift @@ -0,0 +1,295 @@ +// RUN: %target-run-stdlib-swift %S/Inputs/ + +// REQUIRES: executable_test + +// FIXME: this test is currently broken + +import Swift +import StdlibUnittest + +var suite = TestSuite("UTF8.ValidationError") +defer { runAllTests() } + +@available(SwiftStdlib 6.2, *) +extension Array { + func withSpan(_ f: (Span) throws -> R) rethrows -> R { + try self.withUnsafeBufferPointer { + try f(Span(_unsafeElements: $0)) + } + } +} + + +extension Range { + func _offset(by start: Int) -> Range { + start + lowerBound ..< start + upperBound + } +} + +@available(SwiftStdlib 6.2, *) +private struct ValidationError { + var error: UTF8.ValidationError + + // When fetching all errors, we'll get the error kind given. When + // slicing in order to get the next error (e.g. + // `UTF8Span.init(validating:))`, we'll get `.unexpectedContinuation`. + var errorStart: Bool + + + init( + _ error: UTF8.ValidationError, + errorStart: Bool + ) { + self.error = error + self.errorStart = errorStart + } + + public static func unexpectedContinuationByte( + at i: Int, errorStart: Bool = true + ) -> Self { + Self(UTF8.ValidationError(.unexpectedContinuationByte, at: i), errorStart: errorStart) + } + + public static func surrogateCodePointByte( + at i: Int, errorStart: Bool = true + ) -> Self { + Self(UTF8.ValidationError(.surrogateCodePointByte, at: i), errorStart: errorStart) + } + + public static func invalidNonSurrogateCodePointByte( + at i: Int, errorStart: Bool = true + ) -> Self { + Self(UTF8.ValidationError(.invalidNonSurrogateCodePointByte, at: i), errorStart: errorStart) + } + + public static func overlongEncodingByte( + at i: Int, errorStart: Bool = true + ) -> Self { + Self(UTF8.ValidationError(.overlongEncodingByte, at: i), errorStart: errorStart) + } + + public static func truncatedScalar( + _ range: Range, errorStart: Bool = true + ) -> Self { + Self(UTF8.ValidationError(.truncatedScalar, range), errorStart: errorStart) + } +} + + +@available(SwiftStdlib 6.2, *) +private struct ValidationTestCase { + var bytes: [UInt8] + + // When fetching all errors, we'll get the error kind given. When + // slicing in order to get the next error (e.g. + // `UTF8Span.init(validating:))`, we'll get `.unexpectedContinuation`. + var errors: [ValidationError] + + var loc: SourceLocStack + + init( + _ bytes: [UInt8], + file: String = #file, + line: UInt = #line, + _ errors: [ValidationError] + ) { + self.bytes = bytes + self.errors = errors + self.loc = .init(SourceLoc(file, line)) + } + + func fetchError( + at i: Int, wasSliced: Bool + ) -> UTF8.ValidationError { + let err = errors[i] + if wasSliced && !err.errorStart { + return .init(.unexpectedContinuationByte, err.error.byteOffsets) + } + return err.error + } + + func expect( + _ lhs: T, + _ rhs: T, + file: String = #file, + line: UInt = #line + ) { + expectEqual( + lhs, + rhs, + stackTrace: loc.withCurrentLoc(file: file, line: line)) + } + func fail( + _ message: String, + file: String = #file, + line: UInt = #line + ) { + expectationFailure( + message, + trace: "", + stackTrace: loc.with(.init(file, line))) + } + + /// Test UTF8._checkAllErrors(), which matches directly against + /// the provided expected-errors. + func testAllErrors() { + let caughtErrors = Array(UTF8._checkAllErrors(bytes)) + for i in 0.., + _ file: String = #file, line: UInt = #line, + _ errors: ValidationError... + ) { + ValidationTestCase( + bytes, file: file, line: line, errors + ).run() + } + + // Valid string + // test(Array("abcde\u{301}f😀🇺🇸🧟‍♀️🧟‍♀️".utf8), []) + + // Bad URL + // test( + // Array("http://servername/scripts/..".utf8) + // + [0xC0, 0xAF] + // + Array("../winnt/system32/cmd.exe".utf8), + // [.overlongEncodingByte(at: 28), // C0 + // .overlongEncodingByte(at: 29, errorStart: false), // AF + // ]) + + // test( + // [0xC0, 0xAF, 0xE0, 0x80, 0xBF, 0xF0, 0x81, 0x82, 0x41], + // [.overlongEncodingByte(at: 0), // C0 + // .overlongEncodingByte(at: 1, errorStart: false), // AF + // .overlongEncodingByte(at: 2), // E0 + // .overlongEncodingByte(at: 3, errorStart: false), // 80 + // .overlongEncodingByte(at: 4, errorStart: false), // BF + // .overlongEncodingByte(at: 5), // F0 + // .overlongEncodingByte(at: 6, errorStart: false), // 81 + // .overlongEncodingByte(at: 7, errorStart: false), // 82 + // ]) + // test( + // [0x41, 0xC0, 0xAF, 0x41, 0xF4, 0x80, 0x80, 0x41], + // [.overlongEncodingByte(at: 1), // C0 + // .overlongEncodingByte(at: 2, errorStart: false), // AF + // .truncatedScalar(4...6), // F4 80 80 + // ]) + // test( + // [0xED, 0xAF, 0x41], + // [.surrogateCodePointByte(at: 0), // ED + // .surrogateCodePointByte(at: 1, errorStart: false), // AF + // ]) + // test( + // [0xED, 0xA0, 0x80, 0xED, 0xBF, 0xBF, 0xED, 0xAF, 0x41], + // [.surrogateCodePointByte(at: 0), // ED + // .surrogateCodePointByte(at: 1, errorStart: false), // A0 + // .surrogateCodePointByte(at: 2, errorStart: false), // 80 + // .surrogateCodePointByte(at: 3), // ED + // .surrogateCodePointByte(at: 4, errorStart: false), // BF + // .surrogateCodePointByte(at: 5, errorStart: false), // BF + // .surrogateCodePointByte(at: 6), // ED + // .surrogateCodePointByte(at: 7, errorStart: false), // AF + // ]) + // test( + // [0xF4, 0x91, 0x92, 0x93, 0xFF, 0x41, 0x80, 0xBF, 0x42], + // [.invalidNonSurrogateCodePointByte(at: 0), // F4 + // .invalidNonSurrogateCodePointByte(at: 1, errorStart: false), // 91 + // .invalidNonSurrogateCodePointByte(at: 2, errorStart: false), // 92 + // .invalidNonSurrogateCodePointByte(at: 3, errorStart: false), // 93 + // .invalidNonSurrogateCodePointByte(at: 4), // FF + // .unexpectedContinuationByte(at: 6), // 80 + // .unexpectedContinuationByte(at: 7), // BF + // ]) + // test( + // [0xE1, 0x80, 0xE2, 0xF0, 0x91, 0x92, 0xF1, 0xBF, 0x41], + // [.truncatedScalar(0...1), // E1 80 + // .truncatedScalar(2...2), // E2 + // .truncatedScalar(3...5), // F0 91 92 + // .truncatedScalar(6...7), // F1 BF + // ]) + // test( + // [0xE0, 0x81, 0x80], + // [.overlongEncodingByte(at: 0), // E0 + // .overlongEncodingByte(at: 1, errorStart: false), // 81 + // .overlongEncodingByte(at: 2, errorStart: false), // 80 + // ]) + } +} diff --git a/test/stdlib/UTF8SpanIteratorTests.swift b/test/stdlib/UTF8SpanIteratorTests.swift new file mode 100644 index 00000000000..8b794714704 --- /dev/null +++ b/test/stdlib/UTF8SpanIteratorTests.swift @@ -0,0 +1,279 @@ +// RUN: %target-run-stdlib-swift(-enable-experimental-feature LifetimeDependence) %S/Inputs/ +// REQUIRES: swift_feature_LifetimeDependence +// REQUIRES: executable_test + +import Swift +import StdlibUnittest + +var suite = TestSuite("UTF8SpanIterator") +defer { runAllTests() } + +@available(SwiftStdlib 6.2, *) +extension Array { + func withSpan(_ f: (Span) throws -> R) rethrows -> R { + try self.withUnsafeBufferPointer { + try f(Span(_unsafeElements: $0)) + } + } +} + +@available(SwiftStdlib 6.2, *) +extension UTF8Span { + func withSpan(_ f: (Span) throws -> R) rethrows -> R { + try self._withUnsafeBufferPointer { + try f(Span(_unsafeElements: $0)) + } + } +} + + +// +@available(SwiftStdlib 6.2, *) +struct ContentEquivalenceTestCase { + var str: String + var loc: SourceLocStack +} + +@available(SwiftStdlib 6.2, *) +extension ContentEquivalenceTestCase { + func expectStart( + _ scalars: inout UTF8Span.UnicodeScalarIterator + ) { + let firstScalar = str.unicodeScalars.first + expectEqual(0, scalars.currentCodeUnitOffset, stackTrace: loc) + expectNil(scalars.previous(), stackTrace: loc) + expectEqual(firstScalar, scalars.next(), stackTrace: loc) + expectEqual(firstScalar, scalars.previous(), stackTrace: loc) + expectNil(scalars.previous(), stackTrace: loc) + } + + func expectEnd( + _ scalars: inout UTF8Span.UnicodeScalarIterator + ) { + let lastScalar = str.unicodeScalars.last + expectEqual(scalars.currentCodeUnitOffset, scalars.codeUnits.count, stackTrace: loc) + expectNil(scalars.next(), stackTrace: loc) + expectEqual(lastScalar, scalars.previous(), stackTrace: loc) + expectEqual(lastScalar, scalars.next(), stackTrace: loc) + expectNil(scalars.next(), stackTrace: loc) + } + + func expectStart( + _ chars: inout UTF8Span.CharacterIterator + ) { + let firstChar = str.first + expectEqual(0, chars.currentCodeUnitOffset, stackTrace: loc) + expectNil(chars.previous(), stackTrace: loc) + expectEqual(firstChar, chars.next(), stackTrace: loc) + expectEqual(firstChar, chars.previous(), stackTrace: loc) + expectNil(chars.previous(), stackTrace: loc) + } + + func expectEnd( + _ chars: inout UTF8Span.CharacterIterator + ) { + let lastChar = str.last + expectEqual(chars.currentCodeUnitOffset, chars.codeUnits.count, stackTrace: loc) + expectNil(chars.next(), stackTrace: loc) + expectEqual(lastChar, chars.previous(), stackTrace: loc) + expectEqual(lastChar, chars.next(), stackTrace: loc) + expectNil(chars.next(), stackTrace: loc) + } + + + func withUTF8Span(_ f: (UTF8Span) throws -> R) rethrows -> R { + try Array(str.utf8).withSpan { span in + try f(try! UTF8Span(validating: span)) + } + } +} + +@available(SwiftStdlib 6.2, *) +extension ContentEquivalenceTestCase { + func testBytes() { + let otherBytes = Array((str+"abc").utf8) + + withUTF8Span { utf8Span in + utf8Span._withUnsafeBufferPointer { + expectEqualSequence(str.utf8, $0, stackTrace: loc) + } + } + + // NOTE: There's a slight jarring due to not having the same + // iterators for code units + } + + func testScalars() { + withUTF8Span { utf8Span in + // Test forwards + var utf8SpanIter = utf8Span.makeUnicodeScalarIterator() + var stringIter = str.unicodeScalars.makeIterator() + while let scalar = utf8SpanIter.next() { + expectEqual(scalar, stringIter.next(), stackTrace: loc) + } + expectNil(stringIter.next(), stackTrace: loc) + expectEnd(&utf8SpanIter) + + // Test backwards + var stringRevIter = str.unicodeScalars.reversed().makeIterator() + while let scalar = utf8SpanIter.previous() { + expectEqual(scalar, stringRevIter.next(), stackTrace: loc) + } + expectNil(stringRevIter.next(), stackTrace: loc) + expectStart(&utf8SpanIter) + + let numElements = str.unicodeScalars.count + let lastElement = str.unicodeScalars.last + let firstElement = str.unicodeScalars.first + + expectEqual(numElements, utf8SpanIter.skipForward(by: Int.max)) + expectEnd(&utf8SpanIter) + expectEqual(numElements, utf8SpanIter.skipBack(by: Int.max)) + expectStart(&utf8SpanIter) + expectEqual(numElements, utf8SpanIter.skipForward(by: numElements)) + expectEnd(&utf8SpanIter) + expectEqual(numElements, utf8SpanIter.skipBack(by: numElements)) + expectStart(&utf8SpanIter) + + if numElements > 0 { + expectStart(&utf8SpanIter) + expectEqual(numElements-1, utf8SpanIter.skipForward(by: numElements-1)) + expectEqual(lastElement, utf8SpanIter.next()) + expectEnd(&utf8SpanIter) + + expectEqual(numElements-1, utf8SpanIter.skipBack(by: numElements-1)) + expectEqual(firstElement, utf8SpanIter.previous()) + expectStart(&utf8SpanIter) + } + + // TODO: test reset variants + // TODO: test prefix/suffix + + } + } + + func testCharacters() { + withUTF8Span { utf8Span in + // Test forwards + var utf8SpanIter = utf8Span.makeCharacterIterator() + var stringIter = str.makeIterator() + while let char = utf8SpanIter.next() { + expectEqual(char, stringIter.next(), stackTrace: loc) + } + expectNil(stringIter.next(), stackTrace: loc) + expectEnd(&utf8SpanIter) + + // Test backwards + var stringRevIter = str.reversed().makeIterator() + while let char = utf8SpanIter.previous() { + expectEqual(char, stringRevIter.next(), stackTrace: loc) + } + expectNil(stringRevIter.next(), stackTrace: loc) + expectStart(&utf8SpanIter) + + let numElements = str.count + let lastElement = str.last + let firstElement = str.first + + expectEqual(numElements, utf8SpanIter.skipForward(by: Int.max)) + expectEnd(&utf8SpanIter) + expectEqual(numElements, utf8SpanIter.skipBack(by: Int.max)) + expectStart(&utf8SpanIter) + expectEqual(numElements, utf8SpanIter.skipForward(by: numElements)) + expectEnd(&utf8SpanIter) + expectEqual(numElements, utf8SpanIter.skipBack(by: numElements)) + expectStart(&utf8SpanIter) + + if numElements > 0 { + expectStart(&utf8SpanIter) + expectEqual(numElements-1, utf8SpanIter.skipForward(by: numElements-1)) + expectEqual(lastElement, utf8SpanIter.next()) + expectEnd(&utf8SpanIter) + + expectEqual(numElements-1, utf8SpanIter.skipBack(by: numElements-1)) + expectEqual(firstElement, utf8SpanIter.previous()) + expectStart(&utf8SpanIter) + } + + // TODO: test reset variants + // TODO: test prefix/suffix + } + } + + func run() { + testBytes() + testScalars() + testCharacters() + + // TODO: test grapheme break iterator + } + +} + +if #available(SwiftStdlib 6.2, *) { + suite.test("UTF8Span/iterators") { + func test( + _ s: String, + file: String = #file, + line: UInt = #line + ) { + // print("testing: \(s)") + let t = ContentEquivalenceTestCase( + str: s, loc: .init(SourceLoc(file, line))) + t.run() + } + + test("") + test("a") + test("á") + test("a\u{301}") + test("🧟‍♀️") + test("abc") + test("abcde\u{301}") + test("abéÏ𓀀") + test("012345678901234567890") + test("abéÏ012345678901234567890𓀀") + test("😀😃🤢🤮👩🏿‍🎤🧛🏻‍♂️🧛🏻‍♂️👩‍👩‍👦‍👦") + test("defghijklmnopqrstuvwxyz") + test("ab🧟‍♀️de\u{301}bytés") + test("ab🧟‍♀️de\u{301}🧟‍♀️") + test("ab🧟‍♀️de🧟‍♀️\u{301}") + } +} + +// @available(SwiftStdlib 6.2, *) +// extension UTF8Span { +// func splitOffASCIIPrefix() -> (UTF8Span, UTF8Span) { +// if isKnownASCII { +// return (self, .init()) +// } +// var splitPoint = 0 +// while splitPoint < codeUnits.count && codeUnits[unchecked: split] < 0x80 { +// splitPoint += 1 +// } + +// } +// } + +if #available(SwiftStdlib 6.2, *) { + suite.test("UTF8Span/whatever") { + // var badURLBytes: [UInt8] = [] + // badURLBytes.append(contentsOf: "http://servername/scripts/..".utf8) + + // // Invalid overlong encoding of "/" + // badURLBytes.append(contentsOf: [0xC0, 0xAF]) + + // badURLBytes.append(contentsOf: "../winnt/system32/cmd.exe".utf8) + + // // try! UTF8Span(validating: badURLBytes.span) + + // badURLBytes.withSpan { + // try! UTF8Span(validating: $0) + // } + + + + } +} + + diff --git a/test/stdlib/UTF8SpanQueriesComparisons.swift b/test/stdlib/UTF8SpanQueriesComparisons.swift new file mode 100644 index 00000000000..b37bfbc2024 --- /dev/null +++ b/test/stdlib/UTF8SpanQueriesComparisons.swift @@ -0,0 +1,278 @@ +// RUN: %target-run-stdlib-swift %S/Inputs/ + +// REQUIRES: executable_test + +import Swift +import StdlibUnittest + +@available(SwiftStdlib 6.2, *) +extension UTF8Span { + static func ~=(_ lhs: StaticString, _ rhs: UTF8Span) -> Bool { + return lhs.withUTF8Buffer { str in + rhs._withUnsafeBufferPointer { span in + str.elementsEqual(span) + } + } + } +} + +var suite = TestSuite("UTF8SpanQueriesComparisons") +defer { runAllTests() } + +@available(SwiftStdlib 6.2, *) +extension Array where Element == UInt8 { + func withSpan(_ f: (Span) throws -> R) rethrows -> R { + try self.withUnsafeBufferPointer { + try f(Span(_unsafeElements: $0)) + } + } + func withUTF8Span(_ f: (UTF8Span) throws -> R) rethrows -> R { + try self.withSpan { span in + try f(try! UTF8Span(validating: span)) + } + } +} + +if #available(SwiftStdlib 6.2, *) { + suite.test("UTF8Span/tilde equals") { + Array("abcdefg".utf8).withUTF8Span { utf8Span in + switch utf8Span { + case "def": + expectationFailure( + "unexpected pattern match", + trace: "", + stackTrace: SourceLocStack().withCurrentLoc()) + case "abcdef": + expectationFailure( + "unexpected pattern match", + trace: "", + stackTrace: SourceLocStack().withCurrentLoc()) + case "abcdefg ": + expectationFailure( + "unexpected pattern match", + trace: "", + stackTrace: SourceLocStack().withCurrentLoc()) + case "abcdefg\0": + expectationFailure( + "unexpected pattern match", + trace: "", + stackTrace: SourceLocStack().withCurrentLoc()) + case "abcdefg": + break + default: + expectationFailure( + "expected a pattern match", + trace: "", + stackTrace: SourceLocStack().withCurrentLoc()) + } + } + } + + suite.test("UTF8Span/Sequence equal") { + // // A string and its canonical equivalent + // let testCases: [(String, String?)] = [ + // ("abdefg", nil) + // ("café", "cafe\u{301}") + // ] + } + + suite.test("UTF8Span/isKnownASCII") { + let tests: [(String, Bool)] = [ + ("abc", true), + ("abcdefghil1235@#% _/.sladfj234 ", true), + ("abcdefghil1\u{80}sladfj234 ", false), + ] + + for (test, expected) in tests { + Array(test.utf8).withUTF8Span { + expectEqual(expected, $0.isKnownASCII) + } + } + } + + suite.test("UTF8Span/isKnownNFC") { + enum Normalness { + case known + case quickCheck + case fullCheck + case notNFC + } + + let nfcQCNo = "\u{0374}" + let nfcQCYes = "\u{0374}" + + let tests: [(String, Normalness)] = [ + ("abc", .known), + ("abcdefghil123567890", .known), + ("abcdefghil1\u{299}123345678 ", .quickCheck), + ("abc日曜日xyz", .quickCheck), + ("abcde日曜日\u{301}", .fullCheck), + ("abcde\u{301}fghijkl", .notNFC), + ] + + for (test, expected) in tests { + Array(test.utf8).withUTF8Span { + var span = $0 + if span.isKnownNFC { + expectEqual(expected, .known) + } else if span.checkForNFC(quickCheck: true) { + expectEqual(expected, .quickCheck) + } else if span.checkForNFC(quickCheck: false) { + expectEqual(expected, .fullCheck) + } else { + expectEqual(expected, .notNFC) + } + } + } + } + + suite.test("UTF8Span/canonical equivalence") { + + // TODO: refactor to be test-case declaration driven, and add more tests... + // `(normalized: String, variants: [String], lessThan: String, greaterThan: String)` + + let precomposedStr = "café" + let decomposedStr = "cafe\u{301}" + + let precomposed = Array(precomposedStr.utf8) + let decomposed = Array(decomposedStr.utf8) + + precomposed.withSpan { pre in + let utf8Precomposed = try! UTF8Span(validating: pre) + decomposed.withSpan { de in + let utf8Decomposed = try! UTF8Span(validating: de) + + // print("scalars for \(precomposedStr.unicodeScalars)") + // var preScalars = utf8Precomposed.makeUnicodeScalarIterator() + // while let s = preScalars.next() { + // print(s) + // } + + // print("scalars for \(decomposedStr.unicodeScalars)") + // var deScalars = utf8Decomposed.makeUnicodeScalarIterator() + // while let s = deScalars.next() { + // print(s) + // } + + expectTrue(utf8Precomposed.isCanonicallyEquivalent(to: utf8Decomposed)) + + expectTrue(utf8Precomposed.bytesEqual(to: precomposedStr.utf8)) + expectFalse(utf8Precomposed.bytesEqual(to: decomposedStr.utf8)) + + expectTrue(utf8Decomposed.bytesEqual(to: decomposedStr.utf8)) + expectFalse(utf8Decomposed.bytesEqual(to: precomposedStr.utf8)) + + expectTrue(utf8Precomposed.unicodeScalarsEqual(to: precomposedStr.unicodeScalars)) + expectFalse(utf8Precomposed.unicodeScalarsEqual(to: decomposedStr.unicodeScalars)) + + expectTrue(utf8Decomposed.unicodeScalarsEqual(to: decomposedStr.unicodeScalars)) + expectFalse(utf8Decomposed.unicodeScalarsEqual(to: precomposedStr.unicodeScalars)) + + expectTrue(utf8Precomposed.charactersEqual(to: precomposedStr)) + expectTrue(utf8Precomposed.charactersEqual(to: decomposedStr)) + + expectTrue(utf8Decomposed.charactersEqual(to: decomposedStr)) + expectTrue(utf8Decomposed.charactersEqual(to: precomposedStr)) + + // Equivalence means no-one is less than the other + expectFalse(utf8Decomposed.isCanonicallyLessThan(utf8Precomposed)) + expectFalse(utf8Precomposed.isCanonicallyLessThan(utf8Decomposed)) + + } + + } + + + } + + +} + +// TODO: Rest of this file is in-progress TODOs + + +/* + + +isASCII +isKnownNFC +checkForNFC(quickCheck:) +isKnownSingleScalarCharacters +checkForSingleScalarCharacters(quickCheck:) + +public func bytesEqual(to other: UTF8Span) -> Bool +public func bytesEqual(to other: some Sequence) -> Bool + +public func scalarsEqual( + to other: some Sequence +) -> Bool + +public func charactersEqual( + to other: some Sequence +) -> Bool + +public func isCanonicallyEquivalent( + to other: UTF8Span +) -> Bool + +public func isCanonicallyLessThan( + _ other: UTF8Span +) -> Bool + +*/ + +// @available(SwiftStdlib 6.2, *) +// private struct QueryTestCase { +// var content: String + +// var loc: SourceLocStack + +// var isASCII: Bool + +// // TODO: This might become API, or otherwise calculated at init time +// var isLatinyNFC: Bool { +// bytes.allSatisfy { $0 < 0xCC } +// } + +// var isQuickNFC: Bool +// var isNFC: Bool + +// var isQuickSSC: Bool +// var isSSC: Bool +// } + +// if #available(SwiftStdlib 6.2, *) { +// suite.test("UTF8Span/queries") { +// } +// } + +// enum ComparisonResult { +// binaryEqual +// canonicallyEqual +// canonicallyLess +// inequal +// } + +// private struct ComparisonTestCase { +// var content: String +// var comparisons: [(String, ComparisonResult)] + +// var loc: SourceLocStack +// } + +// if #available(SwiftStdlib 6.2, *) { +// suite.test("UTF8Span/comparisons") { +// func test() +// } +// } + + +/* + + input string, to check the bits and relevant info + comparison string and expected comparison level + +*/ + + +// } \ No newline at end of file diff --git a/unittests/runtime/CompatibilityOverrideConcurrency.cpp b/unittests/runtime/CompatibilityOverrideConcurrency.cpp index b2fa56b6e0b..5a4358a6351 100644 --- a/unittests/runtime/CompatibilityOverrideConcurrency.cpp +++ b/unittests/runtime/CompatibilityOverrideConcurrency.cpp @@ -118,7 +118,7 @@ static void swift_task_startOnMainActor_override(AsyncTask* task) { } SWIFT_CC(swift) -static void swift_task_startSynchronously_override(AsyncTask* task) { +static void swift_task_startSynchronously_override(AsyncTask* task, SerialExecutorRef targetExecutor) { Ran = true; } @@ -351,7 +351,7 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startOnMainActorImpl) { } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startSynchronously) { - swift_task_startSynchronously(nullptr); + swift_task_startSynchronously(nullptr, SerialExecutorRef::generic()); } TEST_F(CompatibilityOverrideConcurrencyTest, diff --git a/userdocs/diagnostics/async-caller-execution.md b/userdocs/diagnostics/async-caller-execution.md index a763e13482c..374ee542630 100644 --- a/userdocs/diagnostics/async-caller-execution.md +++ b/userdocs/diagnostics/async-caller-execution.md @@ -7,9 +7,9 @@ these functions. This feature was proposed in [SE-0461](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0461-async-function-isolation.md) -* The `@execution(concurrent)` attribute specifies that a function must always +* The `@concurrent` attribute specifies that a function must always switch off of an actor to run. This is the default behavior without `AsyncCallerExecution`. -* The `@execution(caller)` attribute specifies that a function must always +* The `nonisolated(nonsending)` modifier specifies that a function must always run on the caller's actor. This is the default behavior with `AsyncCallerExecution`. diff --git a/userdocs/diagnostics/sending-risks-data-race.md b/userdocs/diagnostics/sending-risks-data-race.md index 2f7d65c187e..2e6247b0575 100644 --- a/userdocs/diagnostics/sending-risks-data-race.md +++ b/userdocs/diagnostics/sending-risks-data-race.md @@ -29,13 +29,13 @@ await person.printNameConcurrently() This happens because the `printNameConcurrently` function runs off of the main actor, and the `onMainActor` function suspends while waiting for `printNameConcurrently` to complete. While suspended, the main actor can run other tasks that still have access to `person`, which can lead to a data race. -The most common fix is to change the `async` method to run on the caller's actor using the `@execution(caller)` attribute: +The most common fix is to change the `async` method to run on the caller's actor using the `nonisolated(nonsending)` specifier: ```swift class Person { var name: String = "" - @execution(caller) + nonisolated(nonsending) func printNameConcurrently() async { print(name) } @@ -49,4 +49,4 @@ func onMainActor(person: Person) async { This eliminates the risk of data-races because `printNameConcurrently` continues to run on the main actor, so all access to `person` is serialized. -You can also enable the `AsyncCallerExecution` upcoming feature to make `@execution(caller)` the default for async functions on non-`Sendable` types. \ No newline at end of file +You can also enable the `AsyncCallerExecution` upcoming feature to make `nonisolated(nonsending)` the default for async functions on non-`Sendable` types. diff --git a/utils/availability-macros.def b/utils/availability-macros.def index 3659e5e2787..f816acd10af 100644 --- a/utils/availability-macros.def +++ b/utils/availability-macros.def @@ -39,6 +39,9 @@ SwiftStdlib 6.0:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0 SwiftStdlib 6.1:macOS 15.4, iOS 18.4, watchOS 11.4, tvOS 18.4, visionOS 2.4 SwiftStdlib 6.2:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, visionOS 9999 +# Like SwiftStdlib 5.0, but also the oldest visionOS from SwiftStdlib 5.10. +SwiftCompatibilitySpan 5.0:macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2, visionOS 1.1 + # Note: This cannot be 9999, so use the last non-placeholder entry instead. # This will go away once 'SwiftStdlib 6.2' is filled in. SwiftCompatibilitySpan 6.2:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0 diff --git a/utils/build.ps1 b/utils/build.ps1 index 1c5df23fbab..317afebfbd7 100644 --- a/utils/build.ps1 +++ b/utils/build.ps1 @@ -765,13 +765,13 @@ function Invoke-Program() { if ($OutNull) { & $Executable @ExecutableArgs | Out-Null } elseif ($Silent) { - & $Executable @ExecutableArgs *> $null + & $Executable @ExecutableArgs | Out-Null 2>&1| Out-Null } elseif ($OutFile -and $ErrorFile) { - & $Executable @ExecutableArgs > $OutFile 2> $ErrorFile + & $Executable @ExecutableArgs | Out-File -FilePath $OutFile -Encoding UTF8 2>&1| Out-File -FilePath $ErrorFile -Encoding UTF8 } elseif ($OutFile) { - & $Executable @ExecutableArgs > $OutFile + & $Executable @ExecutableArgs | Out-File -FilePath $OutFile -Encoding UTF8 } elseif ($ErrorFile) { - & $Executable @ExecutableArgs 2> $ErrorFile + & $Executable @ExecutableArgs 2>&1| Out-File -FilePath $ErrorFile -Encoding UTF8 } else { & $Executable @ExecutableArgs } @@ -1837,7 +1837,7 @@ function Test-Compilers([Hashtable] $Platform, [switch] $TestClang, [switch] $Te # No watchpoint support on windows: https://github.com/llvm/llvm-project/issues/24820 LLDB_TEST_USER_ARGS = "--skip-category=watchpoint"; # gtest sharding breaks llvm-lit's --xfail and LIT_XFAIL inputs: https://github.com/llvm/llvm-project/issues/102264 - LLVM_LIT_ARGS = "-v --no-gtest-sharding --show-xfail"; + LLVM_LIT_ARGS = "-v --no-gtest-sharding --time-tests"; # LLDB Unit tests link against this library LLVM_UNITTEST_LINK_FLAGS = "$(Get-SwiftSDK Windows)\usr\lib\swift\windows\$($Platform.Architecture.LLVMName)\swiftCore.lib"; } @@ -3169,7 +3169,9 @@ function Build-Installer([Hashtable] $Platform) { function Copy-BuildArtifactsToStage([Hashtable] $Platform) { Copy-File "$BinaryCache\$($Platform.Triple)\installer\Release\$($Platform.Architecture.VSName)\*.cab" $Stage Copy-File "$BinaryCache\$($Platform.Triple)\installer\Release\$($Platform.Architecture.VSName)\*.msi" $Stage - Copy-File "$BinaryCache\$($Platform.Triple)\installer\Release\$($Platform.Architecture.VSName)\*.msm" $Stage + foreach ($SDKPlatform in $WindowsSDKPlatforms) { + Copy-File "$BinaryCache\$($Platform.Triple)\installer\Release\$($SDKPlatform.Architecture.VSName)\*.msm" $Stage + } Copy-File "$BinaryCache\$($Platform.Triple)\installer\Release\$($Platform.Architecture.VSName)\installer.exe" $Stage # Extract installer engine to ease code-signing on swift.org CI if ($ToBatch) { diff --git a/validation-test/compiler_crashers_2/issue-80657.swift b/validation-test/compiler_crashers_2/issue-80657.swift new file mode 100644 index 00000000000..cd85d8aadea --- /dev/null +++ b/validation-test/compiler_crashers_2/issue-80657.swift @@ -0,0 +1,17 @@ +// RUN: not --crash %target-swift-frontend -emit-sil %s + +// https://github.com/swiftlang/swift/issues/80657 +enum E { + case e + + static func foo() { + _ = { [self] in + switch e { + case e: + break + default: + break + } + } + } +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar146952007.swift b/validation-test/compiler_crashers_2_fixed/rdar146952007.swift new file mode 100644 index 00000000000..6362d4c725d --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar146952007.swift @@ -0,0 +1,45 @@ +// RUN: %target-swift-frontend -emit-sil %s + +// These cases are similar to https://github.com/swiftlang/swift/issues/80657, +// but we can avoid hitting the same issue for non-enum members. + +struct S { + let y = 0 + func foo(_ x: Int) { + let _ = { [self] in + switch x { + case y: break + default: break + } + } + } +} + +class C { + let y = 0 + func foo(_ x: Int) { + let _ = { [self] in + switch x { + case y: break + default: break + } + } + } +} + +enum E { + case e + + func bar() -> Int {0} + + func foo() { + _ = { [self] in + switch 0 { + case bar(): + break + default: + break + } + } + } +}