Skip to content

Commit 93f4fda

Browse files
committed
[NFC] TypeCheckType: Streamline logic in the any syntax checker
1 parent 55189ba commit 93f4fda

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
@@ -6157,7 +6157,7 @@ namespace {
61576157
/// written syntax.
61586158
class ExistentialTypeSyntaxChecker : public ASTWalker {
61596159
ASTContext &Ctx;
6160-
bool checkStatements;
6160+
const bool checkStatements;
61616161
bool hitTopStmt;
61626162
bool warnUntilSwift7;
61636163

@@ -6201,7 +6201,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
62016201
}
62026202

62036203
PostWalkAction walkToTypeReprPost(TypeRepr *T) override {
6204-
assert(reprStack.back() == T);
6204+
ASSERT(reprStack.back() == T);
62056205
reprStack.pop_back();
62066206
return Action::Continue();
62076207
}
@@ -6325,14 +6325,13 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63256325
diag.fixItReplace(replacementT->getSourceRange(), fix);
63266326
}
63276327

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

63346333
if (reprStack.size() < 2) {
6335-
return true;
6334+
return false;
63366335
}
63376336

63386337
auto it = reprStack.end() - 1;
@@ -6342,7 +6341,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63426341
break;
63436342
}
63446343

6345-
// Look through parens, inverses, metatypes, and compositions.
6344+
// Look through parens, inverses, `.Type` metatypes, and compositions.
63466345
if ((*it)->isParenType() || isa<InverseTypeRepr>(*it) ||
63476346
isa<CompositionTypeRepr>(*it) || isa<MetatypeTypeRepr>(*it)) {
63486347
continue;
@@ -6358,83 +6357,99 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63586357
break;
63596358
}
63606359

6361-
return !(isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it));
6360+
return isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it);
63626361
}
63636362

63646363
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
6365-
assert(!T->isInvalid());
6366-
63676364
if (Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
63686365
return;
63696366
}
63706367

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

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

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

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

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)