Skip to content

Commit c21d089

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 93f4fda commit c21d089

File tree

5 files changed

+91
-69
lines changed

5 files changed

+91
-69
lines changed

include/swift/AST/Decl.h

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

5531-
/// Determine whether an existential type constrained by this protocol must
5532-
/// be written using `any` syntax.
5533-
///
5534-
/// \Note This method takes language feature state into account.
5535-
bool existentialRequiresAny() const;
5536-
55375531
/// Returns a list of protocol requirements that must be assessed to
55385532
/// determine a concrete's conformance effect polymorphism kind.
55395533
PolymorphicEffectRequirementList getPolymorphicEffectRequirements(

lib/AST/Decl.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6841,13 +6841,6 @@ bool ProtocolDecl::hasSelfOrAssociatedTypeRequirements() const {
68416841
true);
68426842
}
68436843

6844-
bool ProtocolDecl::existentialRequiresAny() const {
6845-
if (getASTContext().LangOpts.hasFeature(Feature::ExistentialAny))
6846-
return true;
6847-
6848-
return hasSelfOrAssociatedTypeRequirements();
6849-
}
6850-
68516844
ArrayRef<AssociatedTypeDecl *>
68526845
ProtocolDecl::getPrimaryAssociatedTypes() const {
68536846
return evaluateOrDefault(getASTContext().evaluator,

lib/Sema/TypeCheckType.cpp

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

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

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

61736173
MacroWalking getMacroWalkingBehavior() const override {
61746174
return MacroWalking::ArgumentsAndExpansion;
@@ -6360,6 +6360,50 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63606360
return isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it);
63616361
}
63626362

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

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

6436-
return false;
6437-
}();
6459+
if (behavior == DiagnosticBehavior::Ignore) {
6460+
return;
6461+
}
64386462

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

6451-
diag->warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6452-
emitInsertAnyFixit(*diag, T);
6469+
std::optional<InFlightDiagnostic> diag;
6470+
if (outerInversion) {
6471+
diag.emplace(Ctx.Diags.diagnose(outerInversion->getTildeLoc(),
6472+
diag::inverse_requires_any));
6473+
} else {
6474+
diag.emplace(Ctx.Diags.diagnose(T->getNameLoc(),
6475+
diag::existential_requires_any, type,
6476+
ExistentialType::get(type),
6477+
/*isAlias=*/isa<TypeAliasDecl>(decl)));
64536478
}
6479+
6480+
diag->limitBehaviorUntilSwiftVersion(behavior, 7);
6481+
emitInsertAnyFixit(*diag, T);
64546482
}
64556483

64566484
public:
@@ -6528,12 +6556,12 @@ void TypeChecker::checkExistentialTypes(ASTContext &ctx, Stmt *stmt,
65286556

65296557
// Previously we missed this diagnostic on 'catch' statements, downgrade
65306558
// to a warning until Swift 7.
6531-
auto downgradeUntilSwift7 = false;
6559+
auto downgradeErrorsToWarnings = false;
65326560
if (auto *CS = dyn_cast<CaseStmt>(stmt))
6533-
downgradeUntilSwift7 = CS->getParentKind() == CaseParentKind::DoCatch;
6561+
downgradeErrorsToWarnings = CS->getParentKind() == CaseParentKind::DoCatch;
65346562

65356563
ExistentialTypeSyntaxChecker checker(ctx, /*checkStatements=*/true,
6536-
downgradeUntilSwift7);
6564+
downgradeErrorsToWarnings);
65376565
stmt->walk(checker);
65386566
}
65396567

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_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)