Skip to content

[SE-0470] Include isolated conformance checks for default value expressions #81450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1723,7 +1723,8 @@ bool swift::hasLetStoredPropertyWithInitialValue(NominalTypeDecl *nominal) {
});
}

/// Determine whether a synth
/// Determine whether a synthesized requirement for the given conformance
/// should be explicitly marked as 'nonisolated'.
static bool synthesizedRequirementIsNonIsolated(
const NormalProtocolConformance *conformance) {
// @preconcurrency suppresses this.
Expand Down
71 changes: 58 additions & 13 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2823,6 +2823,29 @@ namespace {
}
}

/// Function object that refines isolation for each actor isolation it is
/// given, returning true if all of the provided isolations have been
/// accounted for, or false if the caller should handle them.
class RefineConformances {
ActorIsolationChecker &self;

public:
RefineConformances(ActorIsolationChecker &self) : self(self) { }

bool operator()(ArrayRef<ActorIsolation> isolations) const {
bool anyRefined = false;
bool anyUnrefined = false;
for (const auto &isolation : isolations) {
if (self.refineRequiredIsolation(isolation))
anyRefined = true;
else
anyUnrefined = true;
}

return anyRefined && !anyUnrefined;
}
};

bool refineRequiredIsolation(ActorIsolation refinedIsolation) {
if (requiredIsolationLoc.isInvalid())
return false;
Expand Down Expand Up @@ -3332,13 +3355,13 @@ namespace {
if (auto erasureExpr = dyn_cast<ErasureExpr>(expr)) {
checkIsolatedConformancesInContext(
erasureExpr->getConformances(), erasureExpr->getLoc(),
getDeclContext());
getDeclContext(), RefineConformances{*this});
}

if (auto *underlyingToOpaque = dyn_cast<UnderlyingToOpaqueExpr>(expr)) {
checkIsolatedConformancesInContext(
underlyingToOpaque->substitutions, underlyingToOpaque->getLoc(),
getDeclContext());
getDeclContext(), RefineConformances{*this});
}

