You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
struct Map10ElementsIterator<Base: IteratorProtocol, Element>: IteratorProtocol {
var base: Base
let transform: (AnySequence<Base.Element>) -> Element
mutating func next() -> Element? { /* ... */ }
}
The next() function calls transform with a sequence that is extracted from base, so that inner sequence's own next is defined within the outer. The inner incrementing code can be defined as a local function:
mutating func next() -> Element? {
guard let first = base.next() else { return nil }
return self.base.next() // Error here
}) { transform(AnySequence(AnyIterator($0))) }
}
The error is highlighted at the "self" token as "Closure cannot implicitly capture a mutating self parameter". But this can be worked around with a helper function shielding the call to withoutActuallyEscaping:
There seems to be a dependence on the length of the closure, because this:
public mutating func next() -> Element? {
switch remainingSplits {
case .some(0):
guard let first = base.next() else { return nil }
let firstCollection = CollectionOfOne(first)
var firstIterator = firstCollection.makeIterator()
return withoutActuallyEscaping({ firstIterator.next() ?? self.base.next() }, do: { transform(AnySequence(AnyIterator($0))) })
case .some(let limit):
remainingSplits = .some(limit - 1)
fallthrough
case .none:
return nil
}
}
From a similar type works just find at the end of the .some(0) case. It is a single expression, so the bug may be triggered on the inlined closure's code length.
The text was updated successfully, but these errors were encountered:
I don't see a test case that should work but doesn't in either the report here or the original thread. Jordan mentions that he tried something that should've worked, but his code snippet isn't even particularly close to compiling, so I don't know how to reverse it to something that doesn't work for the right reasons. So this all completely unusable, sorry.
I think I can shed some light on this.
With the Swift 5 compiler, compiling the following code would fail.
With the Swift 5.1 compiler (Apple Swift version 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9) it compiles successfully.
func doSomethingWithEscapingClosure(_ body:@escaping()->Void)->Void{body()}structMyStruct{mutatingfunc myFunc(){letinlineClosure={()->Voidin// Swift 5: "Error: Closure cannot implicitly capture a mutating self parameter"// Swift 5.1: OK!self.foo()}withoutActuallyEscaping(inlineClosure){ inlineClosurePromiseWontEscape ->VoidindoSomethingWithEscapingClosure{inlineClosurePromiseWontEscape()}}}mutatingfunc foo(){}}
Interestingly, pasting this in a Xcode 11.2.1 (11B500) playground, gives a "error: escaping closure captures mutating 'self' parameter" (see attached EscapingClosureError.playground.zip), if someone wants to file a radar for that..
Attachment: Download
Environment
macOS Mojave 10.14.2
Xcode 10.1 with its default Swift
Additional Detail from JIRA
md5: 35ddc4fec6eaa6e4d5385e385721d82f
Issue Description:
Let's start with a type like:
struct Map10ElementsIterator<Base: IteratorProtocol, Element>: IteratorProtocol {
var base: Base
let transform: (AnySequence<Base.Element>) -> Element
mutating func next() -> Element? { /* ... */ }
}
The
next()
function callstransform
with a sequence that is extracted frombase
, so that inner sequence's ownnext
is defined within the outer. The inner incrementing code can be defined as a local function:mutating func next() -> Element? {
guard let first = base.next() else { return nil }
var remaining = 10
func generate() -> Base.Element? {
guard remaining > 0 else { return nil }
defer { remaining -= 1 }
guard remaining < 10 else { return first }
return base.next()
}
return withoutActuallyEscaping(generate, do: { transform(AnySequence(AnyIterator($0))) })
}
The problems occur when we try to define the incrementing code directly as a closure:
mutating func next() -> Element? {
guard let first = base.next() else { return nil }
var remaining = 10
return withoutActuallyEscaping({
guard remaining > 0 else { return nil }
defer { remaining -= 1 }
guard remaining < 10 else { return first }
return self.base.next() // Error here
}) { transform(AnySequence(AnyIterator($0))) }
}
The error is highlighted at the "self" token as "Closure cannot implicitly capture a mutating self parameter". But this can be worked around with a helper function shielding the call to
withoutActuallyEscaping
:mutating func next() -> Element? {
func helper(_ body: () -> Base.Element?) -> Element {
return withoutActuallyEscaping(body) {
transform(AnySequence(AnyIterator($0)))
}
}
guard let first = base.next() else { return nil }
var remaining = 10
return helper {
guard remaining > 0 else { return nil }
defer { remaining -= 1 }
guard remaining < 10 else { return first }
return self.base.next()
}
}
There seems to be a dependence on the length of the closure, because this:
public mutating func next() -> Element? {
switch remainingSplits {
case .some(0):
guard let first = base.next() else { return nil }
let firstCollection = CollectionOfOne(first)
var firstIterator = firstCollection.makeIterator()
return withoutActuallyEscaping({ firstIterator.next() ?? self.base.next() }, do: { transform(AnySequence(AnyIterator($0))) })
case .some(let limit):
remainingSplits = .some(limit - 1)
fallthrough
case .none:
return nil
}
}
From a similar type works just find at the end of the
.some(0)
case. It is a single expression, so the bug may be triggered on the inlined closure's code length.The text was updated successfully, but these errors were encountered: