Skip to content

Commit a5f8bfd

Browse files
committed
TypeCheckType: Make ExistentialAny warn until Swift 7 rather than error
1 parent 1f46b15 commit a5f8bfd

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
@@ -5450,12 +5450,6 @@ class ProtocolDecl final : public NominalTypeDecl {
54505450
/// value requirement.
54515451
bool hasSelfOrAssociatedTypeRequirements() const;
54525452

5453-
/// Determine whether an existential type constrained by this protocol must
5454-
/// be written using `any` syntax.
5455-
///
5456-
/// \Note This method takes language feature state into account.
5457-
bool existentialRequiresAny() const;
5458-
54595453
/// Returns a list of protocol requirements that must be assessed to
54605454
/// determine a concrete's conformance effect polymorphism kind.
54615455
PolymorphicEffectRequirementList getPolymorphicEffectRequirements(

lib/AST/Decl.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6691,13 +6691,6 @@ bool ProtocolDecl::hasSelfOrAssociatedTypeRequirements() const {
66916691
true);
66926692
}
66936693

6694-
bool ProtocolDecl::existentialRequiresAny() const {
6695-
if (getASTContext().LangOpts.hasFeature(Feature::ExistentialAny))
6696-
return true;
6697-
6698-
return hasSelfOrAssociatedTypeRequirements();
6699-
}
6700-
67016694
ArrayRef<AssociatedTypeDecl *>
67026695
ProtocolDecl::getPrimaryAssociatedTypes() const {
67036696
return evaluateOrDefault(getASTContext().evaluator,

lib/Sema/TypeCheckType.cpp

Lines changed: 73 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6115,16 +6115,16 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
61156115
ASTContext &Ctx;
61166116
const bool checkStatements;
61176117
bool hitTopStmt;
6118-
bool warnUntilSwift7;
6118+
const bool downgradeErrorsToWarnings;
61196119

61206120
unsigned exprCount = 0;
61216121
llvm::SmallVector<TypeRepr *, 4> reprStack;
61226122

61236123
public:
61246124
ExistentialTypeSyntaxChecker(ASTContext &ctx, bool checkStatements,
6125-
bool warnUntilSwift7 = false)
6125+
bool downgradeErrorsToWarnings = false)
61266126
: Ctx(ctx), checkStatements(checkStatements), hitTopStmt(false),
6127-
warnUntilSwift7(warnUntilSwift7) {}
6127+
downgradeErrorsToWarnings(downgradeErrorsToWarnings) {}
61286128

61296129
MacroWalking getMacroWalkingBehavior() const override {
61306130
return MacroWalking::ArgumentsAndExpansion;
@@ -6316,6 +6316,50 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63166316
return isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it);
63176317
}
63186318

6319+
/// Returns the behavior with which to diagnose a missing `any` or `some`
6320+
/// keyword.
6321+
///
6322+
/// \param constraintTy The constraint type that is missing the keyword.
6323+
/// \param isInverted Whether the constraint type is an object of an `~`
6324+
/// inversion.
6325+
static DiagnosticBehavior getDiagnosticBehaviorForMissingAnyOrSomeKeyword(
6326+
Type constraintTy, bool isInverted, ASTContext &ctx) {
6327+
// `Any` and `AnyObject` are always exempt from `any` syntax.
6328+
if (constraintTy->isAny() || constraintTy->isAnyObject()) {
6329+
return DiagnosticBehavior::Ignore;
6330+
}
6331+
6332+
// If the type is inverted, a missing `any` or `some` is an error.
6333+
if (isInverted) {
6334+
return DiagnosticBehavior::Error;
6335+
}
6336+
6337+
// If one of the protocols is inverted, a missing `any` or `some` is
6338+
// an error.
6339+
if (auto *PCT = constraintTy->getAs<ProtocolCompositionType>()) {
6340+
if (!PCT->getInverses().empty()) {
6341+
return DiagnosticBehavior::Error;
6342+
}
6343+
}
6344+
6345+
// If one of the protocols has "Self or associated type" requirements,
6346+
// a missing `any` or `some` is an error.
6347+
auto layout = constraintTy->getExistentialLayout();
6348+
for (auto *protoDecl : layout.getProtocols()) {
6349+
if (protoDecl->hasSelfOrAssociatedTypeRequirements()) {
6350+
return DiagnosticBehavior::Error;
6351+
}
6352+
}
6353+
6354+
if (ctx.LangOpts.hasFeature(Feature::ExistentialAny)) {
6355+
// For anything else, a missing `any` or `some` is a warning if this
6356+
// feature is enabled.
6357+
return DiagnosticBehavior::Warning;
6358+
}
6359+
6360+
return DiagnosticBehavior::Ignore;
6361+
}
6362+
63196363
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
63206364
if (Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
63216365
return;
@@ -6364,49 +6408,33 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63646408
return dyn_cast<InverseTypeRepr>(*it);
63656409
}();
63666410

6367-
const bool shouldDiagnose = [&] {
6368-
// `Any` and `AnyObject` are always exempt from `any` syntax.
6369-
if (type->isAny() || type->isAnyObject()) {
6370-
return false;
6371-
}
6372-
6373-
// Look for protocol members that require 'any'.
6374-
auto layout = type->getExistentialLayout();
6375-
for (auto *protoDecl : layout.getProtocols()) {
6376-
if (protoDecl->existentialRequiresAny()) {
6377-
return true;
6378-
}
6379-
}
6380-
6381-
// If inverses are present, require 'any' too.
6382-
if (auto *PCT = type->getAs<ProtocolCompositionType>()) {
6383-
if (!PCT->getInverses().empty()) {
6384-
return true;
6385-
}
6386-
}
6387-
6388-
if (outerInverseRepr) {
6389-
return true;
6390-
}
6411+
DiagnosticBehavior behavior =
6412+
this->getDiagnosticBehaviorForMissingAnyOrSomeKeyword(
6413+
type, /*isInverted=*/outerInversion, this->Ctx);
63916414

6392-
return false;
6393-
}();
6415+
if (behavior == DiagnosticBehavior::Ignore) {
6416+
return;
6417+
}
63946418

6395-
if (shouldDiagnose) {
6396-
std::optional<InFlightDiagnostic> diag;
6397-
if (outerInversion) {
6398-
diag.emplace(Ctx.Diags.diagnose(outerInversion->getTildeLoc(),
6399-
diag::inverse_requires_any));
6400-
} else {
6401-
diag.emplace(Ctx.Diags.diagnose(T->getNameLoc(),
6402-
diag::existential_requires_any, type,
6403-
ExistentialType::get(type),
6404-
/*isAlias=*/isa<TypeAliasDecl>(decl)));
6405-
}
6419+
// If we were asked to downgrade errors, respect it.
6420+
if (behavior == DiagnosticBehavior::Error &&
6421+
this->downgradeErrorsToWarnings) {
6422+
behavior = DiagnosticBehavior::Warning;
6423+
}
64066424

6407-
diag->warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6408-
emitInsertAnyFixit(*diag, T);
6425+
std::optional<InFlightDiagnostic> diag;
6426+
if (outerInversion) {
6427+
diag.emplace(Ctx.Diags.diagnose(outerInversion->getTildeLoc(),
6428+
diag::inverse_requires_any));
6429+
} else {
6430+
diag.emplace(Ctx.Diags.diagnose(T->getNameLoc(),
6431+
diag::existential_requires_any, type,
6432+
ExistentialType::get(type),
6433+
/*isAlias=*/isa<TypeAliasDecl>(decl)));
64096434
}
6435+
6436+
diag->limitBehaviorUntilSwiftVersion(behavior, 7);
6437+
emitInsertAnyFixit(*diag, T);
64106438
}
64116439

64126440
public:
@@ -6484,12 +6512,12 @@ void TypeChecker::checkExistentialTypes(ASTContext &ctx, Stmt *stmt,
64846512

64856513
// Previously we missed this diagnostic on 'catch' statements, downgrade
64866514
// to a warning until Swift 7.
6487-
auto downgradeUntilSwift7 = false;
6515+
auto downgradeErrorsToWarnings = false;
64886516
if (auto *CS = dyn_cast<CaseStmt>(stmt))
6489-
downgradeUntilSwift7 = CS->getParentKind() == CaseParentKind::DoCatch;
6517+
downgradeErrorsToWarnings = CS->getParentKind() == CaseParentKind::DoCatch;
64906518

64916519
ExistentialTypeSyntaxChecker checker(ctx, /*checkStatements=*/true,
6492-
downgradeUntilSwift7);
6520+
downgradeErrorsToWarnings);
64936521
stmt->walk(checker);
64946522
}
64956523

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)