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-13815] Confusing unwrapping diagnostic when using implicit member access on Optionals. #56212
Comments
Could you please add a short comment describing why this was closed as Invalid, in case someone else stumbles across the same problem and lands here? |
Comment by Davide Sibilio (JIRA) Hello theindigamer (JIRA User), the real problem here, is that the compiler is searching for the Optional<Object>.init() and not the Object.init?(). So it might seem like a bug, but thinking with some colleagues, maybe it's not. |
Comment by Davide Sibilio (JIRA) Adding an Extension like this: extension Optional where Wrapped == Object {
init() {
self = .none
}
} will make the above code work, is it intentional? |
I think yes, that makes sense. The way the leading dot syntax works is that the "return type" is used as the prefix for the lookup. In this case, from context, the type of |
Comment by Davide Sibilio (JIRA) Despite this, I think that the given error is incorrect, it should be something like "Does not exist an initializer for Optional<Object>" and not "Value of optional type 'Object?' must be unwrapped to a value of type 'Object' ". |
Comment by Davide Sibilio (JIRA) There is something strange with failable inits This code doesn't work: struct Object {
let id: Int
init?() {
return nil
}
init(id: Int) {
self.id = id
}
}
let object: Object = .init() ?? Object(id: 1) // will fail: Value of optional type 'Object?' must be unwrapped to a value of type 'Object'
let object: Object = .init() ?? Object()! // will fail: Value of optional type 'Object?' must be unwrapped to a value of type 'Object'
let object: Object = .init()! // will fail: type of expression is ambiguous without more context |
I agree that the diagnostic could be more helpful. That said, the other examples fail to compile for similar reasons. Based on the operators, the |
(I have edited the title and description to reflect the problem with the diagnostic. Please feel free to change them in case you'd like to improve the wording.) |
@swift-ci create |
Just ran this on a near master branch and the let object: Object = .init()! // will fail: type of expression is ambiguous without more context Seems to typecheck correctly, the others still produce the same confusing diagnostic. |
Comment by Frederick Kellison-Linn (JIRA) A bit more context here, since I've just recently done some implementation work on "leading dot syntax" (AKA implicit member syntax, AKA unresolved member expressions): This one is a bit tricky. The way that lookup is supposed to work for an unresolved member expression ".foo" is basically:
It's (3) that causes the issue seen here: since lookup only considers the base name, and because Optional has at least one `init`, we find results on the first lookup pass in (2) on `Optional<Object>` and thus never look into `Object`. Importantly, (3) does not care whether the results returned by (2) will type check properly or not. If anything exists in `Optional` under the name in question, we will not even try to look into the wrapped type. This causes even more issues when Optional is imbued with conditional conformances. SR-13842 shows an example where, because of Optional's conditional conformance to SwiftUI.View, lookup for `.background as UIImage?` finds `View.background(:alignment cc @xedin, what do you think the correct behavior is here? It seems not sufficient to skip the lookup in `T` whenever `Optional<T>` has results, so there are a couple alternatives:
Note: AFAICT, only (1) above will address the issue here with `init`, since lookup will always find Optional's various initializers. |
jumhyn (JIRA User) Just curious in the 1. option why disfavor the overloads from T if there are results from Optional<T>? |
Comment by Frederick Kellison-Linn (JIRA) @LucianoPAlmeida The best reason is that we have to maintain source compatibility, since today we'll never attempt any results from `T` if there are results from `T?`, so we wouldn't want to start diagnosing those cases as ambiguous. You can see this today if you define a custom type with a static `.none` member (although in this specific case we emit a warning): ``` func takesE(_: E?) {} |
Comment by Frederick Kellison-Linn (JIRA) Also, FWIW, we already prefer solutions where an overload choice has kind `Decl` over those where the overload has kind `DeclViaUnwrappedOptional`, so maybe it would "just work" to always look through optional types? |
Fixed by #34715 Thanks again, jumhyn (JIRA User)! iDevid (JIRA User)Please validate using the next available toolchain from main branch (found at swift.org). |
Additional Detail from JIRA
md5: 790bfd0e832f8f87224b0af0f4e3cd77
is duplicated by:
SwiftUI
is importedIssue Description:
As you can see, the Test Struct contains an id along with an optional Object property.
Passing the Object's .init() in the Test initializer will lead to the error:
This diagnostic doesn't make it clear what the issue is.
The text was updated successfully, but these errors were encountered: