Skip to content

Commit d585d16

Browse files
Merge pull request swiftlang#78971 from AnthonyLatsis/amorphophallus-titanum
2 parents 681a6c8 + 9fb198f commit d585d16

File tree

3 files changed

+219
-19
lines changed

3 files changed

+219
-19
lines changed

include/swift/AST/ProtocolConformance.h

+20-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
147147
SWIFT_INLINE_BITFIELD_EMPTY(RootProtocolConformance, ProtocolConformance);
148148

149149
SWIFT_INLINE_BITFIELD_FULL(NormalProtocolConformance, RootProtocolConformance,
150-
1+1+
150+
1+1+1+
151151
bitmax(NumProtocolConformanceOptions,8)+
152152
bitmax(NumProtocolConformanceStateBits,8)+
153153
bitmax(NumConformanceEntryKindBits,8),
@@ -157,6 +157,10 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
157157
/// populated any of its elements).
158158
HasComputedAssociatedConformances : 1,
159159

160+
/// Whether the preconcurrency attribute is effectful (not redundant) for
161+
/// this conformance.
162+
IsPreconcurrencyEffectful : 1,
163+
160164
: NumPadBits,
161165

162166
/// Options.
@@ -584,6 +588,7 @@ class NormalProtocolConformance : public RootProtocolConformance,
584588
"Cannot have a @preconcurrency location without isPreconcurrency");
585589
setState(state);
586590
Bits.NormalProtocolConformance.IsInvalid = false;
591+
Bits.NormalProtocolConformance.IsPreconcurrencyEffectful = false;
587592
Bits.NormalProtocolConformance.Options = options.toRaw();
588593
Bits.NormalProtocolConformance.HasComputedAssociatedConformances = false;
589594
Bits.NormalProtocolConformance.SourceKind =
@@ -645,6 +650,20 @@ class NormalProtocolConformance : public RootProtocolConformance,
645650
(getOptions() | ProtocolConformanceFlags::Unchecked).toRaw();
646651
}
647652

653+
/// Whether the preconcurrency attribute is effectful (not redundant) for
654+
/// this conformance.
655+
bool isPreconcurrencyEffectful() const {
656+
ASSERT(isPreconcurrency() && isComplete());
657+
return Bits.NormalProtocolConformance.IsPreconcurrencyEffectful;
658+
}
659+
660+
/// Record that the preconcurrency attribute is effectful (not redundant)
661+
/// for this conformance.
662+
void setPreconcurrencyEffectful() {
663+
ASSERT(isPreconcurrency());
664+
Bits.NormalProtocolConformance.IsPreconcurrencyEffectful = true;
665+
}
666+
648667
/// Whether this is an preconcurrency conformance.
649668
bool isPreconcurrency() const {
650669
return getOptions().contains(ProtocolConformanceFlags::Preconcurrency);

lib/Sema/TypeCheckProtocol.cpp

+91-18
Original file line numberDiff line numberDiff line change
@@ -2010,6 +2010,9 @@ class MultiConformanceChecker {
20102010
/// Determine whether the given requirement was left unsatisfied.
20112011
bool isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req);
20122012

2013+
/// Diagnose redundant `@preconcurrency` attributes on conformances.
2014+
void diagnoseRedundantPreconcurrency();
2015+
20132016
public:
20142017
MultiConformanceChecker(ASTContext &ctx) : Context(ctx) {}
20152018

@@ -2020,6 +2023,11 @@ class MultiConformanceChecker {
20202023
AllConformances.push_back(conformance);
20212024
}
20222025

2026+
/// Get the conformances associated with this checker.
2027+
ArrayRef<NormalProtocolConformance *> getConformances() const {
2028+
return AllConformances;
2029+
}
2030+
20232031
/// Peek the unsatisfied requirements collected during conformance checking.
20242032
ArrayRef<ValueDecl*> getUnsatisfiedRequirements() {
20252033
return llvm::ArrayRef(UnsatisfiedReqs);
@@ -2082,7 +2090,74 @@ static void diagnoseProtocolStubFixit(
20822090
NormalProtocolConformance *conformance,
20832091
ArrayRef<ASTContext::MissingWitness> missingWitnesses);
20842092

2093+
void MultiConformanceChecker::diagnoseRedundantPreconcurrency() {
2094+
// Collect explicit preconcurrency conformances for which preconcurrency is
2095+
// not directly effectful.
2096+
SmallVector<NormalProtocolConformance *, 2> explicitConformances;
2097+
for (auto *conformance : AllConformances) {
2098+
if (conformance->getSourceKind() == ConformanceEntryKind::Explicit &&
2099+
conformance->isPreconcurrency() &&
2100+
!conformance->isPreconcurrencyEffectful()) {
2101+
explicitConformances.push_back(conformance);
2102+
}
2103+
}
2104+
2105+
if (explicitConformances.empty()) {
2106+
return;
2107+
}
2108+
2109+
// If preconcurrency is effectful for an implied conformance (a conformance
2110+
// to an inherited protocol), consider it effectful for the explicit implying
2111+
// one.
2112+
for (auto *conformance : AllConformances) {
2113+
switch (conformance->getSourceKind()) {
2114+
case ConformanceEntryKind::Inherited:
2115+
case ConformanceEntryKind::PreMacroExpansion:
2116+
llvm_unreachable("Invalid normal protocol conformance kind");
2117+
case ConformanceEntryKind::Explicit:
2118+
case ConformanceEntryKind::Synthesized:
2119+
continue;
2120+
case ConformanceEntryKind::Implied:
2121+
if (!conformance->isPreconcurrency() ||
2122+
!conformance->isPreconcurrencyEffectful()) {
2123+
continue;
2124+
}
2125+
2126+
auto *proto = conformance->getProtocol();
2127+
for (auto *explicitConformance : explicitConformances) {
2128+
if (explicitConformance->getProtocol()->inheritsFrom(proto)) {
2129+
explicitConformance->setPreconcurrencyEffectful();
2130+
}
2131+
}
2132+
2133+
continue;
2134+
}
2135+
}
2136+
2137+
// Diagnose all explicit preconcurrency conformances for which preconcurrency
2138+
// is not effectful (redundant).
2139+
for (auto *conformance : explicitConformances) {
2140+
if (conformance->isPreconcurrencyEffectful()) {
2141+
continue;
2142+
}
2143+
2144+
auto diag = Context.Diags.diagnose(
2145+
conformance->getLoc(), diag::preconcurrency_conformance_not_used,
2146+
conformance->getProtocol()->getDeclaredInterfaceType());
2147+
2148+
SourceLoc preconcurrencyLoc = conformance->getPreconcurrencyLoc();
2149+
if (preconcurrencyLoc.isValid()) {
2150+
SourceLoc endLoc = preconcurrencyLoc.getAdvancedLoc(1);
2151+
diag.fixItRemove(SourceRange(preconcurrencyLoc, endLoc));
2152+
}
2153+
}
2154+
}
2155+
20852156
void MultiConformanceChecker::checkAllConformances() {
2157+
if (AllConformances.empty()) {
2158+
return;
2159+
}
2160+
20862161
llvm::SmallVector<ASTContext::MissingWitness, 2> MissingWitnesses;
20872162

20882163
bool anyInvalid = false;
@@ -2147,6 +2222,9 @@ void MultiConformanceChecker::checkAllConformances() {
21472222
}
21482223
}
21492224

2225+
// Diagnose any redundant preconcurrency.
2226+
this->diagnoseRedundantPreconcurrency();
2227+
21502228
// Emit diagnostics at the very end.
21512229
for (auto *conformance : AllConformances) {
21522230
emitDelayedDiags(conformance);
@@ -5341,16 +5419,8 @@ void ConformanceChecker::resolveValueWitnesses() {
53415419
}
53425420
}
53435421

5344-
if (Conformance->isPreconcurrency() && !usesPreconcurrency) {
5345-
auto diag = DC->getASTContext().Diags.diagnose(
5346-
Conformance->getLoc(), diag::preconcurrency_conformance_not_used,
5347-
Proto->getDeclaredInterfaceType());
5348-
5349-
SourceLoc preconcurrencyLoc = Conformance->getPreconcurrencyLoc();
5350-
if (preconcurrencyLoc.isValid()) {
5351-
SourceLoc endLoc = preconcurrencyLoc.getAdvancedLoc(1);
5352-
diag.fixItRemove(SourceRange(preconcurrencyLoc, endLoc));
5353-
}
5422+
if (Conformance->isPreconcurrency() && usesPreconcurrency) {
5423+
Conformance->setPreconcurrencyEffectful();
53545424
}
53555425

53565426
// Finally, check some ad-hoc protocol requirements.
@@ -6245,7 +6315,6 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
62456315
ProtocolConformance *SendableConformance = nullptr;
62466316
bool hasDeprecatedUnsafeSendable = false;
62476317
bool sendableConformancePreconcurrency = false;
6248-
bool anyInvalid = false;
62496318
for (auto conformance : conformances) {
62506319
// Check and record normal conformances.
62516320
if (auto normal = dyn_cast<NormalProtocolConformance>(conformance)) {
@@ -6379,12 +6448,6 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
63796448
}
63806449
}
63816450

6382-
// Catalog all of members of this declaration context that satisfy
6383-
// requirements of conformances in this context.
6384-
SmallVector<ValueDecl *, 16>
6385-
unsatisfiedReqs(groupChecker.getUnsatisfiedRequirements().begin(),
6386-
groupChecker.getUnsatisfiedRequirements().end());
6387-
63886451
// Diagnose any conflicts attributed to this declaration context.
63896452
for (const auto &diag : idc->takeConformanceDiagnostics()) {
63906453
// Figure out the declaration of the existing conformance.
@@ -6516,9 +6579,19 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
65166579
diag.ExistingExplicitProtocol->getName());
65176580
}
65186581

6582+
if (groupChecker.getConformances().empty()) {
6583+
return;
6584+
}
6585+
6586+
// Catalog all of members of this declaration context that satisfy
6587+
// requirements of conformances in this context.
6588+
SmallVector<ValueDecl *, 16> unsatisfiedReqs(
6589+
groupChecker.getUnsatisfiedRequirements().begin(),
6590+
groupChecker.getUnsatisfiedRequirements().end());
6591+
65196592
// If there were any unsatisfied requirements, check whether there
65206593
// are any near-matches we should diagnose.
6521-
if (!unsatisfiedReqs.empty() && !anyInvalid) {
6594+
if (!unsatisfiedReqs.empty()) {
65226595
if (sf->Kind != SourceFileKind::Interface) {
65236596
// Find all of the members that aren't used to satisfy
65246597
// requirements, and check whether they are close to an

test/Concurrency/preconcurrency_conformances.swift

+108
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,111 @@ do {
198198
func b() {} // Ok
199199
}
200200
}
201+
202+
do {
203+
protocol P1 {}
204+
protocol P2 {}
205+
protocol P3: P1, P2 {}
206+
207+
// expected-warning@+1 {{@preconcurrency attribute on conformance to 'P3' has no effect}}
208+
@MainActor struct S: @preconcurrency P3 {}
209+
}
210+
211+
// rdar://137794903
212+
do {
213+
protocol P1 {}
214+
protocol P2 {
215+
func foo() // expected-note 2 {{mark the protocol requirement 'foo()' 'async' to allow actor-isolated conformances}}
216+
}
217+
protocol P3: P1, P2 {}
218+
219+
// OK, preconcurrency effectful because it is used by (implied) conformance
220+
// to inherited protocol 'P2'.
221+
@MainActor struct S1: @preconcurrency P3 {
222+
func foo() {}
223+
}
224+
// OK.
225+
@MainActor struct S2: @preconcurrency P2, P3 {
226+
func foo() {}
227+
}
228+
// OK.
229+
@MainActor struct S3: P3, @preconcurrency P2 {
230+
func foo() {}
231+
}
232+
233+
// Explicit conformances to inherited protocols do not contribute to whether
234+
// preconcurrency has effect on the conformance to the refined protocol, so
235+
// preconcurrency has no effect here.
236+
@MainActor struct S4: @preconcurrency P3, P2 {
237+
// expected-warning@-1:21 {{@preconcurrency attribute on conformance to 'P3' has no effect}}
238+
// expected-note@-2:45 {{add '@preconcurrency' to the 'P2' conformance to defer isolation checking to run time}}
239+
func foo() {}
240+
// expected-warning@-1 {{main actor-isolated instance method 'foo()' cannot be used to satisfy nonisolated requirement from protocol 'P2'}}
241+
// expected-note@-2 {{add 'nonisolated' to 'foo()' to make this instance method not isolated to the actor}}
242+
}
243+
@MainActor struct S5: P2, @preconcurrency P3 {
244+
// expected-warning@-1:21 {{@preconcurrency attribute on conformance to 'P3' has no effect}}
245+
// expected-note@-2:25 {{add '@preconcurrency' to the 'P2' conformance to defer isolation checking to run time}}
246+
func foo() {}
247+
// expected-warning@-1 {{main actor-isolated instance method 'foo()' cannot be used to satisfy nonisolated requirement from protocol 'P2'}}
248+
// expected-note@-2 {{add 'nonisolated' to 'foo()' to make this instance method not isolated to the actor}}
249+
}
250+
// expected-warning@+1 {{@preconcurrency attribute on conformance to 'P3' has no effect}}
251+
@MainActor struct S6: @preconcurrency P2, @preconcurrency P3 {
252+
func foo() {}
253+
}
254+
}
255+
do {
256+
protocol P1 {}
257+
protocol P2 {
258+
func foo()
259+
}
260+
protocol P3: P1, P2 {}
261+
protocol P4 {}
262+
263+
// OK, preconcurrency effectful because it is used by implied conformance to
264+
// inherited protocol 'P2'.
265+
@MainActor struct S1: P4, @preconcurrency P3 {
266+
func foo() {}
267+
}
268+
@MainActor struct S2: @preconcurrency P3, P4 {
269+
func foo() {}
270+
}
271+
272+
// Preconcurrency effectful for 'P3' only.
273+
@MainActor struct S3: @preconcurrency P3 & P4 {
274+
// expected-warning@-1:21 {{@preconcurrency attribute on conformance to 'P4' has no effect}}
275+
func foo() {}
276+
}
277+
}
278+
do {
279+
protocol P1 {}
280+
protocol P2 {
281+
func foo() // expected-note {{mark the protocol requirement 'foo()' 'async' to allow actor-isolated conformances}}
282+
}
283+
protocol P3: P1, P2 {}
284+
protocol P5: P3 {}
285+
protocol P6: P3 {}
286+
287+
// OK, preconcurrency effectful for both 'P5' and 'P6' because it is used
288+
// by implied conformance to mutually inherited protocol 'P2'.
289+
@MainActor struct S1: @preconcurrency P5 & P6 {
290+
func foo() {}
291+
}
292+
@MainActor struct S2: @preconcurrency P5, @preconcurrency P6 {
293+
func foo() {}
294+
}
295+
296+
// OK, preconcurrency effectful because it is used by implied conformance to
297+
// inherited protocol 'P2'.
298+
@MainActor struct S3: @preconcurrency P5, P6 {
299+
func foo() {}
300+
}
301+
@MainActor struct S4: P6, @preconcurrency P5 {
302+
// expected-warning@-1:21 {{@preconcurrency attribute on conformance to 'P5' has no effect}}
303+
func foo() {}
304+
// expected-warning@-1 {{main actor-isolated instance method 'foo()' cannot be used to satisfy nonisolated requirement from protocol 'P2'}}
305+
// expected-note@-2 {{add 'nonisolated' to 'foo()' to make this instance method not isolated to the actor}}
306+
}
307+
}
308+

0 commit comments

Comments
 (0)