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-3512] Issue with IUO A and inout Optional<A> #46100

Closed
DevAndArtist mannequin opened this issue Dec 30, 2016 · 10 comments
Closed

[SR-3512] Issue with IUO A and inout Optional<A> #46100

DevAndArtist mannequin opened this issue Dec 30, 2016 · 10 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself type checker Area → compiler: Semantic analysis

Comments

@DevAndArtist
Copy link
Mannequin

DevAndArtist mannequin commented Dec 30, 2016

Previous ID SR-3512
Radar None
Original Reporter @DevAndArtist
Type Bug
Status Resolved
Resolution Done
Environment

Xcode Version 8.2.1, Swift 3.0.2 or DEVELOPMENT-SNAPSHOT-2016-12-15-a

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, TypeChecker
Assignee @DevAndArtist
Priority Medium

md5: 315fa6188981bd509a2cc21e0e81feb7

Issue Description:

class A {}

func foo(_: A?) {}

// An API of mine needs similar overloading
func bar(_: inout A) {} // #&#8203;1
func bar(_: inout A?) {}

var a: A! = nil
var b: A? = a

foo(a)
foo(b)

bar(&b) // fine

bar(&a)

// Remove #&#8203;1: Cannot pass immutable value of type 'A?' as inout argument
// Playground error message: Cannot invoke 'bar' with an argument list of type '(inout A!)'
// 
// I also had this error once, but could not reproduce it again: 
// Cannot convert value of type ‘A!’ to expected argument type 'inout A?’ (aka 'inout Optional<A>’)
@swift-ci
Copy link
Collaborator

swift-ci commented Jan 2, 2017

Comment by Omri Mor (JIRA)

This seems to be a problem centered around inout and optional types.

func test(val: inout Int?) { }
var a: Int = 0
test(val: &a)
// error: cannot pass immutable value as inout argument: implicit conversion from 'Int' to 'Int?' requires a temporary

func test2(val: inout [[Int]?]!) { }
var b: [[Int]] = [[1, 2, 3], [4, 5]]
test2(val: &b)
// error: cannot pass immutable value of type '[[Int]?]!' as inout argument

In both cases, assigning a variable of the 'correct' type (e.g. Int? or [[Int]?]! in the examples) results in proper execution.

I think there is a valid reason for this. Say there was a valid function as follows:

func test3(val: inout Int?) { val = nil}
var c: Int = 2
test3(val: &c)
// runtime error? a non-optional value is nil!

This also occurs with Unsafe[Mutable]Pointer. This is particularly frustrating when importing unannotated C APIs, as e.g. int ** gets imported as UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>!. Whereas functions with UnsafeMutablePointer<Type>! parameters can be directly passed a non-Optional variable of type Type, ones with pointer-to-pointer parameters cannot.

This last point is particularly confounding. While the Swift behavior is logical and excusable, the difference in behavior in allowing to pass arguments to C functions taking a pointer vs a pointer-to-pointer is contradictory and makes interacting with C APIs needlessly difficult.

@DevAndArtist
Copy link
Mannequin Author

DevAndArtist mannequin commented Jan 2, 2017

omor1 (JIRA User) It might be my English or I'm just a little bit confused by the latter example of yours. Are you saying that this is not a bug, but intended?

Here is how I'm using the `inout` in my API:

NSLayoutConstraint.activate([
    Constraint.where(view1.widthAnchor == 200).named("WidthConstraint"),
    Constraint.where(view1.heightAnchor == view2.heightAnchor)
        .multiplied(with: 2.0)
        .offset(by: -50)
        .named("HeightConstraint")
        .withPriority(of: 1000)
        .shouldBeArchived()
        .assignedTo(&self.someConstraintVariable) // <--- HERE is the magical function
])

Such constraints are most likely to be IUO's or non-optional. I assume when the example without `inout` is fine, where `!` is converted back to `?`, then the `inout` function should work the same way.

@swift-ci
Copy link
Collaborator

swift-ci commented Jan 2, 2017

Comment by Omri Mor (JIRA)

Sorry, I wrote that when I was a bit tired and wasn't quite clear.

I think that it is intended that one cannot pass a variable declared as T (non-optional) to a function taking an inout T?, as that could result in the variable being assigned to nil, which is not valid for a non-optional.
In your case, you are trying to pass an implicitly unwrapped optional variable (T!) to a function taking an inout T?, which seems to me that it should be allowed and might be a bug.

