Before the change from @_objcImplementation to @objc @implementation, if a member was unrepresentable in ObjC, it would become implicitly `final`. After that change, this is now an error. We do want a diagnostic here, but we don’t want to break backwards compatibility for early adopters.
Soften the error to a warning when the old @objcImplementation syntax is used.
Fixes rdar://129247349.
…for extensions. This change also removes @implementation(CategoryName); you should attach the category name to the @objc attribute instead. And there are small changes to how much checking the compiler will do on an @objc @implementation after the decl checker has discovered a problem with it.
This now specifies a category name that’s used in TBDGen, IRGen, and PrintAsClang. There are also now category name conflict diagnostics; these subsume some @implementation diagnostics.
(It turns out there was already a check for @objc(CustomName) to make sure it wasn’t a selector!)
• ObjCImplementation controls @implementation on extensions
• CImplementation controls @implementation and @_objcImplementation on cdecl functions
Why the difference between them? Because `@_objcImplementation extension` has already been adopted pretty widely, while `@_objcImplementation @_cdecl` is very new.
ObjCImplementationChecker::matchTypes() implicitly assumed that if it was comparing function types, it must be working on an AbstractFunctionDecl. This isn’t true for a property of block type, which could cause crashes when checking their parameter types. Fix this oversight.
Fixes rdar://122280735.
We eventually want to be able to implement functions imported as globals or members in Swift, but we’re not there yet. Add some basic tests to make sure we get reasonable diagnostics for them.
This commit diagnoses cdecl implementations with no matching imported declaration, and also runs them through the ObjCImplementationChecker. Actually testing that the ObjCImplementationChecker diagnoses various failure conditions correctly will be added in a subsequent commit.
Extensions to lightweight generic classes are such a huge mess that we’re just removing these from the feature’s scope for now.
Fixes rdar://116066409.
Required and designated inits have to be overridable—the former so that uses of the initializer on `any T.Type` will call the subclass initializer, the latter so that inherited convenience inits will call the subclass initializer. However, Swift-only members use vtables to dispatch to subclasses, and @objcImpl classes don’t have vtables, so their Swift-only inits cannot be made overridable. Upshot: Swift-only inits on @objcImpl classes must be `convenience`, not `required` or designated.
Enforce this rule in the ObjCImplementationChecker.
Fixes rdar://109121293.
Nothing in an `@_objcImplementation` block—including Swift-only declarations—should ever require a vtable entry. Diagnose any such members.
The diagnostic emitted here is intended to be a fallback that will be used when a vtable entry is needed but we don’t know the specific reason. A future commit will add a more specific diagnostic for Swift-only non-convenience inits.
Besides improving diagnostics, this also allows us to track whether there were any @objc failures using the invalid bit on the @_objcImplementation attribute.
Initializers can’t be made final, but they *can* be made @nonobjc. (This isn’t always enough to ensure they’re actually valid, but I’ll get to that in a follow-up commit.)
The fix-it suggesting that an unmatched `@objc`-able member can be turned `private` always suggested adding a new modifier, even if one already existed. Make it suggest replacing the existing one instead.
Suppose a superclass declares an initializer unavailable and then a subclass wants to redeclare and use it. Formally, the subclass declaration overrides the superclass one; however, Swift will not actually require the subclass to use the `override` keyword. As currently implemented, this means that the requirement will be skipped as an override, but the candidate will be included as a member implementation. Result: a “candidate does not match any requirement” diagnostic.
Fix this by skipping requirements that are overrides *only* if the declaration they override is not unavailable.
Fixes rdar://109541045.
Temporarily cherry-pick Swift 5.9’s behavior of turning @objcImplementation errors into warnings to 5.10 until we fix the last few bugs in these diagnostics.
Because `required init`s do not have the `override` keyword, they are always treated as member implementations (if they pass other checks). However, these methods sometimes actually are overrides, and when they are, they should not be treated as member implementations. This results in required inits being treated as candidates when there won’t be a requirement for them to match.
Hack around this by separately checking for this situation and skipping the affected members.
Fixes rdar://112910098.
ClangImporter can import some methods as throwing that `@objc` cannot generate. For instance, an imported Objective-C method with an error out parameter in an unconventional position can still be imported as throwing no matter its selector, but `@objc` can only generate an error out parameter in an unconventional position if the matching selector part consists of the word `error` or (for the first part) ends with `Error`. Detect and diagnose these situations.
Note that the tests do not cover all of the new diagnostics because some of these conditions (like the `Void` parameter) cause selector mismatches and others (like the owned error parameter) are representable in the compiler but cannot currently be imported. I have chosen to add these diagnostics anyway in case there is a corner case that I haven’t discovered.
Fixes rdar://110100071.
The @objcImpl checker would accidentally dereference a null pointer when it tried to check if an async requirement could be satisfied by a non-async method. Fix that mistake.
Fixes rdar://111064481.
@objcImpl extensions aren’t allowed to declare new conformances; instead, they should either be declared in the header or in an ordinary extensions. (If they were permitted, they’d be ignored.)
Fixes rdar://110669366.
• Allow `required init`s in @objcImpl extensions of a class’s main body
• Validate that the presence or absence of a `required` modifier matches the imported header declaration.
Fixes rdar://110016760.
Check the types of @objcImpl candidates against their requirements and diagnose *most* mismatches.
Unlike typical type matching rules, @objcImpl allows an implementation’s parameter *and* result types to be an IUO when the requirement isn’t. This runs against the normal covariance rules in the case of the result type. It’s meant to allow an implementation to handle nils passed to it and return nils even if the declaration formally claims nils are not permitted; this is occasionally necessary to reimplement Objective-C APIs without breaking ABI compatibility.
Fixes rdar://102063730.
Sema now diagnoses @objcImpl implementations with:
• The wrong settability (i.e. a `let` used for a `readwrite` property)
• The wrong kind (i.e. a method used for a property)
Create a checker for @_objcImplementation member implementations that considers all of a class’s interface and implementation decls at once. This allows us to handle several things better:
• Unimplemented requirements are now diagnosed
• Header members that can match several implementations, or implementations that could match several header members, are now diagnosed
• Tailored diagnostic when the implementation's Swift name matches the header's selector instead of its Swift name
• Recommends inserting `@objc(<selector>)` when a Swift name matches but the implicit ObjC name doesn't
• An `@objc(<selector>)` on one implementation can eliminate its requirement from being considered for other implementations, resolving ambiguities
This does unfortunately regress the diagnostics when a requirement is implemented in the wrong extension. Some sort of whole-module checking would be needed to address this problem.
• `@objc` is now inferred on non-`final` members of @objcImplementation extensions
• Diagnostics now suggest adding `private` to ObjC helper members, not `@objc`, in line with currently proposed behavior
• Better diagnostic for members implemented in the wrong extension
Part of rdar://103150189.
Create a checker for @_objcImplementation member implementations that considers all of a class’s interface and implementation decls at once. This allows us to handle several things better:
• Unimplemented requirements are now diagnosed
• Header members that can match several implementations, or implementations that could match several header members, are now diagnosed
• Tailored diagnostic when the implementation's Swift name matches the header's selector instead of its Swift name
• Recommends inserting `@objc(<selector>)` when a Swift name matches but the implicit ObjC name doesn't
• An `@objc(<selector>)` on one implementation can eliminate its requirement from being considered for other implementations, resolving ambiguities
This does unfortunately regress the diagnostics when a requirement is implemented in the wrong extension. Some sort of whole-module checking would be needed to address this problem.
• `@objc` is now inferred on non-`final` members of @objcImplementation extensions
• Diagnostics now suggest adding `private` to ObjC helper members, not `@objc`, in line with currently proposed behavior
• Better diagnostic for members implemented in the wrong extension
Part of rdar://103150189.
Previously, Swift would reject an `override public init(…)` in an `@_objcImplementation` because ClangImporter would have already synthesized inherited initializers that conflicted with the overrides. Ignore these spurious conflicts, and also move a check out of IsObjCRequest and into the conflict-handling code.
Additional work towards rdar://70730077.
Without this change, an `@_objcImplementation` cannot override parent class methods, because the special access control behavior breaks the access control checks for overrides.
Treating this as forbidden was incorrect; an `@objc final` method is simply one that isn’t a member implementation but does have an ObjC entry point.
Formalize and centralize the definition of what a member implementation is, tweak it so that it’s basically “non-final and internal or greater”, and permit `@objc final`. Also remove the inference of `final` on `let`s in @_objcImpl extensions.
Stored properties are only allowed in the extension implementing the class's main interface, not its categories. This also means banning `@objc final`, which is unenforceable anyway when ObjC subclasses are allowed, and therefore allowing `@objc let` and `@objc static` properties to be overridden if they're declared in objcImplementations.