Skip to content

Commit 44e5286

Browse files
committed
ExistentialTypeSyntaxChecker: Look through any and some
1 parent 1b6efc1 commit 44e5286

File tree

4 files changed

+67
-28
lines changed

4 files changed

+67
-28
lines changed

lib/Sema/TypeCheckType.cpp

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5980,33 +5980,28 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
59805980
if (T->isInvalid())
59815981
return Action::SkipNode();
59825982

5983-
// Arbitrary protocol constraints are OK on opaque types.
5984-
if (isa<OpaqueReturnTypeRepr>(T))
5985-
return Action::SkipNode();
5986-
5987-
// Arbitrary protocol constraints are okay for 'any' types.
5988-
if (isa<ExistentialTypeRepr>(T))
5989-
return Action::SkipNode();
5983+
reprStack.push_back(T);
59905984

59915985
// Suppressed conformance needs to be within any/some.
59925986
if (auto inverse = dyn_cast<InverseTypeRepr>(T)) {
5993-
// Find an enclosing protocol composition, if there is one, so we
5994-
// can insert 'any' before that.
5995-
SourceLoc anyLoc = inverse->getTildeLoc();
5996-
if (!reprStack.empty()) {
5997-
if (isa<CompositionTypeRepr>(reprStack.back())) {
5998-
anyLoc = reprStack.back()->getStartLoc();
5987+
if (isAnyOrSomeMissing()) {
5988+
// Find an enclosing protocol composition, if there is one, so we
5989+
// can insert 'any' before that.
5990+
SourceLoc anyLoc = inverse->getTildeLoc();
5991+
if (reprStack.size() > 1) {
5992+
if (auto *repr =
5993+
dyn_cast<CompositionTypeRepr>(*(reprStack.end() - 2))) {
5994+
anyLoc = repr->getStartLoc();
5995+
}
59995996
}
6000-
}
60015997

6002-
Ctx.Diags.diagnose(inverse->getTildeLoc(), diag::inverse_requires_any)
6003-
.highlight(inverse->getConstraint()->getSourceRange())
6004-
.fixItInsert(anyLoc, "any ");
6005-
return Action::SkipNode();
5998+
Ctx.Diags.diagnose(inverse->getTildeLoc(), diag::inverse_requires_any)
5999+
.highlight(inverse->getConstraint()->getSourceRange())
6000+
.fixItInsert(anyLoc, "any ");
6001+
}
6002+
return Action::SkipChildren();
60066003
}
60076004

6008-
reprStack.push_back(T);
6009-
60106005
auto *declRefTR = dyn_cast<DeclRefTypeRepr>(T);
60116006
if (!declRefTR) {
60126007
return Action::Continue();
@@ -6135,6 +6130,40 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
61356130
diag.fixItReplace(replaceRepr->getSourceRange(), fix);
61366131
}
61376132

6133+
/// Returns a Boolean value indicating whether the type representation being
6134+
/// visited, assuming it is a constraint type demanding `any` or `some`, is
6135+
/// missing either keyword.
6136+
bool isAnyOrSomeMissing() const {
6137+
if (reprStack.size() < 2) {
6138+
return true;
6139+
}
6140+
6141+
auto it = reprStack.end() - 1;
6142+
while (true) {
6143+
--it;
6144+
if (it == reprStack.begin()) {
6145+
break;
6146+
}
6147+
6148+
// Look through parens, inverses, metatypes, and compositions.
6149+
if ((*it)->isParenType() || isa<InverseTypeRepr>(*it) ||
6150+
isa<CompositionTypeRepr>(*it) || isa<MetatypeTypeRepr>(*it)) {
6151+
continue;
6152+
}
6153+
6154+
// Look through '?' and '!' too; `any P?` et al. is diagnosed in the
6155+
// type resolver.
6156+
if (isa<OptionalTypeRepr>(*it) ||
6157+
isa<ImplicitlyUnwrappedOptionalTypeRepr>(*it)) {
6158+
continue;
6159+
}
6160+
6161+
break;
6162+
}
6163+
6164+
return !(isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it));
6165+
}
6166+
61386167
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
61396168
assert(!T->isInvalid());
61406169

@@ -6148,7 +6177,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
61486177
}
61496178

61506179
if (auto *proto = dyn_cast<ProtocolDecl>(decl)) {
6151-
if (proto->existentialRequiresAny()) {
6180+
if (proto->existentialRequiresAny() && isAnyOrSomeMissing()) {
61526181
auto diag =
61536182
Ctx.Diags.diagnose(T->getNameLoc(), diag::existential_requires_any,
61546183
proto->getDeclaredInterfaceType(),
@@ -6178,7 +6207,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
61786207
if (auto *PCT = type->getAs<ProtocolCompositionType>())
61796208
diagnose |= !PCT->getInverses().empty();
61806209

6181-
if (diagnose) {
6210+
if (diagnose && isAnyOrSomeMissing()) {
61826211
auto diag = Ctx.Diags.diagnose(
61836212
T->getNameLoc(), diag::existential_requires_any,
61846213
alias->getDeclaredInterfaceType(),

test/decl/protocol/protocols.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ struct DoesNotConform : Up {
103103
// Circular protocols
104104

105105
protocol CircleMiddle : CircleStart { func circle_middle() }
106-
// expected-note@-1 2 {{protocol 'CircleMiddle' declared here}}
107-
protocol CircleStart : CircleEnd { func circle_start() } // expected-error 2 {{protocol 'CircleStart' refines itself}}
108-
protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 2 {{protocol 'CircleEnd' declared here}}
106+
// expected-note@-1 3 {{protocol 'CircleMiddle' declared here}}
107+
protocol CircleStart : CircleEnd { func circle_start() } // expected-error 3 {{protocol 'CircleStart' refines itself}}
108+
protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 3 {{protocol 'CircleEnd' declared here}}
109109

110110
protocol CircleEntry : CircleTrivial { }
111111
protocol CircleTrivial : CircleTrivial { } // expected-error {{protocol 'CircleTrivial' refines itself}}

test/type/explicit_existential.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,12 +433,16 @@ func testAnyFixIt() {
433433
// expected-error@+1 {{constraint that suppresses conformance requires 'any'}}{{21-21=any }}
434434
let _: (borrowing ~Copyable) -> Void
435435
// https://github.com/apple/swift/issues/72588
436-
// FIXME: No diagnostic.
436+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{30-38=any HasAssoc}}
437437
let _: any HasAssocGeneric<HasAssoc>
438+
// expected-error@+1 {{constraint that suppresses conformance requires 'any'}}{{30-30=any }}
438439
let _: any HasAssocGeneric<~Copyable>
440+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{16-24=any HasAssoc}}
439441
let _: any G<HasAssoc>.HasAssoc_Alias
442+
// FIXME: Generic argument not diagnosed.
440443
let _: any ~G<HasAssoc>.Copyable_Alias
441444
do {
445+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{22-30=any HasAssoc}}
442446
func f(_: some G<HasAssoc>.HasAssoc_Alias) {}
443447
}
444448

@@ -448,6 +452,7 @@ func testAnyFixIt() {
448452
// expected-error@+2 {{constraint that suppresses conformance requires 'any'}}{{21-21=any }}
449453
// expected-error@+1 {{use of protocol 'NonCopyableHasAssoc' as a type must be written 'any NonCopyableHasAssoc'}}{{21-40=any NonCopyableHasAssoc}}
450454
let _: (borrowing NonCopyableHasAssoc & ~Copyable) -> Void
455+
let _: any (((((~Copyable) & NonCopyableHasAssoc) & NonCopyableHasAssoc).Type.Type)).Type // OK
451456

452457
// Misplaced '?'.
453458

test/type/explicit_existential_swift6.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func testHasAssoc(_ x: Any, _: any HasAssoc) {
9494

9595
func method() -> any HasAssoc {}
9696
func existentialArray() -> [any HasAssoc] {}
97-
func existentialcSequence() -> any Sequence<HasAssoc> {}
97+
func existentialcSequence() -> any Sequence<any HasAssoc> {}
9898
}
9999
}
100100

@@ -465,12 +465,16 @@ func testAnyFixIt() {
465465
// expected-error@+1 {{constraint that suppresses conformance requires 'any'}}{{21-21=any }}
466466
let _: (borrowing ~Copyable) -> Void
467467
// https://github.com/apple/swift/issues/72588
468-
// FIXME: No diagnostic.
468+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{30-38=any HasAssoc}}
469469
let _: any HasAssocGeneric<HasAssoc>
470+
// expected-error@+1 {{constraint that suppresses conformance requires 'any'}}{{30-30=any }}
470471
let _: any HasAssocGeneric<~Copyable>
472+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{16-24=any HasAssoc}}
471473
let _: any G<HasAssoc>.HasAssoc_Alias
474+
// FIXME: Generic argument not diagnosed.
472475
let _: any ~G<HasAssoc>.Copyable_Alias
473476
do {
477+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{22-30=any HasAssoc}}
474478
func f(_: some G<HasAssoc>.HasAssoc_Alias) {}
475479
}
476480

@@ -480,6 +484,7 @@ func testAnyFixIt() {
480484
// expected-error@+2 {{constraint that suppresses conformance requires 'any'}}{{21-21=any }}
481485
// expected-error@+1 {{use of protocol 'NonCopyableHasAssoc' as a type must be written 'any NonCopyableHasAssoc'}}{{21-40=any NonCopyableHasAssoc}}
482486
let _: (borrowing NonCopyableHasAssoc & ~Copyable) -> Void
487+
let _: any (((((~Copyable) & NonCopyableHasAssoc) & NonCopyableHasAssoc).Type.Type)).Type // OK
483488

484489
// Misplaced '?'.
485490

0 commit comments

Comments
 (0)