Skip to content

Commit d394b23

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 78179da commit d394b23

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
@@ -5542,12 +5542,6 @@ class ProtocolDecl final : public NominalTypeDecl {
55425542
/// value requirement.
55435543
bool hasSelfOrAssociatedTypeRequirements() const;
55445544

5545-
/// Determine whether an existential type constrained by this protocol must
5546-
/// be written using `any` syntax.
5547-
///
5548-
/// \Note This method takes language feature state into account.
5549-
bool existentialRequiresAny() const;
5550-
55515545
/// Returns a list of protocol requirements that must be assessed to
55525546
/// determine a concrete's conformance effect polymorphism kind.
55535547
PolymorphicEffectRequirementList getPolymorphicEffectRequirements(

lib/AST/Decl.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6884,13 +6884,6 @@ bool ProtocolDecl::hasSelfOrAssociatedTypeRequirements() const {
68846884
resultForCycle);
68856885
}
68866886

6887-
bool ProtocolDecl::existentialRequiresAny() const {
6888-
if (getASTContext().LangOpts.hasFeature(Feature::ExistentialAny))
6889-
return true;
6890-
6891-
return hasSelfOrAssociatedTypeRequirements();
6892-
}
6893-
68946887
ArrayRef<AssociatedTypeDecl *>
68956888
ProtocolDecl::getPrimaryAssociatedTypes() const {
68966889
return evaluateOrDefault(getASTContext().evaluator,

lib/Sema/TypeCheckType.cpp

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

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

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

61776177
MacroWalking getMacroWalkingBehavior() const override {
61786178
return MacroWalking::ArgumentsAndExpansion;
@@ -6357,6 +6357,50 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63576357
return isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it);
63586358
}
63596359

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

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

6433-
return false;
6434-
}();
6456+
if (behavior == DiagnosticBehavior::Ignore) {
6457+
return;
6458+
}
64356459

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

6448-
diag->warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6449-
emitInsertAnyFixit(*diag, T);
6466+
std::optional<InFlightDiagnostic> diag;
6467+
if (outerInversion) {
6468+
diag.emplace(Ctx.Diags.diagnose(outerInversion->getTildeLoc(),
6469+
diag::inverse_requires_any));
6470+
} else {
6471+
diag.emplace(Ctx.Diags.diagnose(T->getNameLoc(),
6472+
diag::existential_requires_any, type,
6473+
ExistentialType::get(type),
6474+
/*isAlias=*/isa<TypeAliasDecl>(decl)));
64506475
}
6476+
6477+
diag->limitBehaviorUntilSwiftVersion(behavior, 7);
6478+
emitInsertAnyFixit(*diag, T);
64516479
}
64526480

64536481
public:
@@ -6525,12 +6553,12 @@ void TypeChecker::checkExistentialTypes(ASTContext &ctx, Stmt *stmt,
65256553

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

65326560
ExistentialTypeSyntaxChecker checker(ctx, /*checkStatements=*/true,
6533-
downgradeUntilSwift7);
6561+
downgradeErrorsToWarnings);
65346562
stmt->walk(checker);
65356563
}
65366564

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)