Skip to content

Prohibit isolated conformances with Sendable(Metatype) constraints #79724

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
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
42 changes: 27 additions & 15 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2722,23 +2722,8 @@ WARNING(add_predates_concurrency_import,none,
GROUPED_WARNING(remove_predates_concurrency_import,PreconcurrencyImport,
DefaultIgnore,
"'@preconcurrency' attribute on module %0 has no effect", (Identifier))
NOTE(add_isolated_to_conformance,none,
"add 'isolated' to the %0 conformance to restrict it to %1 code",
(DeclName, ActorIsolation))
NOTE(add_preconcurrency_to_conformance,none,
"add '@preconcurrency' to the %0 conformance to defer isolation checking to run time", (DeclName))
ERROR(isolated_conformance_not_global_actor_isolated,none,
"isolated conformance is only permitted on global-actor-isolated types",
())
ERROR(isolated_conformance_experimental_feature,none,
"isolated conformances require experimental feature "
" 'IsolatedConformances'", ())
ERROR(nonisolated_conformance_depends_on_isolated_conformance,none,
"conformance of %0 to %1 depends on %2 conformance of %3 to %4; mark it as 'isolated'",
(Type, DeclName, ActorIsolation, Type, DeclName))
ERROR(isolated_conformance_mismatch_with_associated_isolation,none,
"%0 conformance of %1 to %2 cannot depend on %3 conformance of %4 to %5",
(ActorIsolation, Type, DeclName, ActorIsolation, Type, DeclName))
WARNING(remove_public_import,none,
"public import of %0 was not used in public declarations or inlinable code",
(Identifier))
Expand Down Expand Up @@ -8309,6 +8294,33 @@ ERROR(attr_abi_incompatible_with_silgen_name,none,
"the same purpose",
(DescriptiveDeclKind))

//===----------------------------------------------------------------------===//
// MARK: Isolated conformances
//===----------------------------------------------------------------------===//
ERROR(isolated_conformance_not_global_actor_isolated,none,
"isolated conformance is only permitted on global-actor-isolated types",
())
ERROR(isolated_conformance_experimental_feature,none,
"isolated conformances require experimental feature "
" 'IsolatedConformances'", ())
ERROR(nonisolated_conformance_depends_on_isolated_conformance,none,
"conformance of %0 to %1 depends on %2 conformance of %3 to %4; mark it as 'isolated'",
(Type, DeclName, ActorIsolation, Type, DeclName))
ERROR(isolated_conformance_mismatch_with_associated_isolation,none,
"%0 conformance of %1 to %2 cannot depend on %3 conformance of %4 to %5",
(ActorIsolation, Type, DeclName, ActorIsolation, Type, DeclName))
NOTE(add_isolated_to_conformance,none,
"add 'isolated' to the %0 conformance to restrict it to %1 code",
(DeclName, ActorIsolation))
ERROR(isolated_conformance_with_sendable,none,
"isolated conformance of %0 to %1 cannot be used to satisfy conformance "
"requirement for a %select{`Sendable`|`SendableMetatype`}2 type "
"parameter %3", (Type, DeclName, bool, Type))
ERROR(isolated_conformance_with_sendable_simple,none,
"isolated conformance of %0 to %1 cannot be used to satisfy conformance "
"requirement for a `Sendable` type parameter ",
(Type, DeclName))

//===----------------------------------------------------------------------===//
// MARK: @execution Attribute
//===----------------------------------------------------------------------===//
Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/ProtocolConformanceRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ class ProtocolConformanceRef {
bool forEachMissingConformance(
llvm::function_ref<bool(BuiltinProtocolConformance *missing)> fn) const;

/// Enumerate all of the isolated conformances in the given conformance.
///
/// The given `body` will be called on each isolated conformance. If it ever
/// returns `true`, this function will abort the search and return `true`.
bool forEachIsolatedConformance(
llvm::function_ref<bool(ProtocolConformance*)> body
) const;

using OpaqueValue = void*;
OpaqueValue getOpaqueValue() const { return Union.getOpaqueValue(); }
static ProtocolConformanceRef getFromOpaqueValue(OpaqueValue value) {
Expand Down
6 changes: 5 additions & 1 deletion include/swift/AST/Requirement.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,13 @@ class Requirement {
/// \param subReqs An out parameter initialized to a list of simpler
/// requirements which the caller must check to ensure this
/// requirement is completely satisfied.
/// \param isolatedConformances If non-NULL, will be provided with all of the
/// isolated conformances that
CheckRequirementResult checkRequirement(
SmallVectorImpl<Requirement> &subReqs,
bool allowMissing = false) const;
bool allowMissing = false,
SmallVectorImpl<ProtocolConformance *> *isolatedConformances = nullptr
) const;

/// Determines if this substituted requirement can ever be satisfied,
/// possibly with additional substitutions.
Expand Down
32 changes: 32 additions & 0 deletions include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,9 @@ enum class FixKind : uint8_t {
/// Ignore when an 'InlineArray' literal has mismatched number of elements to
/// the type it's attempting to bind to.
AllowInlineArrayLiteralCountMismatch,

/// Ignore that a conformance is isolated but is not allowed to be.
IgnoreIsolatedConformance,
};

class ConstraintFix {
Expand Down Expand Up @@ -3865,6 +3868,35 @@ class AllowInlineArrayLiteralCountMismatch final : public ConstraintFix {
}
};

class IgnoreIsolatedConformance : public ConstraintFix {
ProtocolConformance *conformance;

IgnoreIsolatedConformance(ConstraintSystem &cs,
ConstraintLocator *locator,
ProtocolConformance *conformance)
: ConstraintFix(cs, FixKind::IgnoreIsolatedConformance, locator),
conformance(conformance) { }

public:
std::string getName() const override {
return "ignore isolated conformance";
}

bool diagnose(const Solution &solution, bool asNote = false) const override;

bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}

static IgnoreIsolatedConformance *create(ConstraintSystem &cs,
ConstraintLocator *locator,
ProtocolConformance *conformance);

static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::IgnoreIsolatedConformance;
}
};

} // end namespace constraints
} // end namespace swift

