Skip to content

Commit adc2006

Browse files
committed
TypeCheckType: Make ExistentialAny warn until Swift 7 rather than error
This enables programmers to opt in to assisted adoption of the new syntax without breaking code and, thus, migrate in increments.
1 parent 9d704bd commit adc2006

File tree

6 files changed

+96
-74
lines changed

6 files changed

+96
-74
lines changed

include/swift/AST/Decl.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5547,12 +5547,6 @@ class ProtocolDecl final : public NominalTypeDecl {
55475547
/// value requirement.
55485548
bool hasSelfOrAssociatedTypeRequirements() const;
55495549

5550-
/// Determine whether an existential type constrained by this protocol must
5551-
/// be written using `any` syntax.
5552-
///
5553-
/// \Note This method takes language feature state into account.
5554-
bool existentialRequiresAny() const;
5555-
55565550
/// Returns a list of protocol requirements that must be assessed to
55575551
/// determine a concrete's conformance effect polymorphism kind.
55585552
PolymorphicEffectRequirementList getPolymorphicEffectRequirements(

lib/AST/Decl.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6856,13 +6856,6 @@ bool ProtocolDecl::hasSelfOrAssociatedTypeRequirements() const {
68566856
resultForCycle);
68576857
}
68586858

6859-
bool ProtocolDecl::existentialRequiresAny() const {
6860-
if (getASTContext().LangOpts.hasFeature(Feature::ExistentialAny))
6861-
return true;
6862-
6863-
return hasSelfOrAssociatedTypeRequirements();
6864-
}
6865-
68666859
ArrayRef<AssociatedTypeDecl *>
68676860
ProtocolDecl::getPrimaryAssociatedTypes() const {
68686861
return evaluateOrDefault(getASTContext().evaluator,

lib/Sema/TypeCheckType.cpp

Lines changed: 73 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6161,16 +6161,16 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
61616161
ASTContext &Ctx;
61626162
const bool checkStatements;
61636163
bool hitTopStmt;
6164-
bool warnUntilSwift7;
6164+
const bool downgradeErrorsToWarnings;
61656165

61666166
unsigned exprCount = 0;
61676167
llvm::SmallVector<TypeRepr *, 4> reprStack;
61686168

61696169
public:
61706170
ExistentialTypeSyntaxChecker(ASTContext &ctx, bool checkStatements,
6171-
bool warnUntilSwift7 = false)
6171+
bool downgradeErrorsToWarnings = false)
61726172
: Ctx(ctx), checkStatements(checkStatements), hitTopStmt(false),
6173-
warnUntilSwift7(warnUntilSwift7) {}
6173+
downgradeErrorsToWarnings(downgradeErrorsToWarnings) {}
61746174

61756175
MacroWalking getMacroWalkingBehavior() const override {
61766176
return MacroWalking::ArgumentsAndExpansion;
@@ -6355,6 +6355,50 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63556355
return isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it);
63566356
}
63576357

6358+
/// Returns the behavior with which to diagnose a missing `any` or `some`
6359+
/// keyword.
6360+
///
6361+
/// \param constraintTy The constraint type that is missing the keyword.
6362+
/// \param isInverted Whether the constraint type is an object of an `~`
6363+
/// inversion.
6364+
static DiagnosticBehavior getDiagnosticBehaviorForMissingAnyOrSomeKeyword(
6365+
Type constraintTy, bool isInverted, ASTContext &ctx) {
6366+
// `Any` and `AnyObject` are always exempt from `any` syntax.
6367+
if (constraintTy->isAny() || constraintTy->isAnyObject()) {
6368+
return DiagnosticBehavior::Ignore;
6369+
}
6370+
6371+
// If the type is inverted, a missing `any` or `some` is an error.
6372+
if (isInverted) {
6373+
return DiagnosticBehavior::Error;
6374+
}
6375+
6376+
// If one of the protocols is inverted, a missing `any` or `some` is
6377+
// an error.
6378+
if (auto *PCT = constraintTy->getAs<ProtocolCompositionType>()) {
6379+
if (!PCT->getInverses().empty()) {
6380+
return DiagnosticBehavior::Error;
6381+
}
6382+
}
6383+
6384+
// If one of the protocols has "Self or associated type" requirements,
6385+
// a missing `any` or `some` is an error.
6386+
auto layout = constraintTy->getExistentialLayout();
6387+
for (auto *protoDecl : layout.getProtocols()) {
6388+
if (protoDecl->hasSelfOrAssociatedTypeRequirements()) {
6389+
return DiagnosticBehavior::Error;
6390+
}
6391+
}
6392+
6393+
if (ctx.LangOpts.hasFeature(Feature::ExistentialAny)) {
6394+
// For anything else, a missing `any` or `some` is a warning if this
6395+
// feature is enabled.
6396+
return DiagnosticBehavior::Warning;
6397+
}
6398+
6399+
return DiagnosticBehavior::Ignore;
6400+
}
6401+
63586402
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
63596403
if (Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
63606404
return;
@@ -6403,49 +6447,33 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
64036447
return dyn_cast<InverseTypeRepr>(*it);
64046448
}();
64056449

6406-
const bool shouldDiagnose = [&] {
6407-
// `Any` and `AnyObject` are always exempt from `any` syntax.
6408-
if (type->isAny() || type->isAnyObject()) {
6409-
return false;
6410-
}
6411-
6412-
// Look for protocol members that require 'any'.
6413-
auto layout = type->getExistentialLayout();
6414-
for (auto *protoDecl : layout.getProtocols()) {
6415-
if (protoDecl->existentialRequiresAny()) {
6416-
return true;
6417-
}
6418-
}
6419-
6420-
// If inverses are present, require 'any' too.
6421-
if (auto *PCT = type->getAs<ProtocolCompositionType>()) {
6422-
if (!PCT->getInverses().empty()) {
6423-
return true;
6424-
}
6425-
}
6426-
6427-
if (outerInversion) {
6428-
return true;
6429-
}
6450+
DiagnosticBehavior behavior =
6451+
this->getDiagnosticBehaviorForMissingAnyOrSomeKeyword(
6452+
type, /*isInverted=*/outerInversion, this->Ctx);
64306453

6431-
return false;
6432-
}();
6454+
if (behavior == DiagnosticBehavior::Ignore) {
6455+
return;
6456+
}
64336457

6434-
if (shouldDiagnose) {
6435-
std::optional<InFlightDiagnostic> diag;
6436-
if (outerInversion) {
6437-
diag.emplace(Ctx.Diags.diagnose(outerInversion->getTildeLoc(),
6438-
diag::inverse_requires_any));
6439-
} else {
6440-
diag.emplace(Ctx.Diags.diagnose(T->getNameLoc(),
6441-
diag::existential_requires_any, type,
6442-
ExistentialType::get(type),
6443-
/*isAlias=*/isa<TypeAliasDecl>(decl)));
6444-
}
6458+
// If we were asked to downgrade errors, respect it.
6459+
if (behavior == DiagnosticBehavior::Error &&
6460+
this->downgradeErrorsToWarnings) {
6461+
behavior = DiagnosticBehavior::Warning;
6462+
}
64456463

6446-
diag->warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6447-
emitInsertAnyFixit(*diag, T);
6464+
std::optional<InFlightDiagnostic> diag;
6465+
if (outerInversion) {
6466+
diag.emplace(Ctx.Diags.diagnose(outerInversion->getTildeLoc(),
6467+
diag::inverse_requires_any));
6468+
} else {
6469+
diag.emplace(Ctx.Diags.diagnose(T->getNameLoc(),
6470+
diag::existential_requires_any, type,
6471+
ExistentialType::get(type),
6472+
/*isAlias=*/isa<TypeAliasDecl>(decl)));
64486473
}
6474+
6475+
diag->limitBehaviorUntilSwiftVersion(behavior, 7);
6476+
emitInsertAnyFixit(*diag, T);
64496477
}
64506478

64516479
public:
@@ -6523,12 +6551,12 @@ void TypeChecker::checkExistentialTypes(ASTContext &ctx, Stmt *stmt,
65236551

65246552
// Previously we missed this diagnostic on 'catch' statements, downgrade
65256553
// to a warning until Swift 7.
6526-
auto downgradeUntilSwift7 = false;
6554+
auto downgradeErrorsToWarnings = false;
65276555
if (auto *CS = dyn_cast<CaseStmt>(stmt))
6528-
downgradeUntilSwift7 = CS->getParentKind() == CaseParentKind::DoCatch;
6556+
downgradeErrorsToWarnings = CS->getParentKind() == CaseParentKind::DoCatch;
65296557

65306558
ExistentialTypeSyntaxChecker checker(ctx, /*checkStatements=*/true,
6531-
downgradeUntilSwift7);
6559+
downgradeErrorsToWarnings);
65326560
stmt->walk(checker);
65336561
}
65346562

test/type/explicit_existential.swift

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ protocol Bar {
1818
}
1919

2020
class Bistro {
21-
convenience init(_: Bar){ self.init()} // expected-explicit-any-error{{use of protocol 'Bar' as a type must be written 'any Bar'}}{{23-26=any Bar}}
22-
class func returnBar() -> Bar {} // expected-explicit-any-error {{use of protocol 'Bar' as a type must be written 'any Bar'}}{{29-32=any Bar}}
21+
convenience init(_: Bar){ self.init()}
22+
// expected-explicit-any-warning@-1 {{use of protocol 'Bar' as a type must be written 'any Bar'; this will be an error in a future Swift language mode}}{{23-26=any Bar}}
23+
class func returnBar() -> Bar {}
24+
// expected-explicit-any-warning@-1 {{use of protocol 'Bar' as a type must be written 'any Bar'; this will be an error in a future Swift language mode}}{{29-32=any Bar}}
2325
}
2426

2527
func useBarAsType(_ x: any Bar) {}
@@ -217,7 +219,8 @@ protocol RawRepresentable {
217219
enum E1: RawRepresentable {
218220
typealias RawValue = P1
219221

220-
var rawValue: P1 { // expected-explicit-any-error {{use of protocol 'P1' as a type must be written 'any P1'}}{{17-19=any P1}}
222+
var rawValue: P1 {
223+
// expected-explicit-any-warning@-1 {{use of protocol 'P1' as a type must be written 'any P1'; this will be an error in a future Swift language mode}}{{17-19=any P1}}
221224
return ConcreteComposition()
222225
}
223226
}
@@ -315,9 +318,9 @@ enum EE : Equatable, any Empty { // expected-error {{raw type 'any Empty' is not
315318

316319
// Protocols from a serialized module (the standard library).
317320
do {
318-
// expected-explicit-any-error@+1 {{use of protocol 'Decodable' as a type must be written 'any Decodable'}}
321+
// expected-explicit-any-warning@+1 {{use of protocol 'Decodable' as a type must be written 'any Decodable'; this will be an error in a future Swift language mode}}
319322
let _: Decodable
320-
// expected-explicit-any-error@+1 {{use of 'Codable' (aka 'Decodable & Encodable') as a type must be written 'any Codable' (aka 'any Decodable & Encodable')}}
323+
// expected-explicit-any-warning@+1 {{use of 'Codable' (aka 'Decodable & Encodable') as a type must be written 'any Codable' (aka 'any Decodable & Encodable'); this will be an error in a future Swift language mode}}
321324
let _: Codable
322325
}
323326

@@ -543,7 +546,7 @@ func testEnumAssociatedValue() {
543546
case c1((any HasAssoc) -> Void)
544547
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}
545548
case c2((HasAssoc) -> Void)
546-
// expected-explicit-any-error@+1 {{use of protocol 'P' as a type must be written 'any P'}}
549+
// expected-explicit-any-warning@+1 {{use of protocol 'P' as a type must be written 'any P'; this will be an error in a future Swift language mode}}
547550
case c3((P) -> Void)
548551
}
549552
}
@@ -568,5 +571,7 @@ typealias Objectlike = AnyObject
568571
func f(_ x: Objectlike) {}
569572

570573
typealias Copy = Copyable
571-
func h(_ z1: Copy, // expected-explicit-any-error {{use of 'Copy' (aka 'Copyable') as a type must be written 'any Copy' (aka 'any Copyable')}}
572-
_ z2: Copyable) {} // expected-explicit-any-error {{use of protocol 'Copyable' as a type must be written 'any Copyable'}}
574+
func h(_ z1: Copy,
575+
// expected-explicit-any-warning@-1 {{use of 'Copy' (aka 'Copyable') as a type must be written 'any Copy' (aka 'any Copyable'); this will be an error in a future Swift language mode}}
576+
_ z2: Copyable) {}
577+
// expected-explicit-any-warning@-1 {{use of protocol 'Copyable' as a type must be written 'any Copyable'; this will be an error in a future Swift language mode}}

test/type/explicit_existential_cyclic_protocol.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,24 @@ do {
1010
do {
1111
protocol P1 : P2 {}
1212
// expected-no-explicit-any-note@-1 2 {{protocol 'P1' declared here}}
13-
// expected-explicit-any-note@-2 1 {{protocol 'P1' declared here}}
13+
// expected-explicit-any-note@-2 2 {{protocol 'P1' declared here}}
1414
protocol P2 : P1 {}
1515
// expected-no-explicit-any-error@-1 2 {{protocol 'P2' refines itself}}
16-
// expected-explicit-any-error@-2 1 {{protocol 'P2' refines itself}}
16+
// expected-explicit-any-error@-2 2 {{protocol 'P2' refines itself}}
1717

1818
// Diagnosed only with the feature enabled, as a protocol without
1919
// "HasSelfOrAssociatedTypeRequirements" should.
2020
let _: P2
21-
// expected-explicit-any-error@-1 {{use of protocol 'P2' as a type must be written 'any P2'}}
21+
// expected-explicit-any-warning@-1 {{use of protocol 'P2' as a type must be written 'any P2'}}
2222
}
2323
do {
2424
protocol P0 { associatedtype A }
2525
protocol P1 : P2, P0 {}
2626
// expected-no-explicit-any-note@-1 2 {{protocol 'P1' declared here}}
27-
// expected-explicit-any-note@-2 1 {{protocol 'P1' declared here}}
27+
// expected-explicit-any-note@-2 2 {{protocol 'P1' declared here}}
2828
protocol P2 : P1 {}
2929
// expected-no-explicit-any-error@-1 2 {{protocol 'P2' refines itself}}
30-
// expected-explicit-any-error@-2 1 {{protocol 'P2' refines itself}}
30+
// expected-explicit-any-error@-2 2 {{protocol 'P2' refines itself}}
3131

3232
let _: P2
3333
// expected-error@-1 {{use of protocol 'P2' as a type must be written 'any P2'}}

test/type/explicit_existential_multimodule2.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44

55
// REQUIRES: swift_feature_ExistentialAny
66

7-
// Test that a protocol that requires 'any' *only* when the feature is enabled
8-
// is diagnosed as expected when said protocol originates in a different module.
7+
// Test that a protocol that requires 'any' *only* under ExistentialAny
8+
// is diagnosed as expected with the feature enabled when said protocol
9+
// originates in a different module.
910
// In other words, test that deserialization does not affect 'any' migration
1011
// diagnostics.
1112

1213
#if M
1314
public protocol P {}
1415
#else
1516
import M
16-
func test(_: P) {} // expected-error {{use of protocol 'P' as a type must be written 'any P'}}
17+
func test(_: P) {}
18+
// expected-warning@-1 {{use of protocol 'P' as a type must be written 'any P'}}
1719
#endif

0 commit comments

Comments
 (0)