Skip to content

Commit 6cdab78

Browse files
authored
Merge pull request #70867 from xedin/dynamic-enforcement-of-witness-isolation-with-preconcurrency
[TypeChecker/SILGen] Dynamic enforcement of witness/objc isolation with @preconcurrency attribute
2 parents 72ad6c5 + c3a0822 commit 6cdab78

37 files changed

+1002
-94
lines changed

include/swift/AST/ASTBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,7 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedTypeAttrKind : size_t {
11611161
BridgedTypeAttrKind_Sendable,
11621162
BridgedTypeAttrKind_retroactive,
11631163
BridgedTypeAttrKind_unchecked,
1164+
BridgedTypeAttrKind_preconcurrency,
11641165
BridgedTypeAttrKind__local,
11651166
BridgedTypeAttrKind__noMetadata,
11661167
BridgedTypeAttrKind__opaqueReturnTypeOf,

include/swift/AST/ASTContext.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,8 @@ class ASTContext final {
12821282
SourceLoc loc,
12831283
DeclContext *dc,
12841284
ProtocolConformanceState state,
1285-
bool isUnchecked);
1285+
bool isUnchecked,
1286+
bool isPreconcurrency);
12861287

12871288
/// Produce a self-conformance for the given protocol.
12881289
SelfProtocolConformance *

include/swift/AST/Attr.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ TYPE_ATTR(async)
5757
TYPE_ATTR(Sendable)
5858
TYPE_ATTR(retroactive)
5959
TYPE_ATTR(unchecked)
60+
TYPE_ATTR(preconcurrency)
6061
TYPE_ATTR(_local)
6162
TYPE_ATTR(_noMetadata)
6263
TYPE_ATTR(_opaqueReturnTypeOf)

include/swift/AST/Decl.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,11 +1604,15 @@ struct InheritedEntry : public TypeLoc {
16041604
/// Whether there was an @retroactive attribute.
16051605
bool isRetroactive = false;
16061606

1607+
/// Whether there was an @preconcurrency attribute.
1608+
bool isPreconcurrency = false;
1609+
16071610
InheritedEntry(const TypeLoc &typeLoc);
16081611

1609-
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive)
1610-
: TypeLoc(typeLoc), isUnchecked(isUnchecked), isRetroactive(isRetroactive) {
1611-
}
1612+
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive,
1613+
bool isPreconcurrency)
1614+
: TypeLoc(typeLoc), isUnchecked(isUnchecked),
1615+
isRetroactive(isRetroactive), isPreconcurrency(isPreconcurrency) {}
16121616
};
16131617

