Skip to content

Commit bd8430c

Browse files
committed
Sema: Do not diagnose preconcurrency redundancy in implied conformances
1 parent b97682a commit bd8430c

File tree

3 files changed

+141
-11
lines changed

3 files changed

+141
-11
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 20 additions & 1 deletion
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

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,6 +2012,9 @@ class MultiConformanceChecker {
20122012
/// Determine whether the given requirement was left unsatisfied.
20132013
bool isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req);
20142014

2015+
/// Diagnose redundant `@preconcurrency` attributes on conformances.
2016+
void diagnoseRedundantPreconcurrency();
2017+
20152018
public:
20162019
MultiConformanceChecker(ASTContext &ctx) : Context(ctx) {}
20172020

@@ -2089,6 +2092,67 @@ static void diagnoseProtocolStubFixit(
20892092
NormalProtocolConformance *conformance,
20902093
ArrayRef<ASTContext::MissingWitness> missingWitnesses);
20912094

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

2225+
// Diagnose any redundant preconcurrency.
2226+
this->diagnoseRedundantPreconcurrency();
2227+
21612228
// Emit diagnostics at the very end.
21622229
for (auto *conformance : AllConformances) {
21632230
emitDelayedDiags(conformance);
@@ -5353,16 +5420,8 @@ void ConformanceChecker::resolveValueWitnesses() {
53535420
}
53545421
}
53555422

5356-
if (Conformance->isPreconcurrency() && !usesPreconcurrency) {
5357-
auto diag = DC->getASTContext().Diags.diagnose(
5358-
Conformance->getLoc(), diag::preconcurrency_conformance_not_used,
5359-
Proto->getDeclaredInterfaceType());
5360-
5361-
SourceLoc preconcurrencyLoc = Conformance->getPreconcurrencyLoc();
5362-
if (preconcurrencyLoc.isValid()) {
5363-
SourceLoc endLoc = preconcurrencyLoc.getAdvancedLoc(1);
5364-
diag.fixItRemove(SourceRange(preconcurrencyLoc, endLoc));
5365-
}
5423+
if (Conformance->isPreconcurrency() && usesPreconcurrency) {
5424+
Conformance->setPreconcurrencyEffectful();
53665425
}
53675426

53685427
// Finally, check some ad-hoc protocol requirements.

test/Concurrency/preconcurrency_conformances.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,55 @@ 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+
@MainActor protocol P1 {}
214+
protocol P2 {
215+
func foo()
216+
}
217+
protocol P3: P1, P2 {}
218+
219+
do {
220+
// OK, preconcurrency is effectful on 'P3' through 'P2'.
221+
@MainActor struct S: @preconcurrency P3 {
222+
func foo() {}
223+
}
224+
}
225+
226+
do {
227+
// OK, preconcurrency is effectful on 'P3' through 'P2'.
228+
struct S: @preconcurrency P3 {
229+
func foo() {}
230+
}
231+
}
232+
233+
protocol P4 {}
234+
protocol P5 {}
235+
protocol P6: P4, P5 {}
236+
237+
do {
238+
// OK, preconcurrency is effectful on 'P3' through 'P2'.
239+
@MainActor struct S: @preconcurrency P3, P4 {
240+
func foo() {}
241+
}
242+
}
243+
244+
do {
245+
// OK, preconcurrency is effectful on 'P3' through 'P2'.
246+
// expected-warning@+1 {{@preconcurrency attribute on conformance to 'P4' has no effect}}
247+
@MainActor struct S: @preconcurrency P3 & P4 {
248+
func foo() {}
249+
}
250+
}
251+
}
252+

0 commit comments

Comments
 (0)