Skip to content

Commit 826515f

Browse files
authored
Merge pull request #72745 from DougGregor/parse-inverted-in-exprs
Parse inverse requirements like `~Copyable` in expression position.
2 parents 375db9d + 7eb964c commit 826515f

File tree

3 files changed

+60
-1
lines changed

3 files changed

+60
-1
lines changed

lib/Parse/ParseType.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,15 @@ bool Parser::canParseType() {
15671567
if (!canParseTypeIdentifier())
15681568
return false;
15691569
break;
1570+
case tok::oper_prefix:
1571+
if (Tok.getText() != "~") {
1572+
return false;
1573+
}
1574+
1575+
consumeToken();
1576+
if (!canParseTypeIdentifier())
1577+
return false;
1578+
break;
15701579
case tok::kw_protocol:
15711580
return canParseOldStyleProtocolComposition();
15721581
case tok::l_paren: {

lib/Sema/PreCheckExpr.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,10 +1826,37 @@ VarDecl *PreCheckExpression::getImplicitSelfDeclForSuperContext(SourceLoc Loc) {
18261826
return methodSelf;
18271827
}
18281828

1829+
/// Check whether this expression refers to the ~ operator.
1830+
static bool isTildeOperator(Expr *expr) {
1831+
auto nameMatches = [&](DeclName name) {
1832+
return name.isOperator() && name.getBaseName().getIdentifier().is("~");
1833+
};
1834+
1835+
if (auto overload = dyn_cast<OverloadedDeclRefExpr>(expr)) {
1836+
return llvm::any_of(overload->getDecls(), [=](auto *decl) -> bool {
1837+
return nameMatches(decl->getName());
1838+
});
1839+
}
1840+
1841+
if (auto unresolved = dyn_cast<UnresolvedDeclRefExpr>(expr)) {
1842+
return nameMatches(unresolved->getName().getFullName());
1843+
}
1844+
1845+
if (auto declRef = dyn_cast<DeclRefExpr>(expr)) {
1846+
return nameMatches(declRef->getDecl()->getName());
1847+
}
1848+
1849+
return false;
1850+
}
1851+
18291852
/// Simplify expressions which are type sugar productions that got parsed
18301853
/// as expressions due to the parser not knowing which identifiers are
18311854
/// type names.
18321855
TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) {
1856+
// If it's already a type expression, return it.
1857+
if (auto typeExpr = dyn_cast<TypeExpr>(E))
1858+
return typeExpr;
1859+
18331860
// Fold member types.
18341861
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E)) {
18351862
return simplifyNestedTypeExpr(UDE);
@@ -2081,6 +2108,17 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) {
20812108
return new (Ctx) TypeExpr(NewTypeRepr);
20822109
}
20832110

2111+
// Fold '~P' into a composition type.
2112+
if (auto *unaryExpr = dyn_cast<PrefixUnaryExpr>(E)) {
2113+
if (isTildeOperator(unaryExpr->getFn())) {
2114+
if (auto operand = simplifyTypeExpr(unaryExpr->getOperand())) {
2115+
auto inverseTypeRepr = new (Ctx) InverseTypeRepr(
2116+
unaryExpr->getLoc(), operand->getTypeRepr());
2117+
return new (Ctx) TypeExpr(inverseTypeRepr);
2118+
}
2119+
}
2120+
}
2121+
20842122
// Fold 'P & Q' into a composition type
20852123
if (auto *binaryExpr = getCompositionExpr(E)) {
20862124
// The protocols we are composing

test/Parse/inverses.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,16 @@ typealias Z8 = ~Copyable & Hashable // expected-error {{composition cannot conta
101101

102102
struct NotAProtocol {}
103103

104-
struct Bad: ~NotAProtocol {} // expected-error {{type 'NotAProtocol' cannot be suppressed}}
104+
struct Bad: ~NotAProtocol {} // expected-error {{type 'NotAProtocol' cannot be suppressed}}
105+
106+
struct X<T: ~Copyable>: ~Copyable { }
107+
108+
func typeInExpression() {
109+
_ = [~Copyable]() // expected-error{{type 'any Copyable' cannot be suppressed}}
110+
_ = X<any ~Copyable>()
111+
112+
_ = X<any ~Copyable & Foo>()
113+
_ = X<any Foo & ~Copyable>()
114+
115+
_ = X<(borrowing any ~Copyable) -> Void>()
116+
}

0 commit comments

Comments
 (0)