Skip to content

Align isolated conformances with the current proposal #79983

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 10 commits into from
Mar 14, 2025
Merged
Show file tree
Hide file tree
Changes from 8 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
22 changes: 17 additions & 5 deletions include/swift/AST/ConformanceAttributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

namespace swift {

class TypeExpr;

/// Describes all of the attributes that can occur on a conformance.
struct ConformanceAttributes {
/// The location of the "unchecked" attribute, if present.
Expand All @@ -28,9 +30,15 @@ struct ConformanceAttributes {
/// The location of the "unsafe" attribute if present.
SourceLoc unsafeLoc;

/// The location of the "@isolated" attribute if present.
SourceLoc isolatedLoc;

/// The location of the "nonisolated" modifier, if present.
SourceLoc nonisolatedLoc;

/// The location of the '@' prior to the global actor type.
SourceLoc globalActorAtLoc;

/// The global actor type to which this conformance is isolated.
TypeExpr *globalActorType = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this ought to be a TypeLoc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could. We get a TypeExpr out of the CustomAttr itself, so we can either stick that into a TypeLoc or keep it as-is. This way it's only a single pointer in ConformanceAttributes vs. the two-pointer TypeLoc, so I didn't do it.


/// Merge other conformance attributes into this set.
ConformanceAttributes &
operator |=(const ConformanceAttributes &other) {
Expand All @@ -40,8 +48,12 @@ struct ConformanceAttributes {
preconcurrencyLoc = other.preconcurrencyLoc;
if (other.unsafeLoc.isValid())
unsafeLoc = other.unsafeLoc;
if (other.isolatedLoc.isValid())
isolatedLoc = other.isolatedLoc;
if (other.nonisolatedLoc.isValid())
nonisolatedLoc = other.nonisolatedLoc;
if (other.globalActorType && !globalActorType) {
globalActorAtLoc = other.globalActorAtLoc;
globalActorType = other.globalActorType;
}
return *this;
}
};
Expand Down
16 changes: 12 additions & 4 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1806,16 +1806,20 @@ struct InheritedEntry : public TypeLoc {
/// This is true in cases like ~Copyable but not (P & ~Copyable).
bool IsSuppressed : 1;

/// The global actor isolation provided (for a conformance).
TypeExpr *globalActorIsolationType = nullptr;

public:
InheritedEntry(const TypeLoc &typeLoc);

InheritedEntry(const TypeLoc &typeLoc, ProtocolConformanceOptions options,
bool isSuppressed = false)
: TypeLoc(typeLoc), RawOptions(options.toRaw()),
IsSuppressed(isSuppressed) {}
IsSuppressed(isSuppressed),
globalActorIsolationType(options.getGlobalActorIsolationType()) {}

ProtocolConformanceOptions getOptions() const {
return ProtocolConformanceOptions(RawOptions);
return ProtocolConformanceOptions(RawOptions, globalActorIsolationType);
}

bool isUnchecked() const {
Expand All @@ -1827,8 +1831,12 @@ struct InheritedEntry : public TypeLoc {
bool isPreconcurrency() const {
return getOptions().contains(ProtocolConformanceFlags::Preconcurrency);
}
bool isIsolated() const {
return getOptions().contains(ProtocolConformanceFlags::Isolated);
bool isNonisolated() const {
return getOptions().contains(ProtocolConformanceFlags::Nonisolated);
}

TypeExpr *getGlobalActorIsolationType() const {
return globalActorIsolationType;
}

ExplicitSafety getExplicitSafety() const {
Expand Down
23 changes: 10 additions & 13 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -8319,31 +8319,28 @@ ERROR(attr_abi_incompatible_with_silgen_name,none,
//===----------------------------------------------------------------------===//
// 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))
"conformance of %0 to %1 depends on %2 conformance of %3 to %4; mark it as '%5'",
(Type, DeclName, ActorIsolation, Type, DeclName, StringRef))
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))
"add '%0' to the %1 conformance to restrict it to %2 code",
(StringRef, DeclName, ActorIsolation))
ERROR(isolated_conformance_with_sendable,none,
"isolated conformance of %0 to %1 cannot be used to satisfy conformance "
"%4 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))
"parameter %3", (Type, DeclName, bool, Type, ActorIsolation))
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))
"%2 conformance of %0 to %1 cannot be used to satisfy "
"conformance requirement for a `Sendable` type parameter ",
(Type, DeclName, ActorIsolation))
ERROR(isolated_conformance_wrong_domain,none,
"%0 isolated conformance of %1 to %2 cannot be used in %3 context",
"%0 conformance of %1 to %2 cannot be used in %3 context",
(ActorIsolation, Type, DeclName, ActorIsolation))

//===----------------------------------------------------------------------===//
Expand Down
24 changes: 18 additions & 6 deletions include/swift/AST/ProtocolConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
/// Otherwise a new conformance will be created.
ProtocolConformance *getCanonicalConformance();

/// Determine the actor isolation of this conformance.
ActorIsolation getIsolation() const;

/// Determine whether this conformance is isolated to an actor.
bool isIsolated() const {
return getIsolation().isActorIsolated();
}

/// Return true if the conformance has a witness for the given associated
/// type.
bool hasTypeWitness(AssociatedTypeDecl *assocType) const;
Expand Down Expand Up @@ -529,6 +537,7 @@ class NormalProtocolConformance : public RootProtocolConformance,
{
friend class ValueWitnessRequest;
friend class TypeWitnessRequest;
friend class ConformanceIsolationRequest;

/// The protocol being conformed to.
ProtocolDecl *Protocol;
Expand All @@ -546,6 +555,9 @@ class NormalProtocolConformance : public RootProtocolConformance,
/// NominalTypeDecl that declared the conformance.
DeclContext *Context;

/// The global actor isolation for this conformance, if there is one.
TypeExpr *globalActorIsolation = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this null most of the time? You can use a single bit to record the presence of this attribute, while storing the actual value in the request evaluator cache only when present.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's NULL most of the time. We could have a put and put it an ASTContext-managed side table, since it needs to be there at the point when the NormalProtocolConformance is created.


NormalProtocolConformance *ImplyingConformance = nullptr;

/// The mapping of individual requirements in the protocol over to
Expand All @@ -570,6 +582,9 @@ class NormalProtocolConformance : public RootProtocolConformance,

void resolveLazyInfo() const;

/// Set up global actor isolation for this conformance.
void setGlobalActorIsolation(Type globalActorType);

public:
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
SourceLoc loc, DeclContext *dc,
Expand All @@ -593,6 +608,7 @@ class NormalProtocolConformance : public RootProtocolConformance,
Bits.NormalProtocolConformance.HasComputedAssociatedConformances = false;
Bits.NormalProtocolConformance.SourceKind =
unsigned(ConformanceEntryKind::Explicit);
globalActorIsolation = options.getGlobalActorIsolationType();
}

/// Get the protocol being conformed to.
Expand Down Expand Up @@ -634,7 +650,8 @@ class NormalProtocolConformance : public RootProtocolConformance,
void setInvalid() { Bits.NormalProtocolConformance.IsInvalid = true; }

ProtocolConformanceOptions getOptions() const {
return ProtocolConformanceOptions(Bits.NormalProtocolConformance.Options);
return ProtocolConformanceOptions(Bits.NormalProtocolConformance.Options,
globalActorIsolation);
}

/// Whether this is an "unchecked" conformance.
Expand Down Expand Up @@ -669,11 +686,6 @@ class NormalProtocolConformance : public RootProtocolConformance,
return getOptions().contains(ProtocolConformanceFlags::Preconcurrency);
}

/// Whether this is an isolated conformance.
bool isIsolated() const {
return getOptions().contains(ProtocolConformanceFlags::Isolated);
}

/// Retrieve the location of `@preconcurrency`, if there is one and it is
/// known.
SourceLoc getPreconcurrencyLoc() const { return PreconcurrencyLoc; }
Expand Down
103 changes: 98 additions & 5 deletions include/swift/AST/ProtocolConformanceOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

namespace swift {

class TypeExpr;

/// Flags that describe extra attributes on protocol conformances.
enum class ProtocolConformanceFlags {
/// @unchecked conformance
Expand All @@ -34,16 +36,107 @@ enum class ProtocolConformanceFlags {
/// @retroactive conformance
Retroactive = 0x08,

/// @isolated conformance
Isolated = 0x10,
/// nonisolated, which suppresses and inferred global actor isolation
Nonisolated = 0x10,

/// The conformance is global-actor-isolated; the global actor will be
/// stored separately.
GlobalActorIsolated = 0x20,

// Note: whenever you add a bit here, update
// NumProtocolConformanceOptions below.
};

template<typename TheOptionSet>
struct OptionSetStorageType;

template<typename Flags, typename StorageType>
struct OptionSetStorageType<OptionSet<Flags, StorageType>> {
using Type = StorageType;
};

/// Options that describe extra attributes on protocol conformances.
using ProtocolConformanceOptions =
OptionSet<ProtocolConformanceFlags>;
class ProtocolConformanceOptions {
/// The set of options.
OptionSet<ProtocolConformanceFlags> options;

/// Global actor isolation for this conformance.
TypeExpr *globalActorIsolationType = nullptr;

public:
using StorageType =
OptionSetStorageType<OptionSet<ProtocolConformanceFlags>>::Type;

ProtocolConformanceOptions() { }

ProtocolConformanceOptions(ProtocolConformanceFlags flag)
: ProtocolConformanceOptions(static_cast<StorageType>(flag), nullptr) { }

ProtocolConformanceOptions(StorageType flagBits,
TypeExpr *globalActorIsolationType)
: options(flagBits), globalActorIsolationType(globalActorIsolationType) {
assert(options.contains(ProtocolConformanceFlags::GlobalActorIsolated) ==
(bool)globalActorIsolationType);
}

bool contains(ProtocolConformanceFlags flag) const {
return options.contains(flag);
}

TypeExpr *getGlobalActorIsolationType() const {
return globalActorIsolationType;
}

void setGlobalActorIsolation(TypeExpr *globalActorIsolationType) {
options |= ProtocolConformanceFlags::GlobalActorIsolated;
this->globalActorIsolationType = globalActorIsolationType;
}

/// Retrieve the raw bits for just the flags part of the options. You also
/// need to get the global actor isolation (separately) to reconstitute the
/// options.
StorageType toRaw() const {
return options.toRaw();
}

ProtocolConformanceOptions &operator|=(ProtocolConformanceFlags flag) {
assert(flag != ProtocolConformanceFlags::GlobalActorIsolated &&
"global actor isolation requires a type; use setGlobalActorIsolation");
options |= flag;
return *this;
}

ProtocolConformanceOptions &
operator|=(const ProtocolConformanceOptions &other) {
options |= other.options;
if (other.globalActorIsolationType && !globalActorIsolationType)
globalActorIsolationType = other.globalActorIsolationType;
return *this;
}

ProtocolConformanceOptions &operator-=(ProtocolConformanceFlags flag) {
options -= flag;
if (flag == ProtocolConformanceFlags::GlobalActorIsolated)
globalActorIsolationType = nullptr;
return *this;
}

friend ProtocolConformanceOptions operator|(
const ProtocolConformanceOptions &lhs,
const ProtocolConformanceOptions &rhs) {
ProtocolConformanceOptions result(lhs);
result |= rhs;
return result;
}

friend ProtocolConformanceOptions operator-(
const ProtocolConformanceOptions &lhs,
ProtocolConformanceFlags flag) {
ProtocolConformanceOptions result(lhs);
result -= flag;
return result;
}
};

inline ProtocolConformanceOptions operator|(
ProtocolConformanceFlags flag1,
Expand All @@ -52,7 +145,7 @@ inline ProtocolConformanceOptions operator|(
}

enum : unsigned {
NumProtocolConformanceOptions = 5
NumProtocolConformanceOptions = 6
};

} // end namespace swift
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ SIMPLE_TYPE_ATTR(_local, Local)
SIMPLE_TYPE_ATTR(_noMetadata, NoMetadata)
TYPE_ATTR(_opaqueReturnTypeOf, OpaqueReturnTypeOf)
TYPE_ATTR(isolated, Isolated)
SIMPLE_TYPE_ATTR(nonisolated, Nonisolated)
SIMPLE_TYPE_ATTR(_addressable, Addressable)
TYPE_ATTR(execution, Execution)

Expand Down
19 changes: 19 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,25 @@ class ConformanceHasEffectRequest :
bool isCached() const { return true; }
};

class ConformanceIsolationRequest :
public SimpleRequest<ConformanceIsolationRequest,
ActorIsolation(ProtocolConformance *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
ActorIsolation
evaluate(Evaluator &evaluator, ProtocolConformance *conformance) const;

public:
// Caching.
bool isCached() const;
};

/// Determine whether the given declaration is 'final'.
class IsFinalRequest :
public SimpleRequest<IsFinalRequest,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,9 @@ SWIFT_REQUEST(TypeChecker, PolymorphicEffectRequirementsRequest,
SWIFT_REQUEST(TypeChecker, ConformanceHasEffectRequest,
bool(EffectKind, ProtocolConformanceRef),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ConformanceIsolationRequest,
ActorIsolation(ProtocolConformance *), Cached,
NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ResolveTypeRequest,
Type (const TypeResolution *, TypeRepr *, GenericParamList *),
Uncached, NoLocationInfo)
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/TypeRepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr
/// or return an invalid source location if there is no such attribute.
SourceLoc findAttrLoc(TypeAttrKind kind) const;

/// Find a custom attribute within this type, or return NULL if there is
/// no such attribute.
CustomAttr *findCustomAttr() const;

/// Is this type grammatically a type-simple?
inline bool isSimple() const; // bottom of this file

Expand Down
Loading