Skip to content

Commit e8b7a26

Browse files
committed
[AST] Add a flag to indicate that the conformance is @preconcurrency
1 parent 4943e14 commit e8b7a26

14 files changed

+97
-48
lines changed

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/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 {

lib/AST/ASTContext.cpp

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

24962497
llvm::FoldingSetNodeID id;
@@ -2504,10 +2505,9 @@ ASTContext::getNormalConformance(Type conformingType,
25042505
return result;
25052506

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

25132513
return result;

lib/AST/ConformanceLookupTable.cpp

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,19 @@ void ConformanceLookupTable::destroy() {
137137

138138
namespace {
139139
struct ConformanceConstructionInfo : public Located<ProtocolDecl *> {
140-
/// The location of the "unchecked" attribute, if this
140+
/// The location of the "unchecked" attribute, if present.
141141
const SourceLoc uncheckedLoc;
142142

143+
/// The location of the "preconcurrency" attribute if present.
144+
const SourceLoc preconcurrencyLoc;
145+
143146
ConformanceConstructionInfo() { }
144147

145-
ConformanceConstructionInfo(
146-
ProtocolDecl *item, SourceLoc loc,
147-
SourceLoc uncheckedLoc
148-
) : Located(item, loc), uncheckedLoc(uncheckedLoc) { }
148+
ConformanceConstructionInfo(ProtocolDecl *item, SourceLoc loc,
149+
SourceLoc uncheckedLoc,
150+
SourceLoc preconcurrencyLoc)
151+
: Located(item, loc), uncheckedLoc(uncheckedLoc),
152+
preconcurrencyLoc(preconcurrencyLoc) {}
149153
};
150154
}
151155

@@ -200,15 +204,17 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage,
200204
loader.first->loadAllConformances(next, loader.second, conformances);
201205
loadAllConformances(next, conformances);
202206
for (auto conf : conformances) {
203-
protocols.push_back({conf->getProtocol(), SourceLoc(), SourceLoc()});
207+
protocols.push_back(
208+
{conf->getProtocol(), SourceLoc(), SourceLoc(), SourceLoc()});
204209
}
205210
} else if (next->getParentSourceFile() ||
206211
next->getParentModule()->isBuiltinModule()) {
207212
bool anyObject = false;
208213
for (const auto &found :
209214
getDirectlyInheritedNominalTypeDecls(next, anyObject)) {
210215
if (auto proto = dyn_cast<ProtocolDecl>(found.Item))
211-
protocols.push_back({proto, found.Loc, found.uncheckedLoc});
216+
protocols.push_back(
217+
{proto, found.Loc, found.uncheckedLoc, found.preconcurrencyLoc});
212218
}
213219
}
214220

@@ -294,15 +300,15 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
294300
addMacroGeneratedProtocols(
295301
nominal, ConformanceSource::forUnexpandedMacro(nominal));
296302
},
297-
[&](ExtensionDecl *ext,
298-
ArrayRef<ConformanceConstructionInfo> protos) {
303+
[&](ExtensionDecl *ext, ArrayRef<ConformanceConstructionInfo> protos) {
299304
// The extension decl may not be validated, so we can't use
300305
// its inherited protocols directly.
301306
auto source = ConformanceSource::forExplicit(ext);
302307
for (auto locAndProto : protos)
303308
addProtocol(
304-
locAndProto.Item, locAndProto.Loc,
305-
source.withUncheckedLoc(locAndProto.uncheckedLoc));
309+
locAndProto.Item, locAndProto.Loc,
310+
source.withUncheckedLoc(locAndProto.uncheckedLoc)
311+
.withPreconcurrencyLoc(locAndProto.preconcurrencyLoc));
306312
});
307313
break;
308314

@@ -495,8 +501,9 @@ void ConformanceLookupTable::addInheritedProtocols(
495501
for (const auto &found :
496502
getDirectlyInheritedNominalTypeDecls(decl, anyObject)) {
497503
if (auto proto = dyn_cast<ProtocolDecl>(found.Item)) {
498-
addProtocol(
499-
proto, found.Loc, source.withUncheckedLoc(found.uncheckedLoc));
504+
addProtocol(proto, found.Loc,
505+
source.withUncheckedLoc(found.uncheckedLoc)
506+
.withPreconcurrencyLoc(found.preconcurrencyLoc));
500507
}
501508
}
502509
}
@@ -953,10 +960,11 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
953960
}
954961

955962
// Create or find the normal conformance.
956-
auto normalConf =
957-
ctx.getNormalConformance(conformingType, protocol, conformanceLoc,
958-
conformingDC, ProtocolConformanceState::Incomplete,
959-
entry->Source.getUncheckedLoc().isValid());
963+
auto normalConf = ctx.getNormalConformance(
964+
conformingType, protocol, conformanceLoc, conformingDC,
965+
ProtocolConformanceState::Incomplete,
966+
entry->Source.getUncheckedLoc().isValid(),
967+
entry->Source.getPreconcurrencyLoc().isValid());
960968
// Invalid code may cause the getConformance call below to loop, so break
961969
// the infinite recursion by setting this eagerly to shortcircuit with the
962970
// early return at the start of this function.

lib/AST/ConformanceLookupTable.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
9191
/// The location of the "unchecked" attribute, if there is one.
9292
SourceLoc uncheckedLoc;
9393

94+
/// The location of the "preconcurrency" attribute, if there is one.
95+
SourceLoc preconcurrencyLoc;
96+
9497
ConformanceSource(void *ptr, ConformanceEntryKind kind)
9598
: Storage(ptr), Kind(kind) { }
9699

@@ -141,6 +144,15 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
141144
return result;
142145
}
143146

147+
/// Return a new conformance source with the given location of
148+
/// "@preconcurrency".
149+
ConformanceSource withPreconcurrencyLoc(SourceLoc preconcurrencyLoc) {
150+
ConformanceSource result(*this);
151+
if (preconcurrencyLoc.isValid())
152+
result.preconcurrencyLoc = preconcurrencyLoc;
153+
return result;
154+
}
155+
144156
/// Retrieve the kind of conformance formed from this source.
145157
ConformanceEntryKind getKind() const { return Kind; }
146158

@@ -184,6 +196,10 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
184196
return uncheckedLoc;
185197
}
186198

199+
SourceLoc getPreconcurrencyLoc() const {
200+
return preconcurrencyLoc;
201+
}
202+
187203
/// For an inherited conformance, retrieve the class declaration
188204
/// for the inheriting class.
189205
ClassDecl *getInheritingClass() const {

lib/AST/NameLookup.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3687,15 +3687,17 @@ void swift::getDirectlyInheritedNominalTypeDecls(
36873687
// InheritedDeclsReferencedRequest to make this work.
36883688
SourceLoc loc;
36893689
SourceLoc uncheckedLoc;
3690+
SourceLoc preconcurrencyLoc;
36903691
auto inheritedTypes = InheritedTypes(decl);
36913692
if (TypeRepr *typeRepr = inheritedTypes.getTypeRepr(i)) {
36923693
loc = typeRepr->getLoc();
36933694
uncheckedLoc = typeRepr->findAttrLoc(TAK_unchecked);
3695+
preconcurrencyLoc = typeRepr->findAttrLoc(TAK_preconcurrency);
36943696
}
36953697

36963698
// Form the result.
36973699
for (auto nominal : nominalTypes) {
3698-
result.push_back({nominal, loc, uncheckedLoc});
3700+
result.push_back({nominal, loc, uncheckedLoc, preconcurrencyLoc});
36993701
}
37003702
}
37013703

@@ -3733,7 +3735,7 @@ swift::getDirectlyInheritedNominalTypeDecls(
37333735
if (!req.getFirstType()->isEqual(protoSelfTy))
37343736
continue;
37353737

3736-
result.emplace_back(req.getProtocolDecl(), loc, SourceLoc());
3738+
result.emplace_back(req.getProtocolDecl(), loc, SourceLoc(), SourceLoc());
37373739
}
37383740
return result;
37393741
}
@@ -3744,16 +3746,17 @@ swift::getDirectlyInheritedNominalTypeDecls(
37443746
for (auto attr :
37453747
protoDecl->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
37463748
auto loc = attr->getLocation();
3747-
result.push_back(
3748-
{attr->getProtocol(), loc, attr->isUnchecked() ? loc : SourceLoc()});
3749+
result.push_back({attr->getProtocol(), loc,
3750+
attr->isUnchecked() ? loc : SourceLoc(),
3751+
/*preconcurrencyLoc=*/SourceLoc()});
37493752
}
37503753

37513754
// Else we have access to this information on the where clause.
37523755
auto selfBounds = getSelfBoundsFromWhereClause(decl);
37533756
anyObject |= selfBounds.anyObject;
37543757

37553758
for (auto inheritedNominal : selfBounds.decls)
3756-
result.emplace_back(inheritedNominal, loc, SourceLoc());
3759+
result.emplace_back(inheritedNominal, loc, SourceLoc(), SourceLoc());
37573760

37583761
return result;
37593762
}

lib/AST/ProtocolConformance.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,11 @@ bool NormalProtocolConformance::isResilient() const {
338338
return getDeclContext()->getParentModule()->isResilient();
339339
}
340340

341+
bool NormalProtocolConformance::isPreconcurrency() const {
342+
// The conformance is explicitly marked as `@preconcurrency`.
343+
return ContextAndBits.getInt() & PreconcurrencyFlag;
344+
}
345+
341346
llvm::Optional<ArrayRef<Requirement>>
342347
ProtocolConformance::getConditionalRequirementsIfAvailable() const {
343348
CONFORMANCE_SUBCLASS_DISPATCH(getConditionalRequirementsIfAvailable, ());

lib/ClangImporter/ImportDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9453,7 +9453,8 @@ void ClangImporter::Implementation::loadAllConformances(
94539453
dc->getDeclaredInterfaceType(),
94549454
protocol, SourceLoc(), dc,
94559455
ProtocolConformanceState::Incomplete,
9456-
protocol->isSpecificProtocol(KnownProtocolKind::Sendable));
9456+
protocol->isSpecificProtocol(KnownProtocolKind::Sendable),
9457+
/*isPreconcurrency=*/false);
94579458
conformance->setLazyLoader(this, /*context*/0);
94589459
conformance->setState(ProtocolConformanceState::Complete);
94599460
Conformances.push_back(conformance);

lib/Sema/CSDiagnostics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3456,7 +3456,8 @@ bool ContextualFailure::tryProtocolConformanceFixIt(
34563456
for (auto protocol : missingProtocols) {
34573457
auto conformance = NormalProtocolConformance(
34583458
nominal->getDeclaredType(), protocol, SourceLoc(), nominal,
3459-
ProtocolConformanceState::Incomplete, /*isUnchecked=*/false);
3459+
ProtocolConformanceState::Incomplete, /*isUnchecked=*/false,
3460+
/*isPreconcurrency=*/false);
34603461
ConformanceChecker checker(getASTContext(), &conformance,
34613462
missingWitnesses);
34623463
// Type witnesses must be resolved first.

lib/Sema/CodeSynthesisDistributedActor.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,8 @@ addDistributedActorCodableConformance(
811811
actor->getDeclaredInterfaceType(), proto,
812812
actor->getLoc(), /*dc=*/actor,
813813
ProtocolConformanceState::Incomplete,
814-
/*isUnchecked=*/false);
814+
/*isUnchecked=*/false,
815+
/*isPreconcurrency=*/false);
815816
conformance->setSourceKindAndImplyingConformance(
816817
ConformanceEntryKind::Synthesized, nullptr);
817818
actor->registerProtocolConformance(conformance, /*synthesized=*/true);

lib/Sema/TypeCheckBitwise.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,8 @@ DeriveImplicitBitwiseCopyableConformance::synthesizeConformance(
322322
auto conformance = context.getNormalConformance(
323323
nominal->getDeclaredInterfaceType(), protocol, nominal->getLoc(), dc,
324324
ProtocolConformanceState::Complete,
325-
/*isUnchecked=*/false);
325+
/*isUnchecked=*/false,
326+
/*isPreconcurrency=*/false);
326327
conformance->setSourceKindAndImplyingConformance(
327328
ConformanceEntryKind::Synthesized, nullptr);
328329

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5583,7 +5583,8 @@ ProtocolConformance *swift::deriveImplicitSendableConformance(
55835583
auto conformance = ctx.getNormalConformance(
55845584
nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(),
55855585
conformanceDC, ProtocolConformanceState::Complete,
5586-
/*isUnchecked=*/attrMakingUnavailable != nullptr);
5586+
/*isUnchecked=*/attrMakingUnavailable != nullptr,
5587+
/*isPreconcurrency=*/false);
55875588
conformance->setSourceKindAndImplyingConformance(
55885589
ConformanceEntryKind::Synthesized, nullptr);
55895590

lib/Sema/TypeCheckInvertible.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
396396
auto conformance = ctx.getNormalConformance(
397397
nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(),
398398
conformanceDC, ProtocolConformanceState::Complete,
399-
/*isUnchecked=*/false);
399+
/*isUnchecked=*/false, /*isPreconcurrency=*/false);
400400
conformance->setSourceKindAndImplyingConformance(
401401
ConformanceEntryKind::Synthesized, nullptr);
402402

0 commit comments

Comments
 (0)