Skip to content

Commit 9d704bd

Browse files
committed
[NFC] TypeCheckType: Streamline logic in the any syntax checker
1 parent 33bff48 commit 9d704bd

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
@@ -6159,7 +6159,7 @@ namespace {
61596159
/// written syntax.
61606160
class ExistentialTypeSyntaxChecker : public ASTWalker {
61616161
ASTContext &Ctx;
6162-
bool checkStatements;
6162+
const bool checkStatements;
61636163
bool hitTopStmt;
61646164
bool warnUntilSwift7;
61656165

@@ -6203,7 +6203,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
62036203
}
62046204

62056205
PostWalkAction walkToTypeReprPost(TypeRepr *T) override {
6206-
assert(reprStack.back() == T);
6206+
ASSERT(reprStack.back() == T);
62076207
reprStack.pop_back();
62086208
return Action::Continue();
62096209
}
@@ -6327,14 +6327,13 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63276327
diag.fixItReplace(replacementT->getSourceRange(), fix);
63286328
}
63296329

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

63366335
if (reprStack.size() < 2) {
6337-
return true;
6336+
return false;
63386337
}
63396338

63406339
auto it = reprStack.end() - 1;
@@ -6344,7 +6343,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63446343
break;
63456344
}
63466345

6347-
// Look through parens, inverses, metatypes, and compositions.
6346+
// Look through parens, inverses, `.Type` metatypes, and compositions.
63486347
if ((*it)->isParenType() || isa<InverseTypeRepr>(*it) ||
63496348
isa<CompositionTypeRepr>(*it) || isa<MetatypeTypeRepr>(*it)) {
63506349
continue;
@@ -6353,83 +6352,99 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63536352
break;
63546353
}
63556354

6356-
return !(isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it));
6355+
return isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it);
63576356
}
63586357

63596358
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
6360-
assert(!T->isInvalid());
6361-
63626359
if (Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
63636360
return;
63646361
}
63656362

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

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

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

6419-
// If inverses are present, require 'any' too.
6420-
if (auto *PCT = type->getAs<ProtocolCompositionType>())
6421-
diagnose |= !PCT->getInverses().empty();
6422-
6423-
if (diagnose && isAnyOrSomeMissing()) {
6424-
auto diag = Ctx.Diags.diagnose(
6425-
T->getNameLoc(), diag::existential_requires_any,
6426-
alias->getDeclaredInterfaceType(),
6427-
ExistentialType::get(alias->getDeclaredInterfaceType()),
6428-
/*isAlias=*/true);
6429-
diag.warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6430-
emitInsertAnyFixit(diag, T);
6420+
// If inverses are present, require 'any' too.
6421+
if (auto *PCT = type->getAs<ProtocolCompositionType>()) {
6422+
if (!PCT->getInverses().empty()) {
6423+
return true;
64316424
}
64326425
}
6426+
6427+
if (outerInversion) {
6428+
return true;
6429+
}
6430+
6431+
return false;
6432+
}();
6433+
6434+
if (shouldDiagnose) {
6435+
std::optional<InFlightDiagnostic> diag;
6436+
if (outerInversion) {
6437+
diag.emplace(Ctx.Diags.diagnose(outerInversion->getTildeLoc(),
6438+
diag::inverse_requires_any));
6439+
} else {
6440+
diag.emplace(Ctx.Diags.diagnose(T->getNameLoc(),
6441+
diag::existential_requires_any, type,
6442+
ExistentialType::get(type),
6443+
/*isAlias=*/isa<TypeAliasDecl>(decl)));
6444+
}
6445+
6446+
diag->warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6447+
emitInsertAnyFixit(*diag, T);
64336448
}
64346449
}
64356450

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)