Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-817] flatMap returns wrong result with covariant transform closure #43429

Open
swift-ci opened this issue Feb 25, 2016 · 5 comments
Open
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself SILGen Area → compiler: The SIL generation stage

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-817
Radar None
Original Reporter ianterrell (JIRA User)
Type Bug
Environment

Swift 2.1, OS X 10.11.3, Xcode 7.2.1

Additional Detail from JIRA
Votes 1
Component/s Compiler
Labels Bug, SILGen
Assignee None
Priority Medium

md5: 3071c4e2e97357f55bc638d6b46d38eb

Issue Description:

Given the following snippet:

var a: [Int?] = [1,2,nil]
a = a.flatMap { (item: Int?) -> Int? in
    return item
}
print(a)

I expect the return value to be [1,2,nil], but it returns [1,2] instead.

My rationale is that the matching function is...

extension SequenceType {
    @warn_unused_result
    public func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]
}

... so the return type of flatMap must be [Int?] to match the type of a, that implies T is Int?. That means the return type of transform must be Int??. Our closure returning Int? is properly accepted as it is covariant [0]. However, my expectation is that if our closure returns .None it would be treated as .Some(.None) ultimately by the transform, since .None is a valid Int? for wrapping in Int??.

[0]: Indeed, this code does not compile in 2.0, before closure covariance support was added.

@kconner
Copy link
Contributor

kconner commented Feb 25, 2016

Short repro:

let makeNilOptionalInt: () -> Int? = {
    return .None
}

func takeOptionalOptionalInt(getOptionalOptionalInt: () -> Int??) {
    let value = getOptionalOptionalInt()
    switch value {
    case .Some(.Some):
        print(".Some(.Some)")
    case .Some(.None):
        print(".Some(.None)") // Expected
    case .None:
        print(".None") // Actual
    }
}

takeOptionalOptionalInt(makeNilOptionalInt)

We expected lifting .None into a further optional context to yield .Some(.None), but it yields .None.
I ran this code in a playground in the Xcode version listed above.

@belkadan
Copy link
Contributor

cc @gribozavr

@belkadan
Copy link
Contributor

Actually, never mind, this is a compiler bug.

@belkadan
Copy link
Contributor

Elaboration: we have a transformation called "emitOptionalToOptional" that probably doesn't handle multi-level optionals correctly.

@Dante-Broggi
Copy link
Contributor

This still occurs in the Xcode 9.4.1 toolchain.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself SILGen Area → compiler: The SIL generation stage
Projects
None yet
Development

No branches or pull requests

4 participants