16141618
/// A wrapper for the collection of inherited types for either a `TypeDecl` or

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5628,6 +5628,15 @@ ERROR(unchecked_not_inheritance_clause,none,
56285628
ERROR(unchecked_not_existential,none,
56295629
"'unchecked' attribute cannot apply to non-protocol type %0", (Type))
56305630

5631+
WARNING(preconcurrency_conformance_not_used,none,
5632+
"@preconcurrency attribute on conformance to %0 has no effect", (Type))
5633+
ERROR(preconcurrency_not_inheritance_clause,none,
5634+
"'preconcurrency' attribute only applies in inheritance clauses", ())
5635+
ERROR(preconcurrency_not_existential,none,
5636+
"'preconcurrency' attribute cannot apply to non-protocol type %0", (Type))
5637+
ERROR(preconcurrency_attr_disabled,none,
5638+
"attribute requires '-enable-experimental-feature PreconcurrencyConformances'", ())
5639+
56315640
ERROR(redundant_any_in_existential,none,
56325641
"redundant 'any' in type %0",
56335642
(Type))

include/swift/AST/NameLookup.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -586,12 +586,15 @@ struct InheritedNominalEntry : Located<NominalTypeDecl *> {
586586
/// The location of the "unchecked" attribute, if present.
587587
SourceLoc uncheckedLoc;
588588

589+
/// The location of the "preconcurrency" attribute if present.
590+
SourceLoc preconcurrencyLoc;
591+
589592
InheritedNominalEntry() { }
590593

591-
InheritedNominalEntry(
592-
NominalTypeDecl *item, SourceLoc loc,
593-
SourceLoc uncheckedLoc
594-
) : Located(item, loc), uncheckedLoc(uncheckedLoc) { }
594+
InheritedNominalEntry(NominalTypeDecl *item, SourceLoc loc,
595+
SourceLoc uncheckedLoc, SourceLoc preconcurrencyLoc)
596+
: Located(item, loc), uncheckedLoc(uncheckedLoc),
597+
preconcurrencyLoc(preconcurrencyLoc) {}
595598
};
596599

597600
/// Retrieve the set of nominal type declarations that are directly

include/swift/AST/ProtocolConformance.h

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -444,11 +444,11 @@ class NormalProtocolConformance : public RootProtocolConformance,
444444

445445
// Flag bits used in ContextAndBits.
446446
enum {
447-
/// The conformance is invalid.
448-
InvalidFlag = 0x01,
449-
450447
/// The conformance was labeled with @unchecked.
451-
UncheckedFlag = 0x02,
448+
UncheckedFlag = 0x01,
449+
450+
/// The conformance was labeled with @preconcurrency.
451+
PreconcurrencyFlag = 0x02,
452452

453453
/// We have allocated the AssociatedConformances array (but not necessarily
454454
/// populated any of its elements).
@@ -458,10 +458,13 @@ class NormalProtocolConformance : public RootProtocolConformance,
458458
/// The declaration context containing the ExtensionDecl or
459459
/// NominalTypeDecl that declared the conformance.
460460
///
461-
/// Also stores the "invalid", "unchecked" and "has computed associated
461+
/// Also stores the "unchecked", "preconcurrency" and "has computed associated
462462
/// conformances" bits.
463463
llvm::PointerIntPair<DeclContext *, 3, unsigned> ContextAndBits;
464464

465+
/// Indicates whether the conformance is invalid.
466+
bool Invalid : 1;
467+
465468
/// The reason that this conformance exists.
466469
///
467470
/// Either Explicit (e.g. 'struct Foo: Protocol {}' or 'extension Foo:
@@ -501,12 +504,14 @@ class NormalProtocolConformance : public RootProtocolConformance,
501504
public:
502505
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
503506
SourceLoc loc, DeclContext *dc,
504-
ProtocolConformanceState state,
505-
bool isUnchecked)
507+
ProtocolConformanceState state, bool isUnchecked,
508+
bool isPreconcurrency)
506509
: RootProtocolConformance(ProtocolConformanceKind::Normal,
507510
conformingType),
508511
ProtocolAndState(protocol, state), Loc(loc),
509-
ContextAndBits(dc, isUnchecked ? UncheckedFlag : 0) {
512+
ContextAndBits(dc, ((isUnchecked ? UncheckedFlag : 0) |
513+
(isPreconcurrency ? PreconcurrencyFlag : 0))),
514+
Invalid(false) {
510515
assert(!conformingType->hasArchetype() &&
511516
"ProtocolConformances should store interface types");
512517
}
@@ -543,12 +548,12 @@ class NormalProtocolConformance : public RootProtocolConformance,
543548

544549
/// Determine whether this conformance is invalid.
545550
bool isInvalid() const {
546-
return ContextAndBits.getInt() & InvalidFlag;
551+
return Invalid;
547552
}
548553

549554
/// Mark this conformance as invalid.
550555
void setInvalid() {
551-
ContextAndBits.setInt(ContextAndBits.getInt() | InvalidFlag);
556+
Invalid = true;
552557
}
553558

554559
/// Whether this is an "unchecked" conformance.
@@ -563,6 +568,9 @@ class NormalProtocolConformance : public RootProtocolConformance,
563568
ContextAndBits.setInt(ContextAndBits.getInt() | UncheckedFlag);
564569
}
565570

571+
/// Whether this is an preconcurrency conformance.
572+
bool isPreconcurrency() const;
573+
566574
/// Determine whether we've lazily computed the associated conformance array
567575
/// already.
568576
bool hasComputedAssociatedConformances() const {

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ EXPERIMENTAL_FEATURE(GroupActorErrors, true)
270270
// Allow for the 'transferring' keyword to be applied to arguments and results.
271271
EXPERIMENTAL_FEATURE(TransferringArgsAndResults, true)
272272

273+
// Enable `@preconcurrency` attribute on protocol conformances.
274+
EXPERIMENTAL_FEATURE(PreconcurrencyConformances, false)
275+
273276
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
274277
#undef EXPERIMENTAL_FEATURE
275278
#undef UPCOMING_FEATURE

lib/AST/ASTContext.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,7 +2491,8 @@ ASTContext::getNormalConformance(Type conformingType,
24912491
SourceLoc loc,
24922492
DeclContext *dc,
24932493
ProtocolConformanceState state,
2494-
bool isUnchecked) {
2494+
bool isUnchecked,
2495+
bool isPreconcurrency) {
24952496
assert(dc->isTypeContext());
24962497

24972498
llvm::FoldingSetNodeID id;
@@ -2505,10 +2506,9 @@ ASTContext::getNormalConformance(Type conformingType,
25052506
return result;
25062507

25072508
// Build a new normal protocol conformance.
2508-
auto result
2509-
= new (*this, AllocationArena::Permanent)
2510-
NormalProtocolConformance(
2511-
conformingType, protocol, loc, dc, state,isUnchecked);
2509+
auto result = new (*this, AllocationArena::Permanent)
2510+
NormalProtocolConformance(conformingType, protocol, loc, dc, state,
2511+
isUnchecked, isPreconcurrency);
25122512
normalConformances.InsertNode(result, insertPos);
25132513

25142514
return result;

lib/AST/ASTPrinter.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2732,6 +2732,8 @@ void PrintAST::printInherited(const Decl *decl) {
27322732
if (inherited.isRetroactive &&
27332733
!llvm::is_contained(Options.ExcludeAttrList, TAK_retroactive))
27342734
Printer << "@retroactive ";
2735+
if (inherited.isPreconcurrency)
2736+
Printer << "@preconcurrency ";
27352737

27362738
printTypeLoc(inherited);
27372739
}, [&]() {
@@ -3906,6 +3908,30 @@ static bool usesFeatureBitwiseCopyable(Decl *decl) { return false; }
39063908

39073909
static bool usesFeatureTransferringArgsAndResults(Decl *decl) { return false; }
39083910

3911+
static bool usesFeaturePreconcurrencyConformances(Decl *decl) {
3912+
auto usesPreconcurrencyConformance = [&](const InheritedTypes &inherited) {
3913+
return llvm::any_of(
3914+
inherited.getEntries(),
3915+
[](const InheritedEntry &entry) { return entry.isPreconcurrency; });
3916+
};
3917+
3918+
if (auto *T = dyn_cast<TypeDecl>(decl))
3919+
return usesPreconcurrencyConformance(T->getInherited());
3920+
3921+
if (auto *E = dyn_cast<ExtensionDecl>(decl)) {
3922+
// If type has `@preconcurrency` conformance(s) all of its
3923+
// extensions have to be guarded by the flag too.
3924+
if (auto *T = dyn_cast<TypeDecl>(E->getExtendedNominal())) {
3925+
if (usesPreconcurrencyConformance(T->getInherited()))
3926+
return true;
3927+
}
3928+
3929+
return usesPreconcurrencyConformance(E->getInherited());
3930+
}
3931+
3932+
return false;
3933+
}
3934+
39093935
/// Suppress the printing of a particular feature.
39103936
static void suppressingFeature(PrintOptions &options, Feature feature,
39113937
llvm::function_ref<void()> action) {
@@ -8317,7 +8343,8 @@ swift::getInheritedForPrinting(
83178343

83188344
Results.push_back({TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()),
83198345
isUnchecked,
8320-
/*isRetroactive=*/false});
8346+
/*isRetroactive=*/false,
8347+
/*isPreconcurrency=*/false});
83218348
}
83228349
}
83238350

0 commit comments

Comments
 (0)