Expand Down
4 changes: 4 additions & 0 deletions include/swift/Sema/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ enum class ConstraintKind : char {
/// class type).
SubclassOf,
/// The first type must conform to the second type (which is a
/// protocol type) and the conformance must not be an isolated conformance.
NonisolatedConformsTo,
/// The first type must conform to the second type (which is a
/// protocol type).
ConformsTo,
/// The first type describes a literal that conforms to the second
Expand Down Expand Up @@ -689,6 +692,7 @@ class Constraint final : public llvm::ilist_node<Constraint>,
case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::SubclassOf:
case ConstraintKind::ConformsTo:
case ConstraintKind::NonisolatedConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::TransitivelyConformsTo:
case ConstraintKind::CheckedCast:
Expand Down
4 changes: 3 additions & 1 deletion include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -3666,7 +3666,8 @@ class ConstraintSystem {

/// Add a requirement as a constraint to the constraint system.
void addConstraint(Requirement req, ConstraintLocatorBuilder locator,
bool isFavored = false);
bool isFavored,
bool prohibitNonisolatedConformance);

void addApplicationConstraint(
FunctionType *appliedFn, Type calleeType,
Expand Down Expand Up @@ -4363,6 +4364,7 @@ class ConstraintSystem {

// Record the given requirement in the constraint system.
void openGenericRequirement(DeclContext *outerDC,
GenericSignature signature,
unsigned index,
const Requirement &requirement,
bool skipProtocolSelfConstraint,
Expand Down
36 changes: 36 additions & 0 deletions lib/AST/ProtocolConformanceRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,42 @@ bool ProtocolConformanceRef::forEachMissingConformance(
return false;
}

bool ProtocolConformanceRef::forEachIsolatedConformance(
llvm::function_ref<bool(ProtocolConformance*)> body
) const {
if (isInvalid() || isAbstract())
return false;

if (isPack()) {
auto pack = getPack()->getPatternConformances();
for (auto conformance : pack) {
if (conformance.forEachIsolatedConformance(body))
return true;
}

return false;
}

// Is this an isolated conformance?
auto concrete = getConcrete();
if (auto normal =
dyn_cast<NormalProtocolConformance>(concrete->getRootConformance())) {
if (normal->isIsolated()) {
if (body(concrete))
return true;
}
}

// Check conformances that are part of this conformance.
auto subMap = concrete->getSubstitutionMap();
for (auto conformance : subMap.getConformances()) {
if (conformance.forEachIsolatedConformance(body))
return true;
}

return false;
}

void swift::simple_display(llvm::raw_ostream &out, ProtocolConformanceRef conformanceRef) {
if (conformanceRef.isAbstract()) {
simple_display(out, conformanceRef.getAbstract());
Expand Down
13 changes: 12 additions & 1 deletion lib/AST/Requirement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ ProtocolDecl *Requirement::getProtocolDecl() const {

CheckRequirementResult Requirement::checkRequirement(
SmallVectorImpl<Requirement> &subReqs,
bool allowMissing) const {
bool allowMissing,
SmallVectorImpl<ProtocolConformance *> *isolatedConformances
) const {
if (hasError())
return CheckRequirementResult::SubstitutionFailure;

Expand Down Expand Up @@ -122,6 +124,15 @@ CheckRequirementResult Requirement::checkRequirement(
if (!conformance)
return CheckRequirementResult::RequirementFailure;

// Collect isolated conformances.
if (isolatedConformances) {
conformance.forEachIsolatedConformance(
[&](ProtocolConformance *isolatedConformance) {
isolatedConformances->push_back(isolatedConformance);
return false;
});
}

auto condReqs = conformance.getConditionalRequirements();
if (condReqs.empty())
return CheckRequirementResult::Success;
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/RequirementMachine/RequirementLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ static void desugarSuperclassRequirement(

SmallVector<Requirement, 2> subReqs;

switch (req.checkRequirement(subReqs)) {
switch (req.checkRequirement(subReqs, /*allowMissing=*/false)) {
case CheckRequirementResult::Success:
case CheckRequirementResult::PackRequirement:
break;
Expand Down Expand Up @@ -333,7 +333,7 @@ static void desugarLayoutRequirement(

SmallVector<Requirement, 2> subReqs;

switch (req.checkRequirement(subReqs)) {
switch (req.checkRequirement(subReqs, /*allowMissing=*/false)) {
case CheckRequirementResult::Success:
case CheckRequirementResult::PackRequirement:
break;
Expand Down
2 changes: 1 addition & 1 deletion lib/IDE/IDETypeChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ struct SynthesizedExtensionAnalyzer::Implementation {

while (!subReqs.empty()) {
auto req = subReqs.pop_back_val();
switch (req.checkRequirement(subReqs)) {
switch (req.checkRequirement(subReqs, /*allowMissing=*/false)) {
case CheckRequirementResult::Success:
case CheckRequirementResult::PackRequirement:
case CheckRequirementResult::ConditionalConformance:
Expand Down
5 changes: 4 additions & 1 deletion lib/Sema/CSBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ BindingSet::BindingSet(ConstraintSystem &CS, TypeVariableType *TypeVar,

for (auto *constraint : info.Constraints) {
switch (constraint->getKind()) {
case ConstraintKind::NonisolatedConformsTo:
case ConstraintKind::ConformsTo:
if (constraint->getSecondType()->is<ProtocolType>())
Protocols.push_back(constraint);
Expand Down Expand Up @@ -256,7 +257,8 @@ bool BindingSet::isPotentiallyIncomplete() const {
// that's done as a last resort effort at resolving first member.
if (auto *constraint = binding.getSource()) {
if (binding.BindingType->is<ProtocolType>() &&
constraint->getKind() == ConstraintKind::ConformsTo)
(constraint->getKind() == ConstraintKind::ConformsTo ||
constraint->getKind() == ConstraintKind::NonisolatedConformsTo))
return true;
}
}
Expand Down Expand Up @@ -1941,6 +1943,7 @@ void PotentialBindings::infer(ConstraintSystem &CS,
case ConstraintKind::PackElementOf:
case ConstraintKind::SameShape:
case ConstraintKind::MaterializePackExpansion:
case ConstraintKind::NonisolatedConformsTo:
case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::Defaultable:
Expand Down
16 changes: 16 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9562,3 +9562,19 @@ bool IncorrectInlineArrayLiteralCount::diagnoseAsError() {
emitDiagnostic(diag::inlinearray_literal_incorrect_count, lhsCount, rhsCount);
return true;
}

bool DisallowedIsolatedConformance::diagnoseAsError() {
emitDiagnostic(diag::isolated_conformance_with_sendable_simple,
conformance->getType(),
conformance->getProtocol()->getName());

auto selectedOverload = getCalleeOverloadChoiceIfAvailable(getLocator());
if (!selectedOverload)
return true;

if (auto *decl = selectedOverload->choice.getDeclOrNull()) {
emitDiagnosticAt(decl, diag::decl_declared_here, decl);
}

return true;
}
15 changes: 15 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -3252,6 +3252,21 @@ class IncorrectInlineArrayLiteralCount final : public FailureDiagnostic {
bool diagnoseAsError() override;
};

/// Diagnose when an isolated conformance is used in a place where one cannot
/// be, e.g., due to a Sendable or SendableMetatype requirement on the
/// corresponding type parameter.
class DisallowedIsolatedConformance final : public FailureDiagnostic {
ProtocolConformance *conformance;

public:
DisallowedIsolatedConformance(const Solution &solution,
ProtocolConformance *conformance,
ConstraintLocator *locator)
: FailureDiagnostic(solution, locator), conformance(conformance) {}

bool diagnoseAsError() override;
};

} // end namespace constraints
} // end namespace swift

Expand Down
15 changes: 15 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2740,3 +2740,18 @@ bool AllowInlineArrayLiteralCountMismatch::diagnose(const Solution &solution,
getLocator());
return failure.diagnose(asNote);
}

IgnoreIsolatedConformance *
IgnoreIsolatedConformance::create(ConstraintSystem &cs,
ConstraintLocator *locator,
ProtocolConformance *conformance) {
assert(conformance && "Must have an isolated conformance");
return new (cs.getAllocator())
IgnoreIsolatedConformance(cs, locator, conformance);
}

bool IgnoreIsolatedConformance::diagnose(const Solution &solution,
bool asNote) const {
DisallowedIsolatedConformance failure(solution, conformance, getLocator());
return failure.diagnose(asNote);
}
Loading