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 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
8 changes: 8 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,14 @@ class ASTContext final {
struct Implementation;
Implementation &getImpl() const;

struct GlobalCache;

/// Retrieve a reference to the global cache within this ASTContext,
/// which is a place where we can stash side tables without having to
/// recompile the world every time we add a side table. See
/// "swift/AST/ASTContextGlobalCache.h"
GlobalCache &getGlobalCache() const;

friend ConstraintCheckerArenaRAII;

void operator delete(void *Data) throw();
Expand Down
34 changes: 34 additions & 0 deletions include/swift/AST/ASTContextGlobalCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===--- ASTContextGlobalCache.h - AST Context Cache ------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the ASTContext::GlobalCache type. DO NOT include this
// header from any other header: it should only be included in those .cpp
// files that need to access the side tables. There are no include guards to
// force the issue.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/ASTContext.h"

namespace swift {

/// A collection of side tables associated with the ASTContext itself, meant
// as
struct ASTContext::GlobalCache {
/// Mapping from normal protocol conformances to the explicitly-specified
/// global actor isolations, e.g., when the conformance was spelled
/// `@MainActor P` or similar.
llvm::DenseMap<const NormalProtocolConformance *, TypeExpr *>
conformanceExplicitGlobalActorIsolation;
};

} // end namespace
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
42 changes: 35 additions & 7 deletions include/swift/AST/ProtocolConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
SWIFT_INLINE_BITFIELD_EMPTY(RootProtocolConformance, ProtocolConformance);

SWIFT_INLINE_BITFIELD_FULL(NormalProtocolConformance, RootProtocolConformance,
1+1+1+
1+1+1+1+1+
bitmax(NumProtocolConformanceOptions,8)+
bitmax(NumProtocolConformanceStateBits,8)+
bitmax(NumConformanceEntryKindBits,8),
Expand All @@ -161,6 +161,12 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
/// this conformance.
IsPreconcurrencyEffectful : 1,

/// Whether the computed actor isolation is nonisolated.
IsComputedNonisolated : 1,

/// Whether there is an explicit global actor specified for this
/// conformance.
HasExplicitGlobalActor : 1,
: NumPadBits,

/// Options.
Expand Down Expand Up @@ -240,6 +246,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 +543,7 @@ class NormalProtocolConformance : public RootProtocolConformance,
{
friend class ValueWitnessRequest;
friend class TypeWitnessRequest;
friend class ConformanceIsolationRequest;

/// The protocol being conformed to.
ProtocolDecl *Protocol;
Expand Down Expand Up @@ -570,6 +585,20 @@ class NormalProtocolConformance : public RootProtocolConformance,

void resolveLazyInfo() const;

/// Retrieve the explicitly-specified global actor isolation.
TypeExpr *getExplicitGlobalActorIsolation() const;

// Record the explicitly-specified global actor isolation.
void setExplicitGlobalActorIsolation(TypeExpr *typeExpr);

bool isComputedNonisolated() const {
return Bits.NormalProtocolConformance.IsComputedNonisolated;
}

void setComputedNonnisolated(bool value = true) {
Bits.NormalProtocolConformance.IsComputedNonisolated = value;
}

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

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

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

/// Whether this is an "unchecked" conformance.
Expand Down Expand Up @@ -669,11 +702,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
Loading