Skip to content

Commit 6b6b8da

Browse files
Merge pull request #65146 from AnthonyLatsis/existential-any-multimodule-5.9
[5.9 🍒] TypeCheckType: Fix existential `any` migration diagnostics for extra-modular protocols
2 parents d4327cc + f0d5990 commit 6b6b8da

14 files changed

+139
-63
lines changed

include/swift/AST/Decl.h

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -553,11 +553,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
553553
/// Whether the existential of this protocol conforms to itself.
554554
ExistentialConformsToSelf : 1,
555555

556-
/// Whether the \c ExistentialRequiresAny bit is valid.
557-
ExistentialRequiresAnyValid : 1,
556+
/// Whether the \c HasSelfOrAssociatedTypeRequirements bit is valid.
557+
HasSelfOrAssociatedTypeRequirementsValid : 1,
558558

559-
/// Whether the existential of this protocol must be spelled with \c any.
560-
ExistentialRequiresAny : 1,
559+
/// Whether this protocol has \c Self or associated type requirements.
560+
/// See \c hasSelfOrAssociatedTypeRequirements() for clarification.
561+
HasSelfOrAssociatedTypeRequirements : 1,
561562

562563
/// True if the protocol has requirements that cannot be satisfied (e.g.
563564
/// because they could not be imported from Objective-C).
@@ -4770,19 +4771,19 @@ class ProtocolDecl final : public NominalTypeDecl {
47704771
Bits.ProtocolDecl.ExistentialConformsToSelf = result;
47714772
}
47724773

