Skip to content

Commit c169f88

Browse files
authored
Merge pull request #79760 from DougGregor/isolated-conformances-checking
Ensure that isolated conformances originate in the same isolation domain
2 parents 593f320 + 5c67cff commit c169f88

File tree

6 files changed

+329
-0
lines changed

6 files changed

+329
-0
lines changed

include/swift/AST/DiagnosticsSema.def

+3
Original file line numberDiff line numberDiff line change
@@ -8324,6 +8324,9 @@ ERROR(isolated_conformance_with_sendable_simple,none,
83248324
"isolated conformance of %0 to %1 cannot be used to satisfy conformance "
83258325
"requirement for a `Sendable` type parameter ",
83268326
(Type, DeclName))
8327+
ERROR(isolated_conformance_wrong_domain,none,
8328+
"%0 isolated conformance of %1 to %2 cannot be used in %3 context",
8329+
(ActorIsolation, Type, DeclName, ActorIsolation))
83278330

83288331
//===----------------------------------------------------------------------===//
83298332
// MARK: @execution Attribute

lib/Sema/TypeCheckConcurrency.cpp

+112
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "MiscDiagnostics.h"
1919
#include "TypeCheckDistributed.h"
2020
#include "TypeCheckInvertible.h"
21+
#include "TypeCheckProtocol.h"
2122
#include "TypeCheckType.h"
2223
#include "TypeChecker.h"
2324
#include "swift/AST/ASTWalker.h"
@@ -3175,6 +3176,18 @@ namespace {
31753176
checkDefaultArgument(defaultArg);
31763177
}
31773178

3179+
if (auto erasureExpr = dyn_cast<ErasureExpr>(expr)) {
3180+
checkIsolatedConformancesInContext(
3181+
erasureExpr->getConformances(), erasureExpr->getLoc(),
3182+
getDeclContext());
3183+
}
3184+
3185+
if (auto *underlyingToOpaque = dyn_cast<UnderlyingToOpaqueExpr>(expr)) {
3186+
checkIsolatedConformancesInContext(
3187+
underlyingToOpaque->substitutions, underlyingToOpaque->getLoc(),
3188+
getDeclContext());
3189+
}
3190+
31783191
return Action::Continue(expr);
31793192
}
31803193

@@ -4282,6 +4295,9 @@ namespace {
42824295
if (!declRef)
42834296
return false;
42844297

4298+
// Make sure isolated conformances are formed in the right context.
4299+
checkIsolatedConformancesInContext(declRef, loc, getDeclContext());
4300+
42854301
auto decl = declRef.getDecl();
42864302

42874303
// If this declaration is a callee from the enclosing application,
@@ -7684,3 +7700,99 @@ ActorIsolation swift::getConformanceIsolation(ProtocolConformance *conformance)
76847700

76857701
return getActorIsolation(nominal);
76867702
}
7703+
7704+
namespace {
7705+
/// Identifies isolated conformances whose isolation differs from the
7706+
/// context's isolation.
7707+
class MismatchedIsolatedConformances {
7708+
llvm::TinyPtrVector<ProtocolConformance *> badIsolatedConformances;
7709+
DeclContext *fromDC;
7710+
mutable std::optional<ActorIsolation> fromIsolation;
7711+
7712+
public:
7713+
MismatchedIsolatedConformances(const DeclContext *fromDC)
7714+
: fromDC(const_cast<DeclContext *>(fromDC)) { }
7715+
7716+
ActorIsolation getContextIsolation() const {
7717+
if (!fromIsolation)
7718+
fromIsolation = getActorIsolationOfContext(fromDC);
7719+
7720+
return *fromIsolation;
7721+
}
7722+
7723+
ArrayRef<ProtocolConformance *> getBadIsolatedConformances() const {
7724+
return badIsolatedConformances;
7725+
}
7726+
7727+
explicit operator bool() const { return !badIsolatedConformances.empty(); }
7728+
7729+
bool operator()(ProtocolConformanceRef conformance) {
7730+
if (conformance.isAbstract() || conformance.isPack())
7731+
return false;
7732+
7733+
auto concrete = conformance.getConcrete();
7734+
auto normal = dyn_cast<NormalProtocolConformance>(
7735+
concrete->getRootConformance());
7736+
if (!normal)
7737+
return false;
7738+
7739+
if (!normal->isIsolated())
7740+
return false;
7741+
7742+
auto conformanceIsolation = getConformanceIsolation(concrete);
7743+
if (conformanceIsolation == getContextIsolation())
7744+
return true;
7745+
7746+
badIsolatedConformances.push_back(concrete);
7747+
return false;
7748+
}
7749+
7750+
/// If there were any bad isolated conformances, diagnose them and return
7751+
/// true. Otherwise, returns false.
7752+
bool diagnose(SourceLoc loc) const {
7753+
if (badIsolatedConformances.empty())
7754+
return false;
7755+
7756+
ASTContext &ctx = fromDC->getASTContext();
7757+
auto firstConformance = badIsolatedConformances.front();
7758+
ctx.Diags.diagnose(
7759+
loc, diag::isolated_conformance_wrong_domain,
7760+
getConformanceIsolation(firstConformance),
7761+
firstConformance->getType(),
7762+
firstConformance->getProtocol()->getName(),
7763+
getContextIsolation());
7764+
return true;
7765+
}
7766+
};
7767+
7768+
}
7769+
7770+
bool swift::checkIsolatedConformancesInContext(
7771+
ConcreteDeclRef declRef, SourceLoc loc, const DeclContext *dc) {
7772+
MismatchedIsolatedConformances mismatched(dc);
7773+
forEachConformance(declRef, mismatched);
7774+
return mismatched.diagnose(loc);
7775+
}
7776+
7777+
bool swift::checkIsolatedConformancesInContext(
7778+
ArrayRef<ProtocolConformanceRef> conformances, SourceLoc loc,
7779+
const DeclContext *dc) {
7780+
MismatchedIsolatedConformances mismatched(dc);
7781+
for (auto conformance: conformances)
7782+
forEachConformance(conformance, mismatched);
7783+
return mismatched.diagnose(loc);
7784+
}
7785+
7786+
bool swift::checkIsolatedConformancesInContext(
7787+
SubstitutionMap subs, SourceLoc loc, const DeclContext *dc) {
7788+
MismatchedIsolatedConformances mismatched(dc);
7789+
forEachConformance(subs, mismatched);
7790+
return mismatched.diagnose(loc);
7791+
}
7792+
7793+
bool swift::checkIsolatedConformancesInContext(
7794+
Type type, SourceLoc loc, const DeclContext *dc) {
7795+
MismatchedIsolatedConformances mismatched(dc);
7796+
forEachConformance(type, mismatched);
7797+
return mismatched.diagnose(loc);
7798+
}

lib/Sema/TypeCheckConcurrency.h

+31
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,37 @@ void introduceUnsafeInheritExecutorReplacements(
703703
/// the immediate conformance, not any conformances on which it depends.
704704
ActorIsolation getConformanceIsolation(ProtocolConformance *conformance);
705705

706+
/// Check for correct use of isolated conformances in the given reference.
707+
///
708+
/// This checks that any isolated conformances that occur in the given
709+
/// declaration reference match the isolated of the context.
710+
bool checkIsolatedConformancesInContext(
711+
ConcreteDeclRef declRef, SourceLoc loc, const DeclContext *dc);
712+
713+
/// Check for correct use of isolated conformances in the set given set of
714+
/// protocol conformances.
715+
///
716+
/// This checks that any isolated conformances that occur in the given
717+
/// declaration reference match the isolated of the context.
718+
bool checkIsolatedConformancesInContext(
719+
ArrayRef<ProtocolConformanceRef> conformances, SourceLoc loc,
720+
const DeclContext *dc);
721+
722+
/// Check for correct use of isolated conformances in the given substitution
723+
/// map.
724+
///
725+
/// This checks that any isolated conformances that occur in the given
726+
/// substitution map match the isolated of the context.
727+
bool checkIsolatedConformancesInContext(
728+
SubstitutionMap subs, SourceLoc loc, const DeclContext *dc);
729+
730+
/// Check for correct use of isolated conformances in the given type.
731+
///
732+
/// This checks that any isolated conformances that occur in the given
733+
/// type match the isolated of the context.
734+
bool checkIsolatedConformancesInContext(
735+
Type type, SourceLoc loc, const DeclContext *dc);
736+
706737
} // end namespace swift
707738

