Skip to content

Commit 02261dc

Browse files
committed
[NFC] TypeCheckType: Streamline logic in the any syntax checker
1 parent d620886 commit 02261dc

File tree

2 files changed

+100
-71
lines changed

2 files changed

+100
-71
lines changed

lib/Sema/TypeCheckType.cpp

Lines changed: 97 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6171,7 +6171,7 @@ namespace {
61716171
/// written syntax.
61726172
class ExistentialTypeSyntaxChecker : public ASTWalker {
61736173
ASTContext &Ctx;
6174-
bool checkStatements;
6174+
const bool checkStatements;
61756175
bool hitTopStmt;
61766176
bool warnUntilSwift7;
61776177

@@ -6215,7 +6215,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
62156215
}
62166216

62176217
PostWalkAction walkToTypeReprPost(TypeRepr *T) override {
6218-
assert(reprStack.back() == T);
6218+
ASSERT(reprStack.back() == T);
62196219
reprStack.pop_back();
62206220
return Action::Continue();
62216221
}
@@ -6339,14 +6339,13 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63396339
diag.fixItReplace(replacementT->getSourceRange(), fix);
63406340
}
63416341

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

63486347
if (reprStack.size() < 2) {
6349-
return true;
6348+
return false;
63506349
}
63516350

63526351
auto it = reprStack.end() - 1;
@@ -6356,7 +6355,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63566355
break;
63576356
}
63586357

6359-
// Look through parens, inverses, metatypes, and compositions.
6358+
// Look through parens, inverses, `.Type` metatypes, and compositions.
63606359
if ((*it)->isParenType() || isa<InverseTypeRepr>(*it) ||
63616360
isa<CompositionTypeRepr>(*it) || isa<MetatypeTypeRepr>(*it)) {
63626361
continue;
@@ -6365,84 +6364,114 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
63656364
break;
63666365
}
63676366

6368-
return !(isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it));
6367+
return isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it);
63696368
}
63706369

6371-
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
6372-
assert(!T->isInvalid());
6370+
/// Returns the behavior with which to diagnose a missing `any` or `some`
6371+
/// keyword.
6372+
///
6373+
/// \param constraintTy The constraint type that is missing the keyword.
6374+
/// \param isInverted Whether the constraint type is an object of an `~`
6375+
/// inversion.
6376+
static bool shouldDiagnoseMissingAnyOrSomeKeyword(Type constraintTy,
6377+
bool isInverted,
6378+
ASTContext &ctx) {
6379+
// `Any` and `AnyObject` are always exempt from `any` syntax.
6380+
if (constraintTy->isAny() || constraintTy->isAnyObject()) {
6381+
return false;
6382+
}
63736383

6374-
if (Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
6375-
return;
6384+
// If the type is inverted, a missing `any` or `some` is always diagnosed.
6385+
if (isInverted) {
6386+
return true;
63766387
}
63776388

6378-
// Backtrack the stack, looking just through parentheses and metatypes. If
6379-
// we find an inverse (which always requires `any`), diagnose it specially.
6380-
if (reprStack.size() > 1) {
6381-
auto it = reprStack.end() - 2;
6382-
while (it != reprStack.begin() &&
6383-
((*it)->isParenType() || isa<MetatypeTypeRepr>(*it))) {
6384-
--it;
6385-
continue;
6389+
// If one of the protocols is inverted, a missing `any` or `some` is
6390+
// always diagnosed.
6391+
if (auto *PCT = constraintTy->getAs<ProtocolCompositionType>()) {
6392+
if (!PCT->getInverses().empty()) {
6393+
return true;
63866394
}
6395+
}
63876396

6388-
if (auto *inverse = dyn_cast<InverseTypeRepr>(*it);
6389-
inverse && isAnyOrSomeMissing()) {
6390-
auto diag = Ctx.Diags.diagnose(inverse->getTildeLoc(),
6391-
diag::inverse_requires_any);
6392-
diag.warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6393-
emitInsertAnyFixit(diag, T);
6394-
return;
6397+
// If one of the protocols has "Self or associated type" requirements,
6398+
// a missing `any` or `some` is always diagnosed.
6399+
auto layout = constraintTy->getExistentialLayout();
6400+
for (auto *protoDecl : layout.getProtocols()) {
6401+
if (protoDecl->existentialRequiresAny()) {
6402+
return true;
63956403
}
63966404
}
63976405

6406+
return false;
6407+
}
6408+
6409+
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
6410+
if (Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
6411+
return;
6412+
}
6413+
63986414
auto *decl = T->getBoundDecl();
63996415
if (!decl) {
64006416
return;
64016417
}
64026418

6403-
if (auto *proto = dyn_cast<ProtocolDecl>(decl)) {
6404-
if (proto->existentialRequiresAny() && isAnyOrSomeMissing()) {
6405-
auto diag =
6406-
Ctx.Diags.diagnose(T->getNameLoc(), diag::existential_requires_any,
6407-
proto->getDeclaredInterfaceType(),
6408-
proto->getDeclaredExistentialType(),
6409-
/*isAlias=*/false);
6410-
diag.warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6411-
emitInsertAnyFixit(diag, T);
6419+
if (!isa<ProtocolDecl>(decl) && !isa<TypeAliasDecl>(decl)) {
6420+
return;
6421+
}
6422+
6423+
// If there is already an `any` or `some` that applies to this node,
6424+
// move on.
6425+
if (currentNodeHasAnyOrSomeKeyword()) {
6426+
return;
6427+
}
6428+
6429+
const auto type = decl->getDeclaredInterfaceType();
6430+
6431+
// A type alias may need to be prefixed with `any` only if it stands for a
6432+
// constraint type.
6433+
if (isa<TypeAliasDecl>(decl) && !type->isConstraintType()) {
6434+
return;
6435+
}
6436+
6437+
// First, consider the possibility of the current node being an object of
6438+
// an inversion, e.g. `~(Copyable)`.
6439+
// Climb up the stack, looking just through parentheses and `.Type`
6440+
// metatypes.
6441+
// If we find an inversion, we will diagnose it specially.
6442+
InverseTypeRepr *const outerInversion = [&] {
6443+
if (reprStack.size() < 2) {
6444+
return (InverseTypeRepr *)nullptr;
64126445
}
6413-
} else if (auto *alias = dyn_cast<TypeAliasDecl>(decl)) {
6414-
auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType());
6415-
6416-
// If this is a type alias to a constraint type, the type
6417-
// alias name must be prefixed with 'any' to be used as an
6418-
// existential type.
6419-
if (type->isConstraintType() && !type->isAny() && !type->isAnyObject()) {
6420-
bool diagnose = false;
6421-
6422-
// Look for protocol members that require 'any'.
6423-
auto layout = type->getExistentialLayout();
6424-
for (auto *protoDecl : layout.getProtocols()) {
6425-
if (protoDecl->existentialRequiresAny()) {
6426-
diagnose = true;
6427-
break;
6428-
}
6429-
}
64306446

6431-
// If inverses are present, require 'any' too.
6432-
if (auto *PCT = type->getAs<ProtocolCompositionType>())
6433-
diagnose |= !PCT->getInverses().empty();
6434-
6435-
if (diagnose && isAnyOrSomeMissing()) {
6436-
auto diag = Ctx.Diags.diagnose(
6437-
T->getNameLoc(), diag::existential_requires_any,
6438-
alias->getDeclaredInterfaceType(),
6439-
ExistentialType::get(alias->getDeclaredInterfaceType()),
6440-
/*isAlias=*/true);
6441-
diag.warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6442-
emitInsertAnyFixit(diag, T);
6443-
}
6447+
auto it = reprStack.end() - 2;
6448+
while (it != reprStack.begin() &&
6449+
((*it)->isParenType() || isa<MetatypeTypeRepr>(*it))) {
6450+
--it;
6451+
continue;
64446452
}
6453+
6454+
return dyn_cast<InverseTypeRepr>(*it);
6455+
}();
6456+
6457+
if (!shouldDiagnoseMissingAnyOrSomeKeyword(
6458+
type, /*isInverted=*/outerInversion, this->Ctx)) {
6459+
return;
6460+
}
6461+
6462+
std::optional<InFlightDiagnostic> diag;
6463+
if (outerInversion) {
6464+
diag.emplace(Ctx.Diags.diagnose(outerInversion->getTildeLoc(),
6465+
diag::inverse_requires_any));
6466+
} else {
6467+
diag.emplace(Ctx.Diags.diagnose(T->getNameLoc(),
6468+
diag::existential_requires_any, type,
6469+
ExistentialType::get(type),
6470+
/*isAlias=*/isa<TypeAliasDecl>(decl)));
64456471
}
6472+
6473+
diag->warnUntilSwiftVersionIf(warnUntilSwift7, 7);
6474+
emitInsertAnyFixit(*diag, T);
64466475
}
64476476

64486477
public:

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)