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-6689] Compiler permits more than one property implementation with same name but differing types via protocol extensions #49238

Open
swift-ci opened this issue Jan 2, 2018 · 2 comments

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Jan 2, 2018

Previous ID SR-6689
Radar None
Original Reporter marcpalmer (JIRA User)
Type Bug
Environment

Xcode 9.2 GM

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: 1273b80bd7c1605a99edfd258f9430bd

Issue Description:

This is a source of hard to find and potentially very dangerous bugs.

Take the following code:

protocol P {}
extension P {
  static var x: Int { return 2 }
}
struct S: P {
  static var x = ""
}

// Outputs "S.x is: [], with type String"
print("S.x is: [\(S.x)], with type \(type(of: S.x))")

let viaProtocol: P.Type = S.self
// Outputs "S.x is: [2], with type Int"
print("S.x is: [\(viaProtocol.x)], with type \(type(of: viaProtocol.x))")

The result is a type S with "two implementations" of x with different types, depending on how you access the property.

The compiler considers S to conform to P because of the protocol extension, but silently ignores the declaration specific to S which has a different type.

This results in different call sites in the app seeing entirely different values and due to static compilation and type safety, everything seems fine!

There appear to be two failings:

1. Extensions are deemed sufficient to satisfy protocol conformance, even if a Type provides its own value/implementation with a different type

2. The compiler permits redeclaration of a property with a different type, when there is an extension that already declares a different type for that property (in the case where the protocol does not have the property as a requirement)

Note that the above problem still happens even if P defines x as a requirement, which is even more surprising given that S redeclares it with a different type and the protocol is clearly mandating a different type.

UPDATE
This is even more pernicious if the difference in type is just optionality:

protocol P {
  static var x: String?
}
extension P {
  static var x: String? { return nil }
}
struct S: P {
  static var x: String = ""
}

In this case, due to type inference the results can be particularly confusing depending on the context in which you access the property, with the compiler making choices for your based on the context and returning different values.

@swift-ci
Copy link
Collaborator Author

swift-ci commented Jan 3, 2018

Comment by Marc Palmer (JIRA)

Here's another concrete use case where type inference breaks this and leads to unexpected behaviour:

protocol Feature {
  static var parent: Feature.Type? { get }
}
extension Feature {
  static var parent: Feature.Type? { return nil }
}

class MyFeature: Feature {
   public static let parent = RemoteControlFeature.self
}

This code compiles fine, but `parent` is nil when read from a `Feature.Type`. Relying on type inference has allowed it to appear to do the right thing but it has merely shadowed the extension's implementation. Explicitly typing the property solves this. This seems pretty bad as avoiding explicit typing is encouraged by Swift.

class MyFeature: Feature {
   public static let parent: Feature.Type? = RemoteControlFeature.self
}

@swift-ci
Copy link
Collaborator Author

swift-ci commented Jan 3, 2018

Comment by Marc Palmer (JIRA)

The above actually means you cannot use type inference at all for properties where the property is optional and has an extension implementing it

@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
Projects
None yet
Development

No branches or pull requests

1 participant