708739
namespace llvm {

lib/Sema/TypeCheckProtocol.cpp

+139
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "swift/AST/GenericSignature.h"
4444
#include "swift/AST/NameLookup.h"
4545
#include "swift/AST/NameLookupRequests.h"
46+
#include "swift/AST/PackConformance.h"
4647
#include "swift/AST/ParameterList.h"
4748
#include "swift/AST/PotentialMacroExpansions.h"
4849
#include "swift/AST/PrettyStackTrace.h"
@@ -7159,3 +7160,141 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) {
71597160
req.getFirstType()->getCanonicalType(), requirementProto, conformance);
71607161
}
71617162
}
7163+
7164+
bool swift::forEachConformance(
7165+
SubstitutionMap subs,
7166+
llvm::function_ref<bool(ProtocolConformanceRef)> body,
7167+
VisitedConformances *visitedConformances) {
7168+
if (!subs)
7169+
return false;
7170+
7171+
VisitedConformances visited;
7172+
if (!visitedConformances)
7173+
visitedConformances = &visited;
7174+
7175+
for (auto type: subs.getReplacementTypes()) {
7176+
if (forEachConformance(type, body, visitedConformances))
7177+
return true;
7178+
}
7179+
7180+
for (auto conformance: subs.getConformances()) {
7181+
if (forEachConformance(conformance, body, visitedConformances))
7182+
return true;
7183+
}
7184+
7185+
return false;
7186+
}
7187+
7188+
bool swift::forEachConformance(
7189+
ProtocolConformanceRef conformance,
7190+
llvm::function_ref<bool(ProtocolConformanceRef)> body,
7191+
VisitedConformances *visitedConformances) {
7192+
// Make sure we can store visited conformances.
7193+
VisitedConformances visited;
7194+
if (!visitedConformances)
7195+
visitedConformances = &visited;
7196+
7197+
if (conformance.isInvalid() || conformance.isAbstract())
7198+
return false;
7199+
7200+
if (conformance.isPack()) {
7201+
auto pack = conformance.getPack()->getPatternConformances();
7202+
for (auto conformance : pack) {
7203+
if (forEachConformance(conformance, body, visitedConformances))
7204+
return true;
7205+
}
7206+
7207+
return false;
7208+
}
7209+
7210+
// Extract the concrete conformance.
7211+
auto concrete = conformance.getConcrete();
7212+
7213+
// Prevent recursion.
7214+
if (!visitedConformances->insert(concrete).second)
7215+
return false;
7216+
7217+
// Visit this conformance.
7218+
if (body(conformance))
7219+
return true;
7220+
7221+
// Check the substitution map within this conformance.
7222+
if (forEachConformance(concrete->getSubstitutionMap(), body,
7223+
visitedConformances))
7224+
return true;
7225+
7226+
return false;
7227+
}
7228+
7229+
bool swift::forEachConformance(
7230+
Type type, llvm::function_ref<bool(ProtocolConformanceRef)> body,
7231+
VisitedConformances *visitedConformances) {
7232+
// Make sure we can store visited conformances.
7233+
VisitedConformances visited;
7234+
if (!visitedConformances)
7235+
visitedConformances = &visited;
7236+
7237+
// Prevent recursion.
7238+
if (!visitedConformances->insert(type.getPointer()).second)
7239+
return false;
7240+
7241+
return type.findIf([&](Type type) {
7242+
if (auto typeAlias = dyn_cast<TypeAliasType>(type.getPointer())) {
7243+
if (forEachConformance(typeAlias->getSubstitutionMap(), body,
7244+
visitedConformances))
7245+
return true;
7246+
7247+
return false;
7248+
}
7249+
7250+
if (auto opaqueArchetype =
7251+
dyn_cast<OpaqueTypeArchetypeType>(type.getPointer())) {
7252+
if (forEachConformance(opaqueArchetype->getSubstitutions(), body,
7253+
visitedConformances))
7254+
return true;
7255+
7256+
return false;
7257+
}
7258+
7259+
// Look through type sugar.
7260+
if (auto sugarType = dyn_cast<SyntaxSugarType>(type.getPointer())) {
7261+
type = sugarType->getImplementationType();
7262+
}
7263+
7264+
if (auto boundGeneric = dyn_cast<BoundGenericType>(type.getPointer())) {
7265+
auto subs = boundGeneric->getContextSubstitutionMap();
7266+
if (forEachConformance(subs, body, visitedConformances))
7267+
return true;
7268+
7269+
return false;
7270+
}
7271+
7272+
return false;
7273+
});
7274+
}
7275+
7276+
bool swift::forEachConformance(
7277+
ConcreteDeclRef declRef,
7278+
llvm::function_ref<bool(ProtocolConformanceRef)> body,
7279+
VisitedConformances *visitedConformances) {
7280+
if (!declRef)
7281+
return false;
7282+
7283+
// Make sure we can store visited conformances.
7284+
VisitedConformances visited;
7285+
if (!visitedConformances)
7286+
visitedConformances = &visited;
7287+
7288+
Type type = declRef.getDecl()->getInterfaceType();
7289+
if (auto subs = declRef.getSubstitutions()) {
7290+
if (forEachConformance(subs, body, visitedConformances))
7291+
return true;
7292+
7293+
type = type.subst(subs);
7294+
}
7295+
7296+
if (forEachConformance(type, body, visitedConformances))
7297+
return true;
7298+
7299+
return false;
7300+
}