4773-
/// Returns the cached result of \c existentialRequiresAny or \c None if it
4774-
/// hasn't yet been computed.
4775-
Optional<bool> getCachedExistentialRequiresAny() {
4776-
if (Bits.ProtocolDecl.ExistentialRequiresAnyValid)
4777-
return Bits.ProtocolDecl.ExistentialRequiresAny;
4774+
/// Returns the cached result of \c hasSelfOrAssociatedTypeRequirements or
4775+
/// \c None if it hasn't yet been computed.
4776+
Optional<bool> getCachedHasSelfOrAssociatedTypeRequirements() {
4777+
if (Bits.ProtocolDecl.HasSelfOrAssociatedTypeRequirementsValid)
4778+
return Bits.ProtocolDecl.HasSelfOrAssociatedTypeRequirements;
47784779

47794780
return None;
47804781
}
47814782

4782-
/// Caches the result of \c existentialRequiresAny
4783-
void setCachedExistentialRequiresAny(bool requiresAny) {
4784-
Bits.ProtocolDecl.ExistentialRequiresAnyValid = true;
4785-
Bits.ProtocolDecl.ExistentialRequiresAny = requiresAny;
4783+
/// Caches the result of \c hasSelfOrAssociatedTypeRequirements
4784+
void setCachedHasSelfOrAssociatedTypeRequirements(bool value) {
4785+
Bits.ProtocolDecl.HasSelfOrAssociatedTypeRequirementsValid = true;
4786+
Bits.ProtocolDecl.HasSelfOrAssociatedTypeRequirements = value;
47864787
}
47874788

47884789
bool hasLazyRequirementSignature() const {
@@ -4803,7 +4804,7 @@ class ProtocolDecl final : public NominalTypeDecl {
48034804
friend class RequirementSignatureRequestGSB;
48044805
friend class ProtocolRequiresClassRequest;
48054806
friend class ExistentialConformsToSelfRequest;
4806-
friend class ExistentialRequiresAnyRequest;
4807+
friend class HasSelfOrAssociatedTypeRequirementsRequest;
48074808
friend class InheritedProtocolsRequest;
48084809
friend class PrimaryAssociatedTypesRequest;
48094810
friend class ProtocolRequirementsRequest;
@@ -4902,9 +4903,20 @@ class ProtocolDecl final : public NominalTypeDecl {
49024903
/// Does this protocol require a self-conformance witness table?
49034904
bool requiresSelfConformanceWitnessTable() const;
49044905

4905-
/// Determine whether an existential type must be explicitly prefixed
4906-
/// with \c any. \c any is required if any of the members contain
4907-
/// an associated type, or if \c Self appears in non-covariant position.
4906+
/// Determine whether this protocol has `Self` or associated type
4907+
/// requirements.
4908+
///
4909+
/// This is true if one of the following conditions is met for this protocol
4910+
/// or an inherited protocol:
4911+
/// - The protocol has an associated type requirement.
4912+
/// - `Self` appears in non-covariant position in the type signature of a
4913+
/// value requirement.
4914+
bool hasSelfOrAssociatedTypeRequirements() const;
4915+
4916+
/// Determine whether an existential type constrained by this protocol must
4917+
/// be written using `any` syntax.
4918+
///
4919+
/// \Note This method takes language feature state into account.
49084920
bool existentialRequiresAny() const;
49094921

49104922
/// Returns a list of protocol requirements that must be assessed to

include/swift/AST/TypeCheckRequests.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,8 @@ class ExistentialConformsToSelfRequest :
300300

301301
/// Determine whether an existential type conforming to this protocol
302302
/// requires the \c any syntax.
303-
class ExistentialRequiresAnyRequest :
304-
public SimpleRequest<ExistentialRequiresAnyRequest,
303+
class HasSelfOrAssociatedTypeRequirementsRequest :
304+
public SimpleRequest<HasSelfOrAssociatedTypeRequirementsRequest,
305305
bool(ProtocolDecl *),
306306
RequestFlags::SeparatelyCached> {
307307
public:

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest,
9595
Type(EnumDecl *), Cached, NoLocationInfo)
9696
SWIFT_REQUEST(TypeChecker, ExistentialConformsToSelfRequest,
9797
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
98-
SWIFT_REQUEST(TypeChecker, ExistentialRequiresAnyRequest,
98+
SWIFT_REQUEST(TypeChecker, HasSelfOrAssociatedTypeRequirementsRequest,
9999
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
100100
SWIFT_REQUEST(TypeChecker, ExtendedTypeRequest, Type(ExtensionDecl *), Cached,
101101
NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6052,9 +6052,18 @@ bool ProtocolDecl::existentialConformsToSelf() const {
60526052
ExistentialConformsToSelfRequest{const_cast<ProtocolDecl *>(this)}, true);
60536053
}
60546054

6055-
bool ProtocolDecl::existentialRequiresAny() const {
6055+
bool ProtocolDecl::hasSelfOrAssociatedTypeRequirements() const {
60566056
return evaluateOrDefault(getASTContext().evaluator,
6057-
ExistentialRequiresAnyRequest{const_cast<ProtocolDecl *>(this)}, true);
6057+
HasSelfOrAssociatedTypeRequirementsRequest{
6058+
const_cast<ProtocolDecl *>(this)},
6059+
true);
6060+
}
6061+
6062+
bool ProtocolDecl::existentialRequiresAny() const {
6063+
if (getASTContext().LangOpts.hasFeature(Feature::ExistentialAny))
6064+
return true;
6065+
6066+
return hasSelfOrAssociatedTypeRequirements();
60586067
}
60596068

60606069
ArrayRef<AssociatedTypeDecl *>

lib/AST/TypeCheckRequests.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,28 +263,31 @@ void ExistentialConformsToSelfRequest::cacheResult(bool value) const {
263263
}
264264

265265
//----------------------------------------------------------------------------//
266-
// existentialRequiresAny computation.
266+
// hasSelfOrAssociatedTypeRequirementsRequest computation.
267267
//----------------------------------------------------------------------------//
268268

269-
void ExistentialRequiresAnyRequest::diagnoseCycle(DiagnosticEngine &diags) const {
269+
void HasSelfOrAssociatedTypeRequirementsRequest::diagnoseCycle(
270+
DiagnosticEngine &diags) const {
270271
auto decl = std::get<0>(getStorage());
271272
diags.diagnose(decl, diag::circular_protocol_def, decl->getName());
272273
}
273274

274-
void ExistentialRequiresAnyRequest::noteCycleStep(DiagnosticEngine &diags) const {
275+
void HasSelfOrAssociatedTypeRequirementsRequest::noteCycleStep(
276+
DiagnosticEngine &diags) const {
275277
auto requirement = std::get<0>(getStorage());
276278
diags.diagnose(requirement, diag::kind_declname_declared_here,
277279
DescriptiveDeclKind::Protocol, requirement->getName());
278280
}
279281

280-
Optional<bool> ExistentialRequiresAnyRequest::getCachedResult() const {
282+
Optional<bool>
283+
HasSelfOrAssociatedTypeRequirementsRequest::getCachedResult() const {
281284
auto decl = std::get<0>(getStorage());
282-
return decl->getCachedExistentialRequiresAny();
285+
return decl->getCachedHasSelfOrAssociatedTypeRequirements();
283286
}
284287

285-
void ExistentialRequiresAnyRequest::cacheResult(bool value) const {
288+
void HasSelfOrAssociatedTypeRequirementsRequest::cacheResult(bool value) const {
286289
auto decl = std::get<0>(getStorage());
287-
decl->setCachedExistentialRequiresAny(value);
290+
decl->setCachedHasSelfOrAssociatedTypeRequirements(value);
288291
}
289292

290293
//----------------------------------------------------------------------------//

lib/Sema/TypeCheckDecl.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -711,13 +711,8 @@ ExistentialConformsToSelfRequest::evaluate(Evaluator &evaluator,
711711
return true;
712712
}
713713

714-
bool
715-
ExistentialRequiresAnyRequest::evaluate(Evaluator &evaluator,
716-
ProtocolDecl *decl) const {
717-
auto &ctx = decl->getASTContext();
718-
if (ctx.LangOpts.hasFeature(Feature::ExistentialAny))
719-
return true;
720-
714+
bool HasSelfOrAssociatedTypeRequirementsRequest::evaluate(
715+
Evaluator &evaluator, ProtocolDecl *decl) const {
721716
// ObjC protocols do not require `any`.
722717
if (decl->isObjC())
723718
return false;
@@ -740,7 +735,7 @@ ExistentialRequiresAnyRequest::evaluate(Evaluator &evaluator,
740735

741736
// Check whether any of the inherited protocols require `any`.
742737
for (auto proto : decl->getInheritedProtocols()) {
743-
if (proto->existentialRequiresAny())
738+
if (proto->hasSelfOrAssociatedTypeRequirements())
744739
return true;
745740
}
746741

lib/Sema/TypeCheckType.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5176,6 +5176,10 @@ class ExistentialTypeVisitor
51765176
if (T->isInvalid())
51775177
return;
51785178

5179+
if (Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
5180+
return;
5181+
}
5182+
51795183
// Compute the type repr to attach 'any' to.
51805184
TypeRepr *replaceRepr = T;
51815185
// Insert parens in expression context for '(any P).self'
@@ -5208,7 +5212,7 @@ class ExistentialTypeVisitor
52085212
OS << ")";
52095213

52105214
if (auto *proto = dyn_cast_or_null<ProtocolDecl>(T->getBoundDecl())) {
5211-
if (proto->existentialRequiresAny() && !Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
5215+
if (proto->existentialRequiresAny()) {
52125216
Ctx.Diags.diagnose(T->getNameLoc(),
52135217
diag::existential_requires_any,
52145218
proto->getDeclaredInterfaceType(),
@@ -5226,7 +5230,7 @@ class ExistentialTypeVisitor
52265230
if (type->isConstraintType()) {
52275231
auto layout = type->getExistentialLayout();
52285232
for (auto *protoDecl : layout.getProtocols()) {
5229-
if (!protoDecl->existentialRequiresAny() || Ctx.LangOpts.hasFeature(Feature::ImplicitSome))
5233+
if (!protoDecl->existentialRequiresAny())
52305234
continue;
52315235

52325236
Ctx.Diags.diagnose(T->getNameLoc(),

lib/Serialization/Deserialization.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4075,14 +4075,14 @@ class DeclDeserializer {
40754075
StringRef blobData) {
40764076
IdentifierID nameID;
40774077
DeclContextID contextID;
4078-
bool isImplicit, isClassBounded, isObjC, existentialRequiresAny;
4078+
bool isImplicit, isClassBounded, isObjC, hasSelfOrAssocTypeRequirements;
40794079
uint8_t rawAccessLevel;
40804080
unsigned numInheritedTypes;
40814081
ArrayRef<uint64_t> rawInheritedAndDependencyIDs;
40824082

40834083
decls_block::ProtocolLayout::readRecord(scratch, nameID, contextID,
40844084
isImplicit, isClassBounded, isObjC,
4085-
existentialRequiresAny,
4085+
hasSelfOrAssocTypeRequirements,
40864086
rawAccessLevel, numInheritedTypes,
40874087
rawInheritedAndDependencyIDs);
40884088

@@ -4110,8 +4110,8 @@ class DeclDeserializer {
41104110

41114111
ctx.evaluator.cacheOutput(ProtocolRequiresClassRequest{proto},
41124112
std::move(isClassBounded));
4113-
ctx.evaluator.cacheOutput(ExistentialRequiresAnyRequest{proto},
4114-
std::move(existentialRequiresAny));
4113+
ctx.evaluator.cacheOutput(HasSelfOrAssociatedTypeRequirementsRequest{proto},
4114+
std::move(hasSelfOrAssocTypeRequirements));
41154115

41164116
if (auto accessLevel = getActualAccessLevel(rawAccessLevel))
41174117
proto->setAccess(*accessLevel);

lib/Serialization/Serialization.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4127,7 +4127,7 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
41274127
const_cast<ProtocolDecl *>(proto)
41284128
->requiresClass(),
41294129
proto->isObjC(),
4130-
proto->existentialRequiresAny(),
4130+
proto->hasSelfOrAssociatedTypeRequirements(),
41314131
rawAccessLevel, numInherited,
41324132
inheritedAndDependencyTypes);
41334133

test/expr/postfix/call/forward_trailing_closure_errors.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func testUnlabeledParamMatching(i: Int, fn: ((Int) -> Int) -> Void) {
2020

2121
// When "fuzzy matching is disabled, this will fail.
2222
func forwardMatchFailure( // expected-note{{declared here}}
23-
onError: ((Error) -> Void)? = nil,
23+
onError: ((any Error) -> Void)? = nil,
2424
onCompletion: (Int) -> Void
2525
) { }
2626

test/type/explicit_existential.swift

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -207,17 +207,9 @@ extension MyError {
207207
}
208208
}
209209

210-
struct Wrapper {
211-
typealias E = Error
212-
}
213-
214-
func typealiasMemberReferences(metatype: Wrapper.Type) {
215-
let _: Wrapper.E.Protocol = metatype.E.self
216-
let _: (any Wrapper.E).Type = metatype.E.self
217-
}
218-
219210
func testAnyTypeExpr() {
220211
let _: (any P).Type = (any P).self
212+
let _: (any P1 & P2).Type = (any P1 & P2).self
221213

222214
func test(_: (any P).Type) {}
223215
test((any P).self)
@@ -281,6 +273,12 @@ enum EE : Equatable, any Empty { // expected-error {{raw type 'any Empty' is not
281273
case hack
282274
}
283275

276+
// Protocols from a serialized module (the standard library).
277+
do {
278+
let _: Decodable
279+
let _: Codable
280+
}
281+
284282
func testAnyFixIt() {
285283
struct ConformingType : HasAssoc {
286284
typealias Assoc = Int
@@ -302,6 +300,19 @@ func testAnyFixIt() {
302300
// expected-error@+2 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=(any HasAssoc)}}
303301
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{30-38=(any HasAssoc)}}
304302
let _: HasAssoc.Protocol = HasAssoc.self
303+
do {
304+
struct Wrapper {
305+
typealias HasAssocAlias = HasAssoc
306+
}
307+
let wrapperMeta: Wrapper.Type
308+
// FIXME: Both of these fix-its are wrong.
309+
// 1. 'any' is attached to 'HasAssocAlias' instead of 'Wrapper.HasAssocAlias'
310+
// 2. What is the correct fix-it for the initializer?
311+
//
312+
// expected-error@+2:20 {{use of 'Wrapper.HasAssocAlias' (aka 'HasAssoc') as a type must be written 'any Wrapper.HasAssocAlias' (aka 'any HasAssoc')}}{{20-33=(any HasAssocAlias)}}
313+
// expected-error@+1:57 {{use of 'Wrapper.HasAssocAlias' (aka 'HasAssoc') as a type must be written 'any Wrapper.HasAssocAlias' (aka 'any HasAssoc')}}{{57-70=(any HasAssocAlias)}}
314+
let _: Wrapper.HasAssocAlias.Protocol = wrapperMeta.HasAssocAlias.self
315+
}
305316
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{11-19=any HasAssoc}}
306317
let _: (HasAssoc).Protocol = (any HasAssoc).self
307318
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=(any HasAssoc)}}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -swift-version 5 -emit-module -DM -module-name M -emit-module-path %t/M.swiftmodule -enable-upcoming-feature ExistentialAny
3+
// RUN: %target-swift-frontend %s -verify -swift-version 5 -typecheck -I %t
4+
5+
// Test that the feature state used to compile a module is not reflected in
6+
// the module file. The feature must not be enforced on protocols originating in
7+
// a different module just because that module was compiled with the feature
8+
// enabled.
9+
10+
#if M
11+
public protocol P {}
12+
#else
13+
import M
14+
func test(_: P) {}
15+
#endif
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -swift-version 5 -emit-module -DM -module-name M -emit-module-path %t/M.swiftmodule
3+
// RUN: %target-swift-frontend %s -verify -swift-version 5 -typecheck -I %t -enable-upcoming-feature ExistentialAny
4+
5+
// Test that a protocol that requires 'any' *only* when the feature is enabled
6+
// is diagnosed as expected when said protocol originates in a different module.
7+
// In other words, test that deserialization does not affect 'any' migration
8+
// diagnostics.
9+
10+
#if M
11+
public protocol P {}
12+
#else
13+
import M
14+
func test(_: P) {} // expected-error {{use of protocol 'P' as a type must be written 'any P'}}
15+
#endif

test/type/explicit_existential_swift6.swift

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -229,17 +229,9 @@ extension MyError {
229229
}
230230
}
231231

232-
struct Wrapper {
233-
typealias E = Error
234-
}
235-
236-
func typealiasMemberReferences(metatype: Wrapper.Type) {
237-
let _: Wrapper.E.Protocol = metatype.E.self
238-
let _: (any Wrapper.E).Type = metatype.E.self
239-
}
240-
241232
func testAnyTypeExpr() {
242233
let _: (any P).Type = (any P).self
234+
let _: (any P1 & P2).Type = (any P1 & P2).self
243235

244236
func test(_: (any P).Type) {}
245237
test((any P).self)
@@ -311,6 +303,13 @@ enum EE : Equatable, any Empty { // expected-error {{raw type 'any Empty' is not
311303
case hack
312304
}
313305

306+
// Protocols from a serialized module (the standard library).
307+
do {
308+
// expected-error@+1 {{use of protocol 'Decodable' as a type must be written 'any Decodable'}}
309+
let _: Decodable
310+
// expected-error@+1 2 {{use of 'Codable' (aka 'Decodable & Encodable') as a type must be written 'any Codable' (aka 'any Decodable & Encodable')}}
311+
let _: Codable
312+
}
314313

315314
func testAnyFixIt() {
316315
struct ConformingType : HasAssoc {
@@ -333,6 +332,19 @@ func testAnyFixIt() {
333332
// expected-error@+2 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=(any HasAssoc)}}
334333
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{30-38=(any HasAssoc)}}
335334
let _: HasAssoc.Protocol = HasAssoc.self
335+
do {
336+
struct Wrapper {
337+
typealias HasAssocAlias = HasAssoc
338+
}
339+
let wrapperMeta: Wrapper.Type
340+
// FIXME: Both of these fix-its are wrong.
341+
// 1. 'any' is attached to 'HasAssocAlias' instead of 'Wrapper.HasAssocAlias'
342+
// 2. What is the correct fix-it for the initializer?
343+
//
344+
// expected-error@+2:20 {{use of 'Wrapper.HasAssocAlias' (aka 'HasAssoc') as a type must be written 'any Wrapper.HasAssocAlias' (aka 'any HasAssoc')}}{{20-33=(any HasAssocAlias)}}
345+
// expected-error@+1:57 {{use of 'Wrapper.HasAssocAlias' (aka 'HasAssoc') as a type must be written 'any Wrapper.HasAssocAlias' (aka 'any HasAssoc')}}{{57-70=(any HasAssocAlias)}}
346+
let _: Wrapper.HasAssocAlias.Protocol = wrapperMeta.HasAssocAlias.self
347+
}
336348
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{11-19=any HasAssoc}}
337349
let _: (HasAssoc).Protocol = (any HasAssoc).self
338350
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=(any HasAssoc)}}

0 commit comments

Comments
 (0)