[ConstraintSystem] Deplay opening generic requirements until after contextual self has been applied

While computing a type of member via `getTypeOfMemberReference`
let's delay opening generic requirements associated with function
type until after self constraint has been created, that would give
a chance for contextual types to get propagated and make mismatch
originated in generic requirements much easier to diagnose.

Consider following example:

```swift
struct S<T> {}

extension S where T == Int {
  func foo() {}
}

func test(_ s: S<String>) {
  s.foo()
}
```

`foo` would get opened as `(S<$T>) -> () -> Void` and contextual `self`
type is going to be `S<String>`, so applying that before generic requirement
`$T == Int` would make sure that `$T` gets bound to a contextual
type of `String` and later fails requirement constraint `$T == Int`.

This is much easier to diagnose comparing to `$T` being bound to
`Int` right away due to same-type generic requirement and then
failing an attempt to convert `S<String>` to `S<Int>` while simplifying
self constraint.

Resolves: rdar://problem/46427500
Resolves: rdar://problem/34770265
This commit is contained in:
Pavel Yaskevich
2019-05-24 11:33:30 -07:00
parent a2b1e34a6f
commit 91dbcfdfcc
7 changed files with 76 additions and 15 deletions

View File

@@ -644,7 +644,8 @@ Type ConstraintSystem::openFunctionType(
OpenedTypeMap &replacements,
DeclContext *innerDC,
DeclContext *outerDC,
bool skipProtocolSelfConstraint) {
bool skipProtocolSelfConstraint,
bool skipGenericRequirements) {
Type type;
if (auto *genericFn = funcType->getAs<GenericFunctionType>()) {
@@ -654,7 +655,8 @@ Type ConstraintSystem::openFunctionType(
genericFn->getGenericSignature(),
skipProtocolSelfConstraint,
locator,
replacements);
replacements,
skipGenericRequirements);
// Transform the parameters and output type.
llvm::SmallVector<AnyFunctionType::Param, 4> openedParams;
@@ -1110,7 +1112,8 @@ void ConstraintSystem::openGeneric(
GenericSignature *sig,
bool skipProtocolSelfConstraint,
ConstraintLocatorBuilder locator,
OpenedTypeMap &replacements) {
OpenedTypeMap &replacements,
bool skipGenericRequirements) {
if (sig == nullptr)
return;
@@ -1137,6 +1140,9 @@ void ConstraintSystem::openGeneric(
bindArchetypesFromContext(*this, outerDC, locatorPtr, replacements);
if (skipGenericRequirements)
return;
// Add the requirements as constraints.
openGenericRequirements(
outerDC, sig, skipProtocolSelfConstraint, locator,
@@ -1327,9 +1333,12 @@ ConstraintSystem::getTypeOfMemberReference(
}
}
// While opening member function type, let's delay opening requirements
// to allow contextual types to affect the situation.
openedType = openFunctionType(funcType, numRemovedArgumentLabels,
locator, replacements, innerDC, outerDC,
/*skipProtocolSelfConstraint=*/true);
/*skipProtocolSelfConstraint=*/true,
/*skipGenericRequirements=*/true);
if (!outerDC->getSelfProtocolDecl()) {
// Class methods returning Self as well as constructors get the
@@ -1373,6 +1382,19 @@ ConstraintSystem::getTypeOfMemberReference(
addSelfConstraint(*this, baseOpenedTy, selfObjTy, locator);
}
// Open generic requirements after self constraint has been
// applied and contextual types have been propagated. This
// helps diagnostics because instead of self type conversion
// failing we'll get a generic requirement constraint failure
// if mismatch is related to generic parameters which is much
// easier to diagnose.
if (auto *genericFn = funcType->getAs<GenericFunctionType>()) {
openGenericRequirements(
outerDC, genericFn->getGenericSignature(),
/*skipProtocolSelfConstraint=*/true, locator,
[&](Type type) { return openType(type, replacements); });
}
// Compute the type of the reference.
Type type;
if (!value->isInstanceMember() || isInstance) {