return Action::Continue(expr);
Expand Down Expand Up @@ -4447,7 +4470,8 @@ namespace {
return false;

// Make sure isolated conformances are formed in the right context.
checkIsolatedConformancesInContext(declRef, loc, getDeclContext());
checkIsolatedConformancesInContext(declRef, loc, getDeclContext(),
RefineConformances{*this});

auto *const decl = declRef.getDecl();

Expand Down Expand Up @@ -8015,11 +8039,14 @@ namespace {
class MismatchedIsolatedConformances {
llvm::TinyPtrVector<ProtocolConformance *> badIsolatedConformances;
DeclContext *fromDC;
HandleConformanceIsolationFn handleBad;
mutable std::optional<ActorIsolation> fromIsolation;

public:
MismatchedIsolatedConformances(const DeclContext *fromDC)
: fromDC(const_cast<DeclContext *>(fromDC)) { }
MismatchedIsolatedConformances(const DeclContext *fromDC,
HandleConformanceIsolationFn handleBad)
: fromDC(const_cast<DeclContext *>(fromDC)),
handleBad(handleBad) { }

ActorIsolation getContextIsolation() const {
if (!fromIsolation)
Expand Down Expand Up @@ -8059,6 +8086,16 @@ namespace {
if (badIsolatedConformances.empty())
return false;

if (handleBad) {
// Capture all of the actor isolations from the conformances.
std::vector<ActorIsolation> badIsolations;
for (auto conformance : badIsolatedConformances)
badIsolations.push_back(conformance->getIsolation());

if (handleBad(badIsolations))
return false;
}

ASTContext &ctx = fromDC->getASTContext();
auto firstConformance = badIsolatedConformances.front();
ctx.Diags.diagnose(
Expand All @@ -8074,32 +8111,40 @@ namespace {

}

bool swift::doNotDiagnoseConformanceIsolation(ArrayRef<ActorIsolation>) {
return false;
}

bool swift::checkIsolatedConformancesInContext(
ConcreteDeclRef declRef, SourceLoc loc, const DeclContext *dc) {
MismatchedIsolatedConformances mismatched(dc);
ConcreteDeclRef declRef, SourceLoc loc, const DeclContext *dc,
HandleConformanceIsolationFn handleBad) {
MismatchedIsolatedConformances mismatched(dc, handleBad);
forEachConformance(declRef, mismatched);
return mismatched.diagnose(loc);
}

bool swift::checkIsolatedConformancesInContext(
ArrayRef<ProtocolConformanceRef> conformances, SourceLoc loc,
const DeclContext *dc) {
MismatchedIsolatedConformances mismatched(dc);
const DeclContext *dc,
HandleConformanceIsolationFn handleBad) {
MismatchedIsolatedConformances mismatched(dc, handleBad);
for (auto conformance: conformances)
forEachConformance(conformance, mismatched);
return mismatched.diagnose(loc);
}

bool swift::checkIsolatedConformancesInContext(
SubstitutionMap subs, SourceLoc loc, const DeclContext *dc) {
MismatchedIsolatedConformances mismatched(dc);
SubstitutionMap subs, SourceLoc loc, const DeclContext *dc,
HandleConformanceIsolationFn handleBad) {
MismatchedIsolatedConformances mismatched(dc, handleBad);
forEachConformance(subs, mismatched);
return mismatched.diagnose(loc);
}

bool swift::checkIsolatedConformancesInContext(
Type type, SourceLoc loc, const DeclContext *dc) {
MismatchedIsolatedConformances mismatched(dc);
Type type, SourceLoc loc, const DeclContext *dc,
HandleConformanceIsolationFn handleBad) {
MismatchedIsolatedConformances mismatched(dc, handleBad);
forEachConformance(type, mismatched);
return mismatched.diagnose(loc);
}
21 changes: 17 additions & 4 deletions lib/Sema/TypeCheckConcurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -734,12 +734,22 @@ void introduceUnsafeInheritExecutorReplacements(
void introduceUnsafeInheritExecutorReplacements(
const DeclContext *dc, Type base, SourceLoc loc, LookupResult &result);

/// Function that attempts to handle all of the "bad" conformance isolation
/// found somewhere, and returns true if it handled them. If not, returns
/// false so that the conformances can be diagnose.
using HandleConformanceIsolationFn =
llvm::function_ref<bool(ArrayRef<ActorIsolation>)>;

/// Function used as a default HandleConformanceIsolationFn.
bool doNotDiagnoseConformanceIsolation(ArrayRef<ActorIsolation>);

/// Check for correct use of isolated conformances in the given reference.
///
/// This checks that any isolated conformances that occur in the given
/// declaration reference match the isolated of the context.
bool checkIsolatedConformancesInContext(
ConcreteDeclRef declRef, SourceLoc loc, const DeclContext *dc);
ConcreteDeclRef declRef, SourceLoc loc, const DeclContext *dc,
HandleConformanceIsolationFn handleBad = doNotDiagnoseConformanceIsolation);

/// Check for correct use of isolated conformances in the set given set of
/// protocol conformances.
Expand All @@ -748,22 +758,25 @@ bool checkIsolatedConformancesInContext(
/// declaration reference match the isolated of the context.
bool checkIsolatedConformancesInContext(
ArrayRef<ProtocolConformanceRef> conformances, SourceLoc loc,
const DeclContext *dc);
const DeclContext *dc,
HandleConformanceIsolationFn handleBad = doNotDiagnoseConformanceIsolation);

/// Check for correct use of isolated conformances in the given substitution
/// map.
///
/// This checks that any isolated conformances that occur in the given
/// substitution map match the isolated of the context.
bool checkIsolatedConformancesInContext(
SubstitutionMap subs, SourceLoc loc, const DeclContext *dc);
SubstitutionMap subs, SourceLoc loc, const DeclContext *dc,
HandleConformanceIsolationFn handleBad = doNotDiagnoseConformanceIsolation);

/// Check for correct use of isolated conformances in the given type.
///
/// This checks that any isolated conformances that occur in the given
/// type match the isolated of the context.
bool checkIsolatedConformancesInContext(
Type type, SourceLoc loc, const DeclContext *dc);
Type type, SourceLoc loc, const DeclContext *dc,
HandleConformanceIsolationFn handleBad = doNotDiagnoseConformanceIsolation);

} // end namespace swift

Expand Down
4 changes: 4 additions & 0 deletions test/Concurrency/isolated_conformance_default_actor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ class CodableClass: Codable {
var state: Int = 0
}

class OtherClass {
var otherState: any Encodable.Type = CodableClass.self
}

func acceptSendablePMeta<T: Sendable & P>(_: T.Type) { }
func acceptSendableQMeta<T: Sendable & Q>(_: T.Type) { }

Expand Down
19 changes: 19 additions & 0 deletions test/Concurrency/isolated_conformance_inference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,22 @@ nonisolated func testConformancesFromNonisolated(tdc: TestDerivedCodable) {

let _: any Codable = tdc
}

protocol P2 {
func g()
}

struct DifferingConformances: @MainActor P {
@MainActor func f() { }
}

extension DifferingConformances: @SomeGlobalActor P2 {
@SomeGlobalActor func g() { }
}

@MainActor
class InferMeDefaults {
var mainState: any P.Type = DifferingConformances.self
var someGlobalActorState: any P2.Type = DifferingConformances.self // expected-error{{global actor 'SomeGlobalActor'-isolated default value in a main actor-isolated context}}
var bothState: any (P & P2).Type = DifferingConformances.self // expected-error{{default argument cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}}
}