lib/Sema/TypeCheckProtocol.h

+38
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,44 @@ bool witnessHasImplementsAttrForRequiredName(ValueDecl *witness,
240240
bool witnessHasImplementsAttrForExactRequirement(ValueDecl *witness,
241241
ValueDecl *requirement);
242242

243+
using VisitedConformances = llvm::SmallPtrSet<void *, 16>;
244+
245+
/// Visit each conformance within the given type.
246+
///
247+
/// If `body` returns true for any conformance, this function stops the
248+
/// traversal and returns true.
249+
bool forEachConformance(
250+
Type type, llvm::function_ref<bool(ProtocolConformanceRef)> body,
251+
VisitedConformances *visitedConformances = nullptr);
252+
253+
/// Visit each conformance within the given conformance (including the given
254+
/// one).
255+
///
256+
/// If `body` returns true for any conformance, this function stops the
257+
/// traversal and returns true.
258+
bool forEachConformance(
259+
ProtocolConformanceRef conformance,
260+
llvm::function_ref<bool(ProtocolConformanceRef)> body,
261+
VisitedConformances *visitedConformances = nullptr);
262+
263+
/// Visit each conformance within the given substitution map.
264+
///
265+
/// If `body` returns true for any conformance, this function stops the
266+
/// traversal and returns true.
267+
bool forEachConformance(
268+
SubstitutionMap subs,
269+
llvm::function_ref<bool(ProtocolConformanceRef)> body,
270+
VisitedConformances *visitedConformances = nullptr);
271+
272+
/// Visit each conformance within the given declaration reference.
273+
///
274+
/// If `body` returns true for any conformance, this function stops the
275+
/// traversal and returns true.
276+
bool forEachConformance(
277+
ConcreteDeclRef declRef,
278+
llvm::function_ref<bool(ProtocolConformanceRef)> body,
279+
VisitedConformances *visitedConformances = nullptr);
280+
243281
}
244282

245283
#endif // SWIFT_SEMA_PROTOCOL_H

test/Concurrency/isolated_conformance.swift

+6
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,9 @@ func testIsolationConformancesInCall(c: C) {
119119
acceptSendableP(c) // expected-error{{isolated conformance of 'C' to 'P' cannot be used to satisfy conformance requirement for a `Sendable` type parameter}}
120120
acceptSendableMetaP(c) // expected-error{{isolated conformance of 'C' to 'P' cannot be used to satisfy conformance requirement for a `Sendable` type parameter}}
121121
}
122+
123+
func testIsolationConformancesFromOutside(c: C) {
124+
acceptP(c) // expected-error{{main actor-isolated isolated conformance of 'C' to 'P' cannot be used in nonisolated context}}
125+
let _: any P = c // expected-error{{main actor-isolated isolated conformance of 'C' to 'P' cannot be used in nonisolated context}}
126+
let _ = PWrapper<C>() // expected-error{{main actor-isolated isolated conformance of 'C' to 'P' cannot be used in nonisolated context}}
127+
}

0 commit comments

Comments
 (0)