[Sema] Improve diagnostics for witness mismatches against @objc protocols.

Simplify and improve the checking of @objc names when matching a
witness to a requirement in the @objc protocol. First, don't use
@objc-ness as part of the initial screening to determine whether a
witness potentially matches an @objc requirement: we will only reject
a potential witness when the potential witness has an explicit
"@nonobjc" attribute on it. Otherwise, the presence of @objc and the
corresponding Objective-C name is checked only after selecting a
candidate. This more closely mirrors what we do for override checking,
where we match based on the Swift names (first) and validate
@objc'ness afterward. It is also a stepping stone to inferring
@objc'ness and @objc names from protocol conformances.

Second, when emitting a diagnostic about a missing or incorrect @objc
annotation, make sure the Fix-It gets the @objc name right: this might
mean adding the Objective-C name along with @objc (e.g.,
"@objc(fooWithString:bar:)"), adding the name to an
unadorned-but-explicit "@objc" attribute, or fixing the name of an
@objc attribute (e.g., "@objc(foo:bar:)" becomes
@objc(fooWithString:bar:)"). Make this diagnostic an error, rather
than a note on a generic "does not conform" diagnostic, so it's much
easier to see the diagnostic and apply the Fix-It.

Third, when emitting the warning about a non-@objc near-match for an
optional @objc requirement, provide two Fix-Its: one that adds the
appropriate @objc annotation (per the paragraph above), and one that
adds @nonobjc to silence the warning.

Part of the QoI improvements for conformances to @objc protocols,
rdar://problem/25159872.
This commit is contained in:
Doug Gregor
2016-04-18 14:22:40 -07:00
parent a22d8569d4
commit bc158c31bf
11 changed files with 407 additions and 140 deletions

View File

@@ -1039,6 +1039,16 @@ Type ConstraintSystem::replaceSelfTypeInArchetype(ArchetypeType *archetype) {
return archetype;
}
/// Determine whether the given locator is for a witness or requirement.
static bool isRequirementOrWitnesss(const ConstraintLocatorBuilder &locator) {
if (auto last = locator.last()) {
return last->getKind() == ConstraintLocator::Requirement ||
last->getKind() == ConstraintLocator::Witness;
}
return false;
}
std::pair<Type, Type>
ConstraintSystem::getTypeOfMemberReference(
Type baseTy, ValueDecl *value,
@@ -1237,10 +1247,14 @@ ConstraintSystem::getTypeOfMemberReference(
// optional/dynamic, is settable, or is not.
auto fnType = openedFnType->getResult()->castTo<FunctionType>();
auto elementTy = fnType->getResult();
if (subscript->getAttrs().hasAttribute<OptionalAttr>())
elementTy = OptionalType::get(elementTy->getRValueType());
else if (isDynamicResult)
elementTy = ImplicitlyUnwrappedOptionalType::get(elementTy->getRValueType());
if (!isRequirementOrWitnesss(locator)) {
if (subscript->getAttrs().hasAttribute<OptionalAttr>())
elementTy = OptionalType::get(elementTy->getRValueType());
else if (isDynamicResult) {
elementTy = ImplicitlyUnwrappedOptionalType::get(
elementTy->getRValueType());
}
}
type = FunctionType::get(fnType->getInput(), elementTy);
} else if (isa<ProtocolDecl>(value->getDeclContext()) &&
@@ -1372,8 +1386,9 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
= getTypeOfReference(choice.getDecl(), isTypeReference,
choice.isSpecialized(), locator);
}
if (choice.getDecl()->getAttrs().hasAttribute<OptionalAttr>() &&
if (!isRequirementOrWitnesss(locator) &&
choice.getDecl()->getAttrs().hasAttribute<OptionalAttr>() &&
!isa<SubscriptDecl>(choice.getDecl())) {
// For a non-subscript declaration that is an optional
// requirement in a protocol, strip off the lvalue-ness (FIXME: