mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Move generic signature check for isolated conformances into GenericSignatureImpl
This is going to need a proper implementation in the requirement
machine. For the moment, provide a slightly-less-broken implementation
but leave a test case where we incorrectly accept racey code.
(cherry picked from commit 92774e0a3c)
This commit is contained in:
@@ -376,6 +376,19 @@ public:
|
||||
/// the given protocol.
|
||||
bool requiresProtocol(Type type, ProtocolDecl *proto) const;
|
||||
|
||||
/// Determine whether a conformance requirement of the given type to the
|
||||
/// given protocol prohibits the use of an isolated conformance.
|
||||
///
|
||||
/// The use of an isolated conformance to satisfy a requirement T: P is
|
||||
/// prohibited when T is a type parameter and T, or some type that can be
|
||||
/// used to reach T, also conforms to Sendable or SendableMetatype. In that
|
||||
/// case, the conforming type and the protocol (Sendable or SendableMetatype)
|
||||
/// is returned.
|
||||
///
|
||||
/// If there is no such requirement, returns std::nullopt.
|
||||
std::optional<std::pair<Type, ProtocolDecl *>>
|
||||
prohibitsIsolatedConformance(Type type) const;
|
||||
|
||||
/// Determine whether the given dependent type is equal to a concrete type.
|
||||
bool isConcreteType(Type type) const;
|
||||
|
||||
|
||||
@@ -371,6 +371,44 @@ bool GenericSignatureImpl::requiresProtocol(Type type,
|
||||
return getRequirementMachine()->requiresProtocol(type, proto);
|
||||
}
|
||||
|
||||
std::optional<std::pair<Type, ProtocolDecl *>>
|
||||
GenericSignatureImpl::prohibitsIsolatedConformance(Type type) const {
|
||||
type = getReducedType(type);
|
||||
|
||||
if (!type->isTypeParameter())
|
||||
return std::nullopt;
|
||||
|
||||
// An isolated conformance cannot be used in a context where the type
|
||||
// parameter can escape the isolation domain in which the conformance
|
||||
// was formed. To establish this, we look for Sendable or SendableMetatype
|
||||
// requirements on the type parameter itself.
|
||||
ASTContext &ctx = type->getASTContext();
|
||||
auto sendableProto = ctx.getProtocol(KnownProtocolKind::Sendable);
|
||||
auto sendableMetatypeProto =
|
||||
ctx.getProtocol(KnownProtocolKind::SendableMetatype);
|
||||
|
||||
// Check for a conformance requirement to SendableMetatype, which is
|
||||
// implied by Sendable.
|
||||
if (sendableMetatypeProto && requiresProtocol(type, sendableMetatypeProto)) {
|
||||
// Check for a conformance requirement to Sendable and return that if
|
||||
// it exists, because it's more recognizable and specific.
|
||||
if (sendableProto && requiresProtocol(type, sendableProto))
|
||||
return std::make_pair(type, sendableProto);
|
||||
|
||||
return std::make_pair(type, sendableMetatypeProto);
|
||||
}
|
||||
|
||||
// If this is a nested type, also check whether the parent type conforms to
|
||||
// SendableMetatype, because one can derive this type from the parent type.
|
||||
// FIXME: This is not a complete check, because there are other ways in which
|
||||
// one might be able to derive this type. This needs to determine whether
|
||||
// there is any path from a SendableMetatype-conforming type to this type.
|
||||
if (auto depMemTy = type->getAs<DependentMemberType>())
|
||||
return prohibitsIsolatedConformance(depMemTy->getBase());
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// Determine whether the given dependent type is equal to a concrete type.
|
||||
bool GenericSignatureImpl::isConcreteType(Type type) const {
|
||||
assert(type->isTypeParameter() && "Expected a type parameter");
|
||||
|
||||
@@ -1064,17 +1064,19 @@ CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics(
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isolatedConformances.empty()) {
|
||||
if (!isolatedConformances.empty() && signature) {
|
||||
// Dig out the original type parameter for the requirement.
|
||||
// FIXME: req might not be the right pre-substituted requirement,
|
||||
// if this came from a conditional requirement.
|
||||
if (auto failedProtocol =
|
||||
typeParameterProhibitsIsolatedConformance(req.getFirstType(),
|
||||
signature)) {
|
||||
return CheckGenericArgumentsResult::createIsolatedConformanceFailure(
|
||||
req, substReq,
|
||||
TinyPtrVector<ProtocolConformanceRef>(isolatedConformances),
|
||||
*failedProtocol);
|
||||
for (const auto &isolatedConformance : isolatedConformances) {
|
||||
(void)isolatedConformance;
|
||||
if (auto failed =
|
||||
signature->prohibitsIsolatedConformance(req.getFirstType())) {
|
||||
return CheckGenericArgumentsResult::createIsolatedConformanceFailure(
|
||||
req, substReq,
|
||||
TinyPtrVector<ProtocolConformanceRef>(isolatedConformances),
|
||||
failed->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1181,28 +1183,3 @@ Type StructuralTypeRequest::evaluate(Evaluator &evaluator,
|
||||
|
||||
return TypeAliasType::get(typeAlias, parent, genericArgs, result);
|
||||
}
|
||||
|
||||
std::optional<ProtocolDecl *> swift::typeParameterProhibitsIsolatedConformance(
|
||||
Type type, GenericSignature signature) {
|
||||
if (!type->isTypeParameter())
|
||||
return std::nullopt;
|
||||
|
||||
// An isolated conformance cannot be used in a context where the type
|
||||
// parameter can escape the isolation domain in which the conformance
|
||||
// was formed. To establish this, we look for Sendable or SendableMetatype
|
||||
// requirements on the type parameter itself.
|
||||
ASTContext &ctx = type->getASTContext();
|
||||
auto sendableProto = ctx.getProtocol(KnownProtocolKind::Sendable);
|
||||
auto sendableMetatypeProto =
|
||||
ctx.getProtocol(KnownProtocolKind::SendableMetatype);
|
||||
|
||||
if (sendableProto &&
|
||||
signature->requiresProtocol(type, sendableProto))
|
||||
return sendableProto;
|
||||
|
||||
if (sendableMetatypeProto &&
|
||||
signature->requiresProtocol(type, sendableMetatypeProto))
|
||||
return sendableMetatypeProto;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -1535,14 +1535,6 @@ bool maybeDiagnoseMissingImportForMember(const ValueDecl *decl,
|
||||
/// source file.
|
||||
void diagnoseMissingImports(SourceFile &sf);
|
||||
|
||||
/// Determine whether the type parameter has requirements that would prohibit
|
||||
/// it from using any isolated conformances.
|
||||
///
|
||||
/// Returns the protocol to which the type conforms that causes the conflict,
|
||||
/// which can be either Sendable or SendableMetatype.
|
||||
std::optional<ProtocolDecl *> typeParameterProhibitsIsolatedConformance(
|
||||
Type type, GenericSignature signature);
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1230,8 +1230,8 @@ void ConstraintSystem::openGenericRequirement(
|
||||
|
||||
// Check whether the given type parameter has requirements that
|
||||
// prohibit it from using an isolated conformance.
|
||||
if (typeParameterProhibitsIsolatedConformance(req.getFirstType(),
|
||||
signature))
|
||||
if (signature &&
|
||||
signature->prohibitsIsolatedConformance(req.getFirstType()))
|
||||
prohibitIsolatedConformance = true;
|
||||
|
||||
openedReq = Requirement(kind, openedFirst, req.getSecondType());
|
||||
|
||||
@@ -153,3 +153,37 @@ func testIsolationConformancesFromOutside(c: C) {
|
||||
let _: any P = c // expected-error{{main actor-isolated conformance of 'C' to 'P' cannot be used in nonisolated context}}
|
||||
let _ = PWrapper<C>() // expected-error{{main actor-isolated conformance of 'C' to 'P' cannot be used in nonisolated context}}
|
||||
}
|
||||
|
||||
protocol HasAssociatedType {
|
||||
associatedtype A
|
||||
}
|
||||
|
||||
func acceptHasAssocWithP<T: HasAssociatedType>(_: T) where T.A: P { }
|
||||
|
||||
func acceptSendableHasAssocWithP<T: Sendable & HasAssociatedType>(_: T) where T.A: P { }
|
||||
// expected-note@-1{{'acceptSendableHasAssocWithP' declared here}}
|
||||
|
||||
|
||||
struct HoldsC: HasAssociatedType {
|
||||
typealias A = C
|
||||
}
|
||||
|
||||
extension HasAssociatedType {
|
||||
static func acceptAliased<T: P>(_: T.Type) where A == T { }
|
||||
}
|
||||
|
||||
extension HasAssociatedType where Self: Sendable {
|
||||
static func acceptSendableAliased<T: P>(_: T.Type) where A == T { }
|
||||
}
|
||||
|
||||
func testIsolatedConformancesOnAssociatedTypes(hc: HoldsC, c: C) {
|
||||
acceptHasAssocWithP(hc)
|
||||
acceptSendableHasAssocWithP(hc) // expected-error{{main actor-isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a 'Sendable' type parameter }}
|
||||
|
||||
HoldsC.acceptAliased(C.self) // okay
|
||||
|
||||
// FIXME: the following should produce an error, because the isolated
|
||||
// conformance of C: P can cross isolation boundaries via the Sendable Self's
|
||||
// associated type.
|
||||
HoldsC.acceptSendableAliased(C.self)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user