Skip to content

Commit f02d300

Browse files
Merge pull request #72659 from AnthonyLatsis/many-any-bugs
TypeCheckType: Fix some bugs in the `any` syntax checker
2 parents 67c0fd8 + 6e5d6f1 commit f02d300

File tree

7 files changed

+493
-130
lines changed

7 files changed

+493
-130
lines changed

include/swift/AST/TypeRepr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr
184184
/// opaque return type reprs.
185185
bool hasOpaque();
186186

187+
/// Returns a Boolean value indicating whether this written type is
188+
/// parenthesized, that is, matches the following grammar: `'(' type ')'`.
189+
bool isParenType() const;
190+
187191
/// Retrieve the type repr without any parentheses around it.
188192
///
189193
/// The use of this function must be restricted to contexts where

lib/AST/TypeRepr.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,18 @@ bool TypeRepr::hasOpaque() {
109109
findIf([](TypeRepr *ty) { return isa<OpaqueReturnTypeRepr>(ty); });
110110
}
111111

112+
bool TypeRepr::isParenType() const {
113+
auto *tuple = dyn_cast<TupleTypeRepr>(this);
114+
return tuple && tuple->isParenType();
115+
}
116+
112117
TypeRepr *TypeRepr::getWithoutParens() const {
113-
auto *repr = const_cast<TypeRepr *>(this);
114-
while (auto *tupleRepr = dyn_cast<TupleTypeRepr>(repr)) {
115-
if (!tupleRepr->isParenType())
116-
break;
117-
repr = tupleRepr->getElementType(0);
118+
auto *result = this;
119+
while (result->isParenType()) {
120+
result = cast<TupleTypeRepr>(result)->getElementType(0);
118121
}
119-
return repr;
122+
123+
return const_cast<TypeRepr *>(result);
120124
}
121125

122126
bool TypeRepr::isSimpleUnqualifiedIdentifier(Identifier identifier) const {

lib/Sema/TypeCheckType.cpp

Lines changed: 96 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5980,31 +5980,6 @@ 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();
5990-
5991-
// Suppressed conformance needs to be within any/some.
5992-
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();
5999-
}
6000-
}
6001-
6002-
Ctx.Diags.diagnose(inverse->getTildeLoc(), diag::inverse_requires_any)
6003-
.highlight(inverse->getConstraint()->getSourceRange())
6004-
.fixItInsert(anyLoc, "any ");
6005-
return Action::SkipNode();
6006-
}
6007-
60085983
reprStack.push_back(T);
60095984

60105985
auto *declRefTR = dyn_cast<DeclRefTypeRepr>(T);
@@ -6057,9 +6032,12 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
60576032
}
60586033

60596034
private:
6060-
bool existentialNeedsParens(TypeRepr *parent) const {
6035+
/// Returns a Boolean value indicating whether the insertion of `any` before
6036+
/// a type representation with the given parent requires paretheses.
6037+
static bool anySyntaxNeedsParens(TypeRepr *parent) {
60616038
switch (parent->getKind()) {
60626039
case TypeReprKind::Optional:
6040+
case TypeReprKind::ImplicitlyUnwrappedOptional:
60636041
case TypeReprKind::Protocol:
60646042
return true;
60656043
case TypeReprKind::Metatype:
@@ -6074,7 +6052,6 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
60746052
case TypeReprKind::UnqualifiedIdent:
60756053
case TypeReprKind::QualifiedIdent:
60766054
case TypeReprKind::Dictionary:
6077-
case TypeReprKind::ImplicitlyUnwrappedOptional:
60786055
case TypeReprKind::Inverse:
60796056
case TypeReprKind::Tuple:
60806057
case TypeReprKind::Fixed:
@@ -6097,40 +6074,94 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
60976074
}
60986075

60996076
void emitInsertAnyFixit(InFlightDiagnostic &diag, DeclRefTypeRepr *T) const {
6100-
TypeRepr *replaceRepr = T;
6077+
TypeRepr *replacementT = T;
61016078

61026079
// Insert parens in expression context for '(any P).self'
61036080
bool needsParens = (exprCount != 0);
6081+
6082+
// Compute the replacement node (the node to which to apply `any`).
61046083
if (reprStack.size() > 1) {
6105-
auto parentIt = reprStack.end() - 2;
6106-
needsParens = existentialNeedsParens(*parentIt);
6107-
6108-
// Expand to include parenthesis before checking if the parent needs
6109-
// to be replaced.
6110-
while (parentIt != reprStack.begin() &&
6111-
(*parentIt)->getWithoutParens() != *parentIt)
6112-
parentIt -= 1;
6113-
6114-
// For existential metatypes, 'any' is applied to the metatype.
6115-
if ((*parentIt)->getKind() == TypeReprKind::Metatype) {
6116-
replaceRepr = *parentIt;
6117-
if (parentIt != reprStack.begin())
6118-
needsParens = existentialNeedsParens(*(parentIt - 1));
6084+
auto it = reprStack.end() - 1;
6085+
auto replacementIt = it;
6086+
6087+
// Backtrack the stack and expand the replacement range to any parent
6088+
// inverses, compositions or `.Type` metatypes, skipping only parentheses.
6089+
//
6090+
// E.g. `(X & ~P).Type` → `any (X & ~P).Type`.
6091+
//
6092+
// We're here
6093+
do {
6094+
--it;
6095+
if ((*it)->isParenType()) {
6096+
continue;
6097+
}
6098+
6099+
if (isa<InverseTypeRepr>(*it) || isa<CompositionTypeRepr>(*it) ||
6100+
isa<MetatypeTypeRepr>(*it)) {
6101+
replacementIt = it;
6102+
continue;
6103+
}
6104+
6105+
break;
6106+
} while (it != reprStack.begin());
6107+
6108+
// Whether parentheses are necessary is determined by the immediate parent
6109+
// of the replacement node.
6110+
if (replacementIt != reprStack.begin()) {
6111+
needsParens = anySyntaxNeedsParens(*(replacementIt - 1));
61196112
}
6113+
6114+
replacementT = *replacementIt;
61206115
}
61216116

61226117
llvm::SmallString<64> fix;
61236118
{
61246119
llvm::raw_svector_ostream OS(fix);
61256120
if (needsParens)
61266121
OS << "(";
6127-
ExistentialTypeRepr existential(SourceLoc(), replaceRepr);
6122+
ExistentialTypeRepr existential(SourceLoc(), replacementT);
61286123
existential.print(OS);
61296124
if (needsParens)
61306125
OS << ")";
61316126
}
61326127

6133-
diag.fixItReplace(replaceRepr->getSourceRange(), fix);
6128+
diag.fixItReplace(replacementT->getSourceRange(), fix);
6129+
}
6130+
6131+
/// Returns a Boolean value indicating whether the type representation being
6132+
/// visited, assuming it is a constraint type demanding `any` or `some`, is
6133+
/// missing either keyword.
6134+
bool isAnyOrSomeMissing() const {
6135+
assert(isa<DeclRefTypeRepr>(reprStack.back()));
6136+
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));
61346165
}
61356166

61366167
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {
@@ -6140,13 +6171,32 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
61406171
return;
61416172
}
61426173

6174+
// Backtrack the stack, looking just through parentheses and metatypes. If
6175+
// we find an inverse (which always requires `any`), diagnose it specially.
6176+
if (reprStack.size() > 1) {
6177+
auto it = reprStack.end() - 2;
6178+
while (it != reprStack.begin() &&
6179+
((*it)->isParenType() || isa<MetatypeTypeRepr>(*it))) {
6180+
--it;
6181+
continue;
6182+
}
6183+
6184+
if (auto *inverse = dyn_cast<InverseTypeRepr>(*it);
6185+
inverse && isAnyOrSomeMissing()) {
6186+
auto diag = Ctx.Diags.diagnose(inverse->getTildeLoc(),
6187+
diag::inverse_requires_any);
6188+
emitInsertAnyFixit(diag, T);
6189+
return;
6190+
}
6191+
}
6192+
61436193
auto *decl = T->getBoundDecl();
61446194
if (!decl) {
61456195
return;
61466196
}
61476197

61486198
if (auto *proto = dyn_cast<ProtocolDecl>(decl)) {
6149-
if (proto->existentialRequiresAny()) {
6199+
if (proto->existentialRequiresAny() && isAnyOrSomeMissing()) {
61506200
auto diag =
61516201
Ctx.Diags.diagnose(T->getNameLoc(), diag::existential_requires_any,
61526202
proto->getDeclaredInterfaceType(),
@@ -6176,7 +6226,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
61766226
if (auto *PCT = type->getAs<ProtocolCompositionType>())
61776227
diagnose |= !PCT->getInverses().empty();
61786228

6179-
if (diagnose) {
6229+
if (diagnose && isAnyOrSomeMissing()) {
61806230
auto diag = Ctx.Diags.diagnose(
61816231
T->getNameLoc(), diag::existential_requires_any,
61826232
alias->getDeclaredInterfaceType(),

test/Parse/inverses.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ func what(one: ~Copyable..., // expected-error {{noncopyable type '~Copyable' ca
9292

9393
struct A { struct B { struct C {} } }
9494

95-
typealias Z0 = (~Copyable).Type // expected-error{{constraint that suppresses conformance requires 'any'}}{{17-17=any }}
96-
typealias Z1 = ~Copyable.Type // expected-error{{constraint that suppresses conformance requires 'any'}}{{16-16=any }}
9795
typealias Z2 = ~A.B.C // expected-error {{type 'A.B.C' cannot be suppressed}}
9896
typealias Z3 = ~A? // expected-error {{type 'A?' cannot be suppressed}}
9997
typealias Z4 = ~Rope<Int> // expected-error {{type 'Rope<Int>' cannot be suppressed}}
@@ -120,13 +118,8 @@ func typeInExpression() {
120118
_ = X<(borrowing any ~Copyable) -> Void>()
121119

122120
_ = ~Copyable.self // expected-error{{unary operator '~' cannot be applied to an operand of type '(any Copyable).Type'}}
123-
_ = (~Copyable).self // expected-error{{constraint that suppresses conformance requires 'any'}}{{8-8=any }}
124121
_ = (any ~Copyable).self
125122
}
126123

127-
func param1(_ t: borrowing ~Copyable) {} // expected-error{{constraint that suppresses conformance requires 'any'}}{{28-28=any }}
128-
func param2(_ t: ~Copyable.Type) {} // expected-error{{constraint that suppresses conformance requires 'any'}}{{18-18=any }}
129124
func param3(_ t: borrowing any ~Copyable) {}
130125
func param4(_ t: any ~Copyable.Type) {}
131-
132-
func param3(_ t: borrowing ExtraNoncopyProto & ~Copyable) {} // expected-error{{constraint that suppresses conformance requires 'any'}}{{28-28=any }}

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}}

0 commit comments

Comments
 (0)