I was also pointing out that a somewhat related issue that I encountered.
I'm working with an API that exports a typedef as a pointer to a struct (e.g. typedef struct struct_type_t * TYPE). Some of the functions take pointers to this type (e.g. int API_Type_free(TYPE * t)), which gets imported in Swift as UnsafeMutablePointer<OpaquePointer?>! (i.e. the details of struct_type_t aren't exported).
The API is meant to be an interface. Another provider instead defines TYPE as an int (typedef int TYPE), so TYPE * is imported in Swift as UnsafeMutablePointer<Int32>!.
I'd like to be able to use the same Swift code, something like the following.

class Type {
    var t: TYPE
    init(t: TYPE) {
        self.t = t
    }
    deinit {
        API_Type_free(&t)
    }
}

This works fine with the second provider of the interface, but not with the first provider.
I'm trying to see if I can work around this by going through and annotating all the API. If I declare e.g. int API_Type_free(TYPE _Nonnull * _Nonnull t) for the first provider, this gets imported as API_Type_free(UnsafeMutablePointer<OpaquePointer>) -> Int32. For the second provider I would declare e.g. int API_Type_free(TYPE * _Nonnull t), which is imported as API_Type_free(UnsafeMutablePointer<Int32>) -> Int32.
However, this is a lot of work, and it would be nice if it didn't need to be done. Looking back at it, I can actually understand Swift's behavior—it makes sense in most cases, but is frustrating for my particular use with the unannotated API.

@DevAndArtist
Copy link
Mannequin Author

DevAndArtist mannequin commented Jan 3, 2017

In your case, you are trying to pass an implicitly unwrapped optional variable (T!) to a function taking an inout T?, which seems to me that it should be allowed and might be a bug.

Exactly what this SR is all about. 🙂 I need `Type!` to work with `function(_: inout Type?)`. It's definitely a bug, because it works as expected without `inout`.

@swift-ci
Copy link
Collaborator

swift-ci commented Jan 3, 2017

Comment by Omri Mor (JIRA)

It's definitely a bug, because it works as expected without `inout`.

Well, Type can be used in a function that takes Type?, but not in one that takes inout Type? (for good reason, as stated above), so that's not quite the reasoning I'd use.
Instead, I'd argue that since Swift 3, ImplicitlyUnwrappedOptional is no longer a type; rather, it is an attribute on an Optional variable. Therefore, there ought to be no barrier to passing Type! to a function that takes an inout Type?.

I thought the issue I was having was related this this, but it isn't quite the same. I think I did have an issue with passing a Type at one point though, which is quite similar to this SR.

@belkadan
Copy link
Contributor

belkadan commented Jan 4, 2017

I didn't read all of that but I think both of your conclusions are correct: this should work, and it should call the Optional overload. Unfortunately we didn't quite get IUO out of the compiler in time for the Swift 3 release, which "explains" the errors.

@swift-ci
Copy link
Collaborator

Comment by Nathan Vander Wilt (JIRA)

Using Swift 4.1 via Xcode 9.3, this issue appears to be fixed with `inout` but still a problem with `UnsafeMutablePointer`:

var obj: SomeClass! ; class SomeClass {}

func inoutFunc(_: inout SomeClass?) {}
inoutFunc(&obj) // works

func pointerFunc(_: UnsafeMutablePointer<SomeClass?>) {}
pointerFunc(&obj) // <-- COMPILER ERROR

See https://stackoverflow.com/questions/49928472/why-cant-i-pass-an-implicitly-unwrapped-optional-as-an-unsafemutablepointer for a bit more background. This affects usage of several lower-level Apple frameworks.

@rudkx
Copy link
Member

rudkx commented Apr 19, 2018

This is fixed in recent snapshot builds as a result of #14299

@DevAndArtist
Copy link
Mannequin Author

DevAndArtist mannequin commented Apr 20, 2018

@rudkx why was I assigned to the issue? I have not contributed the fix for it.

@rudkx
Copy link
Member

rudkx commented Apr 21, 2018

Frequently people who resolve issues will assign it back to the originator to verify that the issue is in fact fixed in recent snapshots.

I didn't actually put that request in the note when I assigned it, which is also relatively common.

Some issue tracking systems to this assignment automatically (i.e. if a person marks something as resolved, it's auto-assigned back to the originator).

So if you have a few moments, please try a recent snapshot from master and verify that in fact the issue you reported is fixed.

natevw (JIRA User), it would be great if you could also confirm that the issue you're reporting is fixed in snapshots from master.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
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 type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

3 participants