Skip to content

Commit 8092420

Browse files
committed
[SE-0302] Implement '@unchecked Sendable' syntax.
Parse and provide semantic checking for '@unchecked Sendable', for a Sendable conformance that doesn't perform additional semantic checks for correctness. Part of rdar://78269000. (cherry picked from commit 29f5d7a)
1 parent e206faa commit 8092420

24 files changed

+201
-70
lines changed

include/swift/AST/ASTContext.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,8 @@ class ASTContext final {
987987
ProtocolDecl *protocol,
988988
SourceLoc loc,
989989
DeclContext *dc,
990-
ProtocolConformanceState state);
990+
ProtocolConformanceState state,
991+
bool isUnchecked);
991992

992993
/// Produce a self-conformance for the given protocol.
993994
SelfProtocolConformance *

include/swift/AST/Attr.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ TYPE_ATTR(differentiable)
5555
TYPE_ATTR(noDerivative)
5656
TYPE_ATTR(async)
5757
TYPE_ATTR(Sendable)
58+
TYPE_ATTR(unchecked)
5859

5960
// SIL-specific attributes
6061
TYPE_ATTR(block_storage)

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4490,16 +4490,17 @@ ERROR(concurrent_value_class_mutable_property,none,
44904490
(DeclName, DescriptiveDeclKind, DeclName))
44914491
ERROR(concurrent_value_outside_source_file,none,
44924492
"conformance to 'Sendable' must occur in the same source file as "
4493-
"%0 %1; use 'UnsafeSendable' for retroactive conformance",
4493+
"%0 %1; use '@unchecked Sendable' for retroactive conformance",
44944494
(DescriptiveDeclKind, DeclName))
44954495
ERROR(concurrent_value_nonfinal_class,none,
44964496
"non-final class %0 cannot conform to `Sendable`; "
4497-
"use `UnsafeSendable`", (DeclName))
4497+
"use `@unchecked Sendable`", (DeclName))
44984498
ERROR(concurrent_value_inherit,none,
44994499
"`Sendable` class %1 cannot inherit from another class"
45004500
"%select{| other than 'NSObject'}0",
45014501
(bool, DeclName))
45024502

4503+
45034504
ERROR(actorindependent_let,none,
45044505
"'@actorIndependent' is meaningless on 'let' declarations because "
45054506
"they are immutable",
@@ -4511,6 +4512,13 @@ ERROR(actorindependent_local_var,none,
45114512
"'@actorIndependent' can not be applied to local variables",
45124513
())
45134514

4515+
WARNING(unchecked_conformance_not_special,none,
4516+
"@unchecked conformance to %0 has no meaning", (Type))
4517+
ERROR(unchecked_not_inheritance_clause,none,
4518+
"'unchecked' attribute only applies in inheritance clauses", ())
4519+
ERROR(unchecked_not_existential,none,
4520+
"'unchecked' attribute cannot apply to non-protocol type %0", (Type))
4521+
45144522
ERROR(nonisolated_let,none,
45154523
"'nonisolated' is meaningless on 'let' declarations because "
45164524
"they are immutable",

include/swift/AST/NameLookup.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,19 @@ void filterForDiscriminator(SmallVectorImpl<Result> &results,
503503

504504
} // end namespace namelookup
505505

506+
/// Describes an inherited nominal entry.
507+
struct InheritedNominalEntry : Located<NominalTypeDecl *> {
508+
/// The location of the "unchecked" attribute, if present.
509+
SourceLoc uncheckedLoc;
510+
511+
InheritedNominalEntry() { }
512+
513+
InheritedNominalEntry(
514+
NominalTypeDecl *item, SourceLoc loc,
515+
SourceLoc uncheckedLoc
516+
) : Located(item, loc), uncheckedLoc(uncheckedLoc) { }
517+
};
518+
506519
/// Retrieve the set of nominal type declarations that are directly
507520
/// "inherited" by the given declaration at a particular position in the
508521
/// list of "inherited" types.
@@ -511,15 +524,15 @@ void filterForDiscriminator(SmallVectorImpl<Result> &results,
511524
/// AnyObject type, set \c anyObject true.
512525
void getDirectlyInheritedNominalTypeDecls(
513526
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
514-
unsigned i, llvm::SmallVectorImpl<Located<NominalTypeDecl *>> &result,
527+
unsigned i, llvm::SmallVectorImpl<InheritedNominalEntry> &result,
515528
bool &anyObject);
516529

517530
/// Retrieve the set of nominal type declarations that are directly
518531
/// "inherited" by the given declaration, looking through typealiases
519532
/// and splitting out the components of compositions.
520533
///
521534
/// If we come across the AnyObject type, set \c anyObject true.
522-
SmallVector<Located<NominalTypeDecl *>, 4> getDirectlyInheritedNominalTypeDecls(
535+
SmallVector<InheritedNominalEntry, 4> getDirectlyInheritedNominalTypeDecls(
523536
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
524537
bool &anyObject);
525538

include/swift/AST/ProtocolConformance.h

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,20 @@ class NormalProtocolConformance : public RootProtocolConformance,
412412
/// The location of this protocol conformance in the source.
413413
SourceLoc Loc;
414414

415+
// Flag bits used in ContextAndBits.
416+
enum {
417+
/// The conformance is invalid.
418+
InvalidFlag = 0x01,
419+
420+
/// The conformance was labeled with @unchecked.
421+
UncheckedFlag = 0x02,
422+
};
423+
415424
/// The declaration context containing the ExtensionDecl or
416425
/// NominalTypeDecl that declared the conformance.
417426
///
418-
/// Also stores the "invalid" bit.
419-
llvm::PointerIntPair<DeclContext *, 1, bool> ContextAndInvalid;
427+
/// Also stores the "invalid" and "unchecked" bits.
428+
llvm::PointerIntPair<DeclContext *, 2, unsigned> ContextAndBits;
420429

421430
/// The reason that this conformance exists.
422431
///
@@ -457,11 +466,12 @@ class NormalProtocolConformance : public RootProtocolConformance,
457466
public:
458467
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
459468
SourceLoc loc, DeclContext *dc,
460-
ProtocolConformanceState state)
469+
ProtocolConformanceState state,
470+
bool isUnchecked)
461471
: RootProtocolConformance(ProtocolConformanceKind::Normal,
462472
conformingType),
463473
ProtocolAndState(protocol, state), Loc(loc),
464-
ContextAndInvalid(dc, false) {
474+
ContextAndBits(dc, isUnchecked ? UncheckedFlag : 0) {
465475
assert(!conformingType->hasArchetype() &&
466476
"ProtocolConformances should store interface types");
467477
}
@@ -475,7 +485,7 @@ class NormalProtocolConformance : public RootProtocolConformance,
475485
/// Get the declaration context that contains the conforming extension or
476486
/// nominal type declaration.
477487
DeclContext *getDeclContext() const {
478-
return ContextAndInvalid.getPointer();
488+
return ContextAndBits.getPointer();
479489
}
480490

481491
/// Get any additional requirements that are required for this conformance to
@@ -497,15 +507,20 @@ class NormalProtocolConformance : public RootProtocolConformance,
497507

498508
/// Determine whether this conformance is invalid.
499509
bool isInvalid() const {
500-
return ContextAndInvalid.getInt();
510+
return ContextAndBits.getInt() & InvalidFlag;
501511
}
502512

503513
/// Mark this conformance as invalid.
504514
void setInvalid() {
505-
ContextAndInvalid.setInt(true);
515+
ContextAndBits.setInt(ContextAndBits.getInt() | InvalidFlag);
506516
SignatureConformances = {};
507517
}
508518

519+
/// Whether this is an "unchecked" conformance.
520+
bool isUnchecked() const {
521+
return ContextAndBits.getInt() & UncheckedFlag;
522+
}
523+
509524
/// Get the kind of source from which this conformance comes.
510525
ConformanceEntryKind getSourceKind() const {
511526
return SourceKindAndImplyingConformance.getInt();

lib/AST/ASTContext.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,7 +2085,8 @@ ASTContext::getConformance(Type conformingType,
20852085
ProtocolDecl *protocol,
20862086
SourceLoc loc,
20872087
DeclContext *dc,
2088-
ProtocolConformanceState state) {
2088+
ProtocolConformanceState state,
2089+
bool isUnchecked) {
20892090
assert(dc->isTypeContext());
20902091

20912092
llvm::FoldingSetNodeID id;
@@ -2101,7 +2102,8 @@ ASTContext::getConformance(Type conformingType,
21012102
// Build a new normal protocol conformance.
21022103
auto result
21032104
= new (*this, AllocationArena::Permanent)
2104-
NormalProtocolConformance(conformingType, protocol, loc, dc, state);
2105+
NormalProtocolConformance(
2106+
conformingType, protocol, loc, dc, state,isUnchecked);
21052107
normalConformances.InsertNode(result, insertPos);
21062108

21072109
return result;

lib/AST/ConformanceLookupTable.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,17 @@ void ConformanceLookupTable::destroy() {
141141
}
142142

143143
namespace {
144-
using ConformanceConstructionInfo = Located<ProtocolDecl *>;
144+
struct ConformanceConstructionInfo : public Located<ProtocolDecl *> {
145+
/// The location of the "unchecked" attribute, if this
146+
const SourceLoc uncheckedLoc;
147+
148+
ConformanceConstructionInfo() { }
149+
150+
ConformanceConstructionInfo(
151+
ProtocolDecl *item, SourceLoc loc,
152+
SourceLoc uncheckedLoc
153+
) : Located(item, loc), uncheckedLoc(uncheckedLoc) { }
154+
};
145155
}
146156

147157
template<typename NominalFunc, typename ExtensionFunc>
@@ -195,14 +205,14 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage,
195205
loader.first->loadAllConformances(next, loader.second, conformances);
196206
loadAllConformances(next, conformances);
197207
for (auto conf : conformances) {
198-
protocols.push_back({conf->getProtocol(), SourceLoc()});
208+
protocols.push_back({conf->getProtocol(), SourceLoc(), SourceLoc()});
199209
}
200210
} else if (next->getParentSourceFile()) {
201211
bool anyObject = false;
202212
for (const auto &found :
203213
getDirectlyInheritedNominalTypeDecls(next, anyObject)) {
204214
if (auto proto = dyn_cast<ProtocolDecl>(found.Item))
205-
protocols.push_back({proto, found.Loc});
215+
protocols.push_back({proto, found.Loc, found.uncheckedLoc});
206216
}
207217
}
208218

@@ -282,7 +292,9 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
282292
// its inherited protocols directly.
283293
auto source = ConformanceSource::forExplicit(ext);
284294
for (auto locAndProto : protos)
285-
addProtocol(locAndProto.Item, locAndProto.Loc, source);
295+
addProtocol(
296+
locAndProto.Item, locAndProto.Loc,
297+
source.withUncheckedLoc(locAndProto.uncheckedLoc));
286298
});
287299
break;
288300

@@ -471,8 +483,10 @@ void ConformanceLookupTable::addInheritedProtocols(
471483
bool anyObject = false;
472484
for (const auto &found :
473485
getDirectlyInheritedNominalTypeDecls(decl, anyObject)) {
474-
if (auto proto = dyn_cast<ProtocolDecl>(found.Item))
475-
addProtocol(proto, found.Loc, source);
486+
if (auto proto = dyn_cast<ProtocolDecl>(found.Item)) {
487+
addProtocol(
488+
proto, found.Loc, source.withUncheckedLoc(found.uncheckedLoc));
489+
}
476490
}
477491
}
478492

@@ -843,7 +857,8 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
843857

844858
auto normalConf =
845859
ctx.getConformance(conformingType, protocol, conformanceLoc,
846-
conformingDC, ProtocolConformanceState::Incomplete);
860+
conformingDC, ProtocolConformanceState::Incomplete,
861+
entry->Source.getUncheckedLoc().isValid());
847862
// Invalid code may cause the getConformance call below to loop, so break
848863
// the infinite recursion by setting this eagerly to shortcircuit with the
849864
// 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
@@ -85,6 +85,9 @@ class ConformanceLookupTable {
8585
class ConformanceSource {
8686
llvm::PointerIntPair<void *, 2, ConformanceEntryKind> Storage;
8787

88+
/// The location of the "unchecked" attribute, if there is one.
89+
SourceLoc uncheckedLoc;
90+
8891
ConformanceSource(void *ptr, ConformanceEntryKind kind)
8992
: Storage(ptr, kind) { }
9093

@@ -123,6 +126,14 @@ class ConformanceLookupTable {
123126
return ConformanceSource(typeDecl, ConformanceEntryKind::Synthesized);
124127
}
125128

129+
/// Return a new conformance source with the given location of "@unchecked".
130+
ConformanceSource withUncheckedLoc(SourceLoc uncheckedLoc) {
131+
ConformanceSource result(*this);
132+
if (uncheckedLoc.isValid())
133+
result.uncheckedLoc = uncheckedLoc;
134+
return result;
135+
}
136+
126137
/// Retrieve the kind of conformance formed from this source.
127138
ConformanceEntryKind getKind() const { return Storage.getInt(); }
128139

@@ -149,6 +160,11 @@ class ConformanceLookupTable {
149160
llvm_unreachable("Unhandled ConformanceEntryKind in switch.");
150161
}
151162

163+
/// The location of the @unchecked attribute, if any.
164+
SourceLoc getUncheckedLoc() const {
165+
return uncheckedLoc;
166+
}
167+
152168
/// For an inherited conformance, retrieve the class declaration
153169
/// for the inheriting class.
154170
ClassDecl *getInheritingClass() const {

lib/AST/NameLookup.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2636,7 +2636,7 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator,
26362636

26372637
void swift::getDirectlyInheritedNominalTypeDecls(
26382638
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
2639-
unsigned i, llvm::SmallVectorImpl<Located<NominalTypeDecl *>> &result,
2639+
unsigned i, llvm::SmallVectorImpl<InheritedNominalEntry> &result,
26402640
bool &anyObject) {
26412641
auto typeDecl = decl.dyn_cast<const TypeDecl *>();
26422642
auto extDecl = decl.dyn_cast<const ExtensionDecl *>();
@@ -2658,18 +2658,28 @@ void swift::getDirectlyInheritedNominalTypeDecls(
26582658
// FIXME: This is a hack. We need cooperation from
26592659
// InheritedDeclsReferencedRequest to make this work.
26602660
SourceLoc loc;
2661+
SourceLoc uncheckedLoc;
26612662
if (TypeRepr *typeRepr = typeDecl ? typeDecl->getInherited()[i].getTypeRepr()
26622663
: extDecl->getInherited()[i].getTypeRepr()){
26632664
loc = typeRepr->getLoc();
2665+
2666+
// Dig out the @unchecked attribute, if it's present.
2667+
while (auto attrTypeRepr = dyn_cast<AttributedTypeRepr>(typeRepr)) {
2668+
if (attrTypeRepr->getAttrs().has(TAK_unchecked)) {
2669+
uncheckedLoc = attrTypeRepr->getAttrs().getLoc(TAK_unchecked);
2670+
}
2671+
2672+
typeRepr = attrTypeRepr->getTypeRepr();
2673+
}
26642674
}
26652675

26662676
// Form the result.
26672677
for (auto nominal : nominalTypes) {
2668-
result.push_back({nominal, loc});
2678+
result.push_back({nominal, loc, uncheckedLoc});
26692679
}
26702680
}
26712681

2672-
SmallVector<Located<NominalTypeDecl *>, 4>
2682+
SmallVector<InheritedNominalEntry, 4>
26732683
swift::getDirectlyInheritedNominalTypeDecls(
26742684
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
26752685
bool &anyObject) {
@@ -2679,7 +2689,7 @@ swift::getDirectlyInheritedNominalTypeDecls(
26792689
// Gather results from all of the inherited types.
26802690
unsigned numInherited = typeDecl ? typeDecl->getInherited().size()
26812691
: extDecl->getInherited().size();
2682-
SmallVector<Located<NominalTypeDecl *>, 4> result;
2692+
SmallVector<InheritedNominalEntry, 4> result;
26832693
for (unsigned i : range(numInherited)) {
26842694
getDirectlyInheritedNominalTypeDecls(decl, i, result, anyObject);
26852695
}
@@ -2705,7 +2715,7 @@ swift::getDirectlyInheritedNominalTypeDecls(
27052715
if (!req.getFirstType()->isEqual(protoSelfTy))
27062716
continue;
27072717

2708-
result.emplace_back(req.getProtocolDecl(), loc);
2718+
result.emplace_back(req.getProtocolDecl(), loc, SourceLoc());
27092719
}
27102720
return result;
27112721
}
@@ -2715,7 +2725,7 @@ swift::getDirectlyInheritedNominalTypeDecls(
27152725
anyObject |= selfBounds.anyObject;
27162726

27172727
for (auto inheritedNominal : selfBounds.decls)
2718-
result.emplace_back(inheritedNominal, loc);
2728+
result.emplace_back(inheritedNominal, loc, SourceLoc());
27192729

27202730
return result;
27212731
}

lib/ClangImporter/ImportDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9915,7 +9915,8 @@ void ClangImporter::Implementation::loadAllConformances(
99159915
auto conformance = SwiftContext.getConformance(
99169916
dc->getDeclaredInterfaceType(),
99179917
protocol, SourceLoc(), dc,
9918-
ProtocolConformanceState::Incomplete);
9918+
ProtocolConformanceState::Incomplete,
9919+
protocol->isSpecificProtocol(KnownProtocolKind::Sendable));
99199920
conformance->setLazyLoader(this, /*context*/0);
99209921
conformance->setState(ProtocolConformanceState::Complete);
99219922
Conformances.push_back(conformance);

0 commit comments

Comments
 (0)