Skip to content

Commit ec8d16c

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

File tree

5 files changed

+86
-64
lines changed

5 files changed

+86
-64
lines changed

include/swift/AST/Decl.h

-6
Original file line numberDiff line numberDiff line change
@@ -5508,12 +5508,6 @@ class ProtocolDecl final : public NominalTypeDecl {
55085508
/// value requirement.
55095509
bool hasSelfOrAssociatedTypeRequirements() const;
55105510

5511-
/// Determine whether an existential type constrained by this protocol must
5512-
/// be written using `any` syntax.
5513-
///
5514-
/// \Note This method takes language feature state into account.
5515-
bool existentialRequiresAny() const;
5516-
55175511
/// Returns a list of protocol requirements that must be assessed to
55185512
/// determine a concrete's conformance effect polymorphism kind.
55195513
PolymorphicEffectRequirementList getPolymorphicEffectRequirements(

lib/AST/Decl.cpp

-7
Original file line numberDiff line numberDiff line change
@@ -6713,13 +6713,6 @@ bool ProtocolDecl::hasSelfOrAssociatedTypeRequirements() const {
67136713
true);
67146714
}
67156715

6716-
bool ProtocolDecl::existentialRequiresAny() const {
6717-
if (getASTContext().LangOpts.hasFeature(Feature::ExistentialAny))
6718-
return true;
6719-
6720-
return hasSelfOrAssociatedTypeRequirements();
6721-
}
6722-
67236716
ArrayRef<AssociatedTypeDecl *>
67246717
ProtocolDecl::getPrimaryAssociatedTypes() const {
67256718
return evaluateOrDefault(getASTContext().evaluator,

lib/Sema/TypeCheckType.cpp

+68-40
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,46 @@ 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 computeDiagnosticBehavior(Type constraintTy,
6370+
bool isInverted,
6371+
ASTContext &ctx) {
6372+
// If the type is inverted, a missing `any` or `some` is an error.
6373+
if (isInverted) {
6374+
return DiagnosticBehavior::Error;
6375+
}
6376+
6377+
// If one of the protocols is inverted, a missing `any` or `some` is
6378+
// an error.
6379+
if (auto *PCT = constraintTy->getAs<ProtocolCompositionType>()) {
6380+
if (!PCT->getInverses().empty()) {
6381+
return DiagnosticBehavior::Error;
6382+
}
6383+
}
6384+
6385+
// If one of the protocols has "Self or associated type" requirements,
6386+
// a missing `any` or `some` is an error.
6387+
auto layout = constraintTy->getExistentialLayout();
6388+
for (auto *protoDecl : layout.getProtocols()) {
6389+
if (protoDecl->hasSelfOrAssociatedTypeRequirements()) {
6390+
return DiagnosticBehavior::Error;
6391+
}
6392+
}
6393+
6394+
if (ctx.LangOpts.hasFeature(Feature::ExistentialAny)) {
6395+
// For anything else, a missing `any` or `some` is a warning if this
6396+
// feature is enabled.
6397+
return DiagnosticBehavior::Warning;
6398+
}
6399+
6400+
return DiagnosticBehavior::Ignore;
6401+
}
6402+
63636403
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
63646404
if (Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
63656405
return;
@@ -6415,44 +6455,32 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
64156455
return dyn_cast<InverseTypeRepr>(*it);
64166456
}();
64176457

6418-
const bool shouldDiagnose = [&] {
6419-
// Look for protocol members that require 'any'.
6420-
auto layout = type->getExistentialLayout();
6421-
for (auto *protoDecl : layout.getProtocols()) {
6422-
if (protoDecl->existentialRequiresAny()) {
6423-
return true;
6424-
}
6425-
}
6458+
DiagnosticBehavior behavior = this->computeDiagnosticBehavior(
6459+
type, /*isInverted=*/outerInversion, this->Ctx);
64266460

6427-
// If inverses are present, require 'any' too.
6428-
if (auto *PCT = type->getAs<ProtocolCompositionType>()) {
6429-
if (!PCT->getInverses().empty()) {
6430-
return true;
6431-
}
6432-
}
6433-
6434-
if (outerInverseRepr) {
6435-
return true;
6436-
}
6437-
6438-
return false;
6439-
}();
6461+
if (behavior == DiagnosticBehavior::Ignore) {
6462+
return;
6463+
}
64406464

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

6453-
diag->warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6454-
emitInsertAnyFixit(*diag, T);
6471+
std::optional<InFlightDiagnostic> diag;
6472+
if (outerInversion) {
6473+
diag.emplace(Ctx.Diags.diagnose(outerInversion->getTildeLoc(),
6474+
diag::inverse_requires_any));
6475+
} else {
6476+
diag.emplace(Ctx.Diags.diagnose(T->getNameLoc(),
6477+
diag::existential_requires_any, type,
6478+
ExistentialType::get(type),
6479+
/*isAlias=*/isa<TypeAliasDecl>(decl)));
64556480
}
6481+
6482+
diag->limitBehaviorUntilSwiftVersion(behavior, 7);
6483+
emitInsertAnyFixit(*diag, T);
64566484
}
64576485

64586486
public:
@@ -6530,12 +6558,12 @@ void TypeChecker::checkExistentialTypes(ASTContext &ctx, Stmt *stmt,
65306558

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

65376565
ExistentialTypeSyntaxChecker checker(ctx, /*checkStatements=*/true,
6538-
downgradeUntilSwift7);
6566+
downgradeErrorsToWarnings);
65396567
stmt->walk(checker);
65406568
}
65416569

test/type/explicit_existential.swift

+13-8
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'}}{{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'}}{{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'}}{{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'}}
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')}}
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'}}
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')}}
576+
_ z2: Copyable) {}
577+
// expected-explicit-any-warning@-1 {{use of protocol 'Copyable' as a type must be written 'any Copyable'}}

test/type/explicit_existential_multimodule2.swift

+5-3
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)