mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Sema: Cleanups and minor fixes for protocol 'Self' types
We had four duplicated implementations of checking how a protocol requirement uses 'Self', all slightly wrong or incomplete: - When deciding if the protocol type can be used as an existential. This one would just ignore 'Self' in the return type of a method completely, which was incorrect for cases where 'Self' is contravariant but part of the return value, for example: func foo() -> (Self -> ()) - When deciding if a member access can be performed on an existential value. This is distinct from the former, because the member may have been defined in a protocol extension, in which case it cannot be used even if the protocol type can be used as an existential. Unfortunately, this implementation was overly conservative, and would reject uses of 'Self' where Sema could in fact erase the existential type, for example: func foo() -> Self?? func foo() -> Self.Type func foo() -> (Self, Self) This function handled function return types correctly, effectively plugging the leak in the previous code. It did lead to inconsistent behavior with protocols that had contravariant Self in requirements though; sometimes we would diagnose uses of the existential type, other times we would only complain about specific members. - When deciding if a method in a non-final class can model a protocol requirement. This one was the most elaborate one, but here contravariance and uses of associated types are actually okay, so it was written to pick up covariant 'Self' only. However, it also did not handle metatypes and tuples. - When opening the type of member of an existential, we would check if the return value was 'Self' or an optional of 'Self', but again this check was too conservative, so after the previous three were fixed, we could reference members on existentials that did not have a correct opened type. Now, these have been combined into one check. To fix some crashes, Sema's implementation of erasing existentials now relies on coerceToType() instead of hand-rolling a few coercions of its own, and wrapping the rest in CovariantFunctionConversionExpr, which didn't make much sense if the result was not a function type. SILGen still does not support function type conversions where an existential return value is being erased; these would silently miscompile before, but crash with an assertion now, because they are correctly modeled as a FunctionConversionExpr, and not CovariantFunctionConversionExpr.
This commit is contained in:
@@ -2488,61 +2488,6 @@ ConstraintSystem::simplifyCheckedCastConstraint(
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether the given type is the Self type of the protocol.
|
||||
static bool isProtocolSelf(Type type) {
|
||||
if (auto genericParam = type->getAs<GenericTypeParamType>())
|
||||
return genericParam->getDepth() == 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Determine whether the given type contains a reference to the 'Self' type
|
||||
/// of a protocol.
|
||||
static bool containsProtocolSelf(Type type) {
|
||||
// If the type is not dependent, it doesn't refer to 'Self'.
|
||||
if (!type->hasTypeParameter())
|
||||
return false;
|
||||
|
||||
return type.findIf([](Type type) -> bool {
|
||||
return isProtocolSelf(type);
|
||||
});
|
||||
}
|
||||
|
||||
/// Determine whether the given protocol member's signature prevents
|
||||
/// it from being used in an existential reference.
|
||||
static bool isUnavailableInExistential(TypeChecker &tc, ValueDecl *decl) {
|
||||
Type type = decl->getInterfaceType();
|
||||
if (!type) // FIXME: deal with broken recursion
|
||||
return true;
|
||||
|
||||
// For a function or constructor, skip the implicit 'this'.
|
||||
if (auto afd = dyn_cast<AbstractFunctionDecl>(decl)) {
|
||||
type = type->castTo<AnyFunctionType>()->getResult();
|
||||
|
||||
// Allow functions to return Self, but not have Self anywhere in
|
||||
// their argument types.
|
||||
for (unsigned i = 1, n = afd->getNumParameterLists(); i != n; ++i) {
|
||||
// Check whether the input type contains Self anywhere.
|
||||
auto fnType = type->castTo<AnyFunctionType>();
|
||||
if (containsProtocolSelf(fnType->getInput()))
|
||||
return true;
|
||||
|
||||
type = fnType->getResult();
|
||||
}
|
||||
|
||||
// Look through one level of optional on the result type.
|
||||
if (auto valueType = type->getAnyOptionalObjectType())
|
||||
type = valueType;
|
||||
|
||||
if (isProtocolSelf(type) || type->is<DynamicSelfType>())
|
||||
return false;
|
||||
|
||||
return containsProtocolSelf(type);
|
||||
}
|
||||
|
||||
return containsProtocolSelf(type);
|
||||
}
|
||||
|
||||
ConstraintSystem::SolutionKind
|
||||
ConstraintSystem::simplifyOptionalObjectConstraint(const Constraint &constraint)
|
||||
{
|
||||
@@ -2791,27 +2736,35 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
||||
// Introduce a new overload set.
|
||||
retry_ctors_after_fail:
|
||||
bool labelMismatch = false;
|
||||
for (auto constructor : ctors) {
|
||||
for (auto ctor : ctors) {
|
||||
// If the constructor is invalid, we fail entirely to avoid error cascade.
|
||||
TC.validateDecl(constructor, true);
|
||||
if (constructor->isInvalid())
|
||||
TC.validateDecl(ctor, true);
|
||||
if (ctor->isInvalid())
|
||||
return result.markErrorAlreadyDiagnosed();
|
||||
|
||||
// FIXME: Deal with broken recursion
|
||||
if (!ctor->getInterfaceType())
|
||||
continue;
|
||||
|
||||
// If the argument labels for this result are incompatible with
|
||||
// the call site, skip it.
|
||||
if (!hasCompatibleArgumentLabels(constructor)) {
|
||||
if (!hasCompatibleArgumentLabels(ctor)) {
|
||||
labelMismatch = true;
|
||||
result.addUnviable(constructor, MemberLookupResult::UR_LabelMismatch);
|
||||
result.addUnviable(ctor, MemberLookupResult::UR_LabelMismatch);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If our base is an existential type, we can't make use of any
|
||||
// constructor whose signature involves associated types.
|
||||
if (isExistential &&
|
||||
isUnavailableInExistential(getTypeChecker(), constructor)) {
|
||||
result.addUnviable(constructor,
|
||||
MemberLookupResult::UR_UnavailableInExistential);
|
||||
continue;
|
||||
if (isExistential) {
|
||||
if (auto *proto = ctor->getDeclContext()
|
||||
->getAsProtocolOrProtocolExtensionContext()) {
|
||||
if (!proto->isAvailableInExistential(ctor)) {
|
||||
result.addUnviable(ctor,
|
||||
MemberLookupResult::UR_UnavailableInExistential);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the invocation's argument expression has a favored constraint,
|
||||
@@ -2820,7 +2773,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
||||
if (favoredType && result.FavoredChoice == ~0U) {
|
||||
// Only try and favor monomorphic initializers.
|
||||
if (auto fnTypeWithSelf =
|
||||
constructor->getType()->getAs<FunctionType>()) {
|
||||
ctor->getType()->getAs<FunctionType>()) {
|
||||
|
||||
if (auto fnType =
|
||||
fnTypeWithSelf->getResult()->getAs<FunctionType>()) {
|
||||
@@ -2838,7 +2791,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
||||
}
|
||||
}
|
||||
|
||||
result.addViable(OverloadChoice(baseTy, constructor,
|
||||
result.addViable(OverloadChoice(baseTy, ctor,
|
||||
/*isSpecialized=*/false, *this));
|
||||
}
|
||||
|
||||
@@ -2919,6 +2872,10 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Deal with broken recursion
|
||||
if (!cand->getInterfaceType())
|
||||
return;
|
||||
|
||||
// If the argument labels for this result are incompatible with
|
||||
// the call site, skip it.
|
||||
if (!hasCompatibleArgumentLabels(cand)) {
|
||||
@@ -2927,12 +2884,17 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If our base is an existential type, we can't make use of any
|
||||
// member whose signature involves associated types.
|
||||
if (isExistential && isUnavailableInExistential(getTypeChecker(), cand)) {
|
||||
result.addUnviable(cand, MemberLookupResult::UR_UnavailableInExistential);
|
||||
return;
|
||||
if (isExistential) {
|
||||
if (auto *proto = cand->getDeclContext()
|
||||
->getAsProtocolOrProtocolExtensionContext()) {
|
||||
if (!proto->isAvailableInExistential(cand)) {
|
||||
result.addUnviable(cand,
|
||||
MemberLookupResult::UR_UnavailableInExistential);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if we have an instance method, instance member or static method,
|
||||
|
||||
Reference in New Issue
Block a user