Skip to content

Commit 78179da

Browse files
committed
[NFC] TypeCheckType: Streamline logic in the any syntax checker
1 parent 1573344 commit 78179da

File tree

2 files changed

+84
-69
lines changed

2 files changed

+84
-69
lines changed

lib/Sema/TypeCheckType.cpp

Lines changed: 81 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6161,7 +6161,7 @@ namespace {
61616161
/// written syntax.
61626162
class ExistentialTypeSyntaxChecker : public ASTWalker {
61636163
ASTContext &Ctx;
6164-
bool checkStatements;
6164+
const bool checkStatements;
61656165
bool hitTopStmt;
61666166
bool warnUntilSwift7;
61676167

@@ -6205,7 +6205,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
62056205
}
62066206

62076207
PostWalkAction walkToTypeReprPost(TypeRepr *T) override {
6208-
assert(reprStack.back() == T);
6208+
ASSERT(reprStack.back() == T);
62096209
reprStack.pop_back();
62106210
return Action::Continue();
62116211
}
@@ -6329,14 +6329,13 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63296329
diag.fixItReplace(replacementT->getSourceRange(), fix);
63306330
}
63316331

6332-
/// Returns a Boolean value indicating whether the type representation being
6333-
/// visited, assuming it is a constraint type demanding `any` or `some`, is
6334-
/// missing either keyword.
6335-
bool isAnyOrSomeMissing() const {
6336-
assert(isa<DeclRefTypeRepr>(reprStack.back()));
6332+
/// Returns a Boolean value indicating whether the currently visited
6333+
/// `DeclRefTypeRepr` node has a `some` or `any` keyword that applies to it.
6334+
bool currentNodeHasAnyOrSomeKeyword() const {
6335+
ASSERT(isa<DeclRefTypeRepr>(reprStack.back()));
63376336

63386337
if (reprStack.size() < 2) {
6339-
return true;
6338+
return false;
63406339
}
63416340

63426341
auto it = reprStack.end() - 1;
@@ -6346,7 +6345,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63466345
break;
63476346
}
63486347

6349-
// Look through parens, inverses, metatypes, and compositions.
6348+
// Look through parens, inverses, `.Type` metatypes, and compositions.
63506349
if ((*it)->isParenType() || isa<InverseTypeRepr>(*it) ||
63516350
isa<CompositionTypeRepr>(*it) || isa<MetatypeTypeRepr>(*it)) {
63526351
continue;
@@ -6355,83 +6354,99 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63556354
break;
63566355
}
63576356

6358-
return !(isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it));
6357+
return isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it);
63596358
}
63606359

63616360
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
6362-
assert(!T->isInvalid());
6363-
63646361
if (Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
63656362
return;
63666363
}
63676364

6368-
// Backtrack the stack, looking just through parentheses and metatypes. If
6369-
// we find an inverse (which always requires `any`), diagnose it specially.
6370-
if (reprStack.size() > 1) {
6365+
auto *decl = T->getBoundDecl();
6366+
if (!decl) {
6367+
return;
6368+
}
6369+
6370+
if (!isa<ProtocolDecl>(decl) && !isa<TypeAliasDecl>(decl)) {
6371+
return;
6372+
}
6373+
6374+
// If there is already an `any` or `some` that applies to this node,
6375+
// move on.
6376+
if (currentNodeHasAnyOrSomeKeyword()) {
6377+
return;
6378+
}
6379+
6380+
const auto type = decl->getDeclaredInterfaceType();
6381+
6382+
// A type alias may need to be prefixed with `any` only if it stands for a
6383+
// constraint type.
6384+
if (isa<TypeAliasDecl>(decl) && !type->isConstraintType()) {
6385+
return;
6386+
}
6387+
6388+
// First, consider the possibility of the current node being an object of
6389+
// an inversion, e.g. `~(Copyable)`.
6390+
// Climb up the stack, looking just through parentheses and `.Type`
6391+
// metatypes.
6392+
// If we find an inversion, we will diagnose it specially.
6393+
InverseTypeRepr *const outerInversion = [&] {
6394+
if (reprStack.size() < 2) {
6395+
return (InverseTypeRepr *)nullptr;
6396+
}
6397+
63716398
auto it = reprStack.end() - 2;
63726399
while (it != reprStack.begin() &&
63736400
((*it)->isParenType() || isa<MetatypeTypeRepr>(*it))) {
63746401
--it;
63756402
continue;
63766403
}
63776404

6378-
if (auto *inverse = dyn_cast<InverseTypeRepr>(*it);
6379-
inverse && isAnyOrSomeMissing()) {
6380-
auto diag = Ctx.Diags.diagnose(inverse->getTildeLoc(),
6381-
diag::inverse_requires_any);
6382-
diag.warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6383-
emitInsertAnyFixit(diag, T);
6384-
return;
6385-
}
6386-
}
6387-
6388-
auto *decl = T->getBoundDecl();
6389-
if (!decl) {
6390-
return;
6391-
}
6405+
return dyn_cast<InverseTypeRepr>(*it);
6406+
}();
63926407

6393-
if (auto *proto = dyn_cast<ProtocolDecl>(decl)) {
6394-
if (proto->existentialRequiresAny() && isAnyOrSomeMissing()) {
6395-
auto diag =
6396-
Ctx.Diags.diagnose(T->getNameLoc(), diag::existential_requires_any,
6397-
proto->getDeclaredInterfaceType(),
6398-
proto->getDeclaredExistentialType(),
6399-
/*isAlias=*/false);
6400-
diag.warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6401-
emitInsertAnyFixit(diag, T);
6408+
const bool shouldDiagnose = [&] {
6409+
// `Any` and `AnyObject` are always exempt from `any` syntax.
6410+
if (type->isAny() || type->isAnyObject()) {
6411+
return false;
64026412
}
6403-
} else if (auto *alias = dyn_cast<TypeAliasDecl>(decl)) {
6404-
auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType());
6405-
6406-
// If this is a type alias to a constraint type, the type
6407-
// alias name must be prefixed with 'any' to be used as an
6408-
// existential type.
6409-
if (type->isConstraintType() && !type->isAny() && !type->isAnyObject()) {
6410-
bool diagnose = false;
6411-
6412-
// Look for protocol members that require 'any'.
6413-
auto layout = type->getExistentialLayout();
6414-
for (auto *protoDecl : layout.getProtocols()) {
6415-
if (protoDecl->existentialRequiresAny()) {
6416-
diagnose = true;
6417-
break;
6418-
}
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;
64196419
}
6420+
}
64206421

6421-
// If inverses are present, require 'any' too.
6422-
if (auto *PCT = type->getAs<ProtocolCompositionType>())
6423-
diagnose |= !PCT->getInverses().empty();
6424-
6425-
if (diagnose && isAnyOrSomeMissing()) {
6426-
auto diag = Ctx.Diags.diagnose(
6427-
T->getNameLoc(), diag::existential_requires_any,
6428-
alias->getDeclaredInterfaceType(),
6429-
ExistentialType::get(alias->getDeclaredInterfaceType()),
6430-
/*isAlias=*/true);
6431-
diag.warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6432-
emitInsertAnyFixit(diag, T);
6422+
// If inverses are present, require 'any' too.
6423+
if (auto *PCT = type->getAs<ProtocolCompositionType>()) {
6424+
if (!PCT->getInverses().empty()) {
6425+
return true;
64336426
}
64346427
}
6428+
6429+
if (outerInversion) {
6430+
return true;
6431+
}
6432+
6433+
return false;
6434+
}();
6435+
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+
}
6447+
6448+
diag->warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6449+
emitInsertAnyFixit(*diag, T);
64356450
}
64366451
}
64376452

test/decl/protocol/protocols.swift

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

114114
protocol CircleMiddle : CircleStart { func circle_middle() }
115-
// expected-note@-1 3 {{protocol 'CircleMiddle' declared here}}
116-
protocol CircleStart : CircleEnd { func circle_start() } // expected-error 3 {{protocol 'CircleStart' refines itself}}
117-
protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 3 {{protocol 'CircleEnd' declared here}}
115+
// expected-note@-1 2 {{protocol 'CircleMiddle' declared here}}
116+
protocol CircleStart : CircleEnd { func circle_start() } // expected-error 2 {{protocol 'CircleStart' refines itself}}
117+
protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 2 {{protocol 'CircleEnd' declared here}}
118118

119119
protocol CircleEntry : CircleTrivial { }
120120
protocol CircleTrivial : CircleTrivial { } // expected-error {{protocol 'CircleTrivial' refines itself}}

0 commit comments

Comments
 (0)