Skip to content

Commit c83bc48

Browse files
authored
Merge pull request #72775 from DougGregor/parse-suppressed-constraints-and-ownership-params-6.0
[6.0] Fix parsing issues related to suppressed conformances / noncopyable generics
2 parents 986e871 + 15d18c5 commit c83bc48

File tree

9 files changed

+148
-16
lines changed

9 files changed

+148
-16
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5696,6 +5696,8 @@ ERROR(incorrect_optional_any,none,
56965696
ERROR(existential_requires_any,none,
56975697
"use of %select{protocol |}2%0 as a type must be written %1",
56985698
(Type, Type, bool))
5699+
ERROR(inverse_requires_any,none,
5700+
"constraint that suppresses conformance requires 'any'", ())
56995701

57005702
ERROR(nonisolated_let,none,
57015703
"'nonisolated' is meaningless on 'let' declarations because "

include/swift/Parse/Parser.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,12 +1239,19 @@ class Parser {
12391239
// the following token is something that can introduce a type. Thankfully
12401240
// none of these tokens overlap with the set of tokens that can follow an
12411241
// identifier in a type production.
1242-
return (Tok.is(tok::identifier) &&
1243-
peekToken().isAny(tok::at_sign, tok::kw_inout, tok::l_paren,
1244-
tok::identifier, tok::l_square, tok::kw_Any,
1245-
tok::kw_Self, tok::kw__, tok::kw_var,
1246-
tok::kw_let)) ||
1247-
isLifetimeDependenceToken();
1242+
if (Tok.is(tok::identifier)) {
1243+
auto next = peekToken();
1244+
if (next.isAny(tok::at_sign, tok::kw_inout, tok::l_paren,
1245+
tok::identifier, tok::l_square, tok::kw_Any,
1246+
tok::kw_Self, tok::kw__, tok::kw_var,
1247+
tok::kw_let))
1248+
return true;
1249+
1250+
if (next.is(tok::oper_prefix) && next.getText() == "~")
1251+
return true;
1252+
}
1253+
1254+
return isLifetimeDependenceToken();
12481255
}
12491256

12501257
struct ParsedTypeAttributeList {

lib/Parse/ParseExpr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,8 @@ ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
479479

480480
// 'any' followed by another identifier is an existential type.
481481
if (Tok.isContextualKeyword("any") &&
482-
peekToken().is(tok::identifier) &&
482+
(peekToken().is(tok::identifier) ||
483+
peekToken().isContextualPunctuator("~")) &&
483484
!peekToken().isAtStartOfLine()) {
484485
ParserResult<TypeRepr> ty = parseType();
485486
auto *typeExpr = new (Context) TypeExpr(ty.get());

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

lib/Sema/TypeCheckType.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ static unsigned getGenericRequirementKind(TypeResolutionOptions options) {
170170
case TypeResolverContext::ImmediateOptionalTypeArgument:
171171
case TypeResolverContext::AbstractFunctionDecl:
172172
case TypeResolverContext::CustomAttr:
173+
case TypeResolverContext::Inverted:
173174
break;
174175
}
175176

@@ -5150,6 +5151,7 @@ NeverNullType TypeResolver::resolveImplicitlyUnwrappedOptionalType(
51505151
case TypeResolverContext::GenericParameterInherited:
51515152
case TypeResolverContext::AssociatedTypeInherited:
51525153
case TypeResolverContext::CustomAttr:
5154+
case TypeResolverContext::Inverted:
51535155
doDiag = true;
51545156
break;
51555157
}
@@ -5740,10 +5742,30 @@ NeverNullType TypeResolver::buildMetatypeType(
57405742

57415743
NeverNullType TypeResolver::resolveInverseType(InverseTypeRepr *repr,
57425744
TypeResolutionOptions options) {
5743-
auto ty = resolveType(repr->getConstraint(), options);
5745+
auto subOptions = options.withoutContext(true)
5746+
.withContext(TypeResolverContext::Inverted);
5747+
auto ty = resolveType(repr->getConstraint(), subOptions);
57445748
if (ty->hasError())
57455749
return ErrorType::get(getASTContext());
57465750

5751+
// If the inverted type is an existential metatype, unwrap the existential
5752+
// metatype so we can look at the instance type. We'll re-wrap at the end.
5753+
ExistentialMetatypeType *existentialTy =
5754+
dyn_cast<ExistentialMetatypeType>(ty.get().getPointer());
5755+
if (existentialTy) {
5756+
ty = existentialTy->getInstanceType();
5757+
}
5758+
5759+
auto wrapInExistential = [existentialTy](Type type) -> Type {
5760+
if (!existentialTy)
5761+
return type;
5762+
5763+
std::optional<MetatypeRepresentation> repr;
5764+
if (existentialTy->hasRepresentation())
5765+
repr = existentialTy->getRepresentation();
5766+
return ExistentialMetatypeType::get(type, repr);
5767+
};
5768+
57475769
if (auto kp = ty->getKnownProtocol()) {
57485770
if (auto kind = getInvertibleProtocolKind(*kp)) {
57495771

@@ -5752,13 +5774,16 @@ NeverNullType TypeResolver::resolveInverseType(InverseTypeRepr *repr,
57525774
!getASTContext().LangOpts.hasFeature(Feature::NonescapableTypes)) {
57535775
diagnoseInvalid(repr, repr->getLoc(),
57545776
diag::escapable_requires_feature_flag);
5755-
return ErrorType::get(getASTContext());
5777+
return wrapInExistential(ErrorType::get(getASTContext()));
57565778
}
57575779

5758-
return ProtocolCompositionType::getInverseOf(getASTContext(), *kind);
5780+
return wrapInExistential(
5781+
ProtocolCompositionType::getInverseOf(getASTContext(), *kind));
57595782
}
57605783
}
57615784

5785+
// Rewrap for diagnostic purposes.
5786+
ty = wrapInExistential(ty);
57625787
diagnoseInvalid(repr, repr->getLoc(), diag::inverse_type_not_invertible, ty);
57635788
return ErrorType::get(getASTContext());
57645789
}
@@ -5917,6 +5942,23 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
59175942
if (isa<ExistentialTypeRepr>(T))
59185943
return Action::SkipNode();
59195944

5945+
// Suppressed conformance needs to be within any/some.
5946+
if (auto inverse = dyn_cast<InverseTypeRepr>(T)) {
5947+
// Find an enclosing protocol composition, if there is one, so we
5948+
// can insert 'any' before that.
5949+
SourceLoc anyLoc = inverse->getTildeLoc();
5950+
if (!reprStack.empty()) {
5951+
if (isa<CompositionTypeRepr>(reprStack.back())) {
5952+
anyLoc = reprStack.back()->getStartLoc();
5953+
}
5954+
}
5955+
5956+
Ctx.Diags.diagnose(inverse->getTildeLoc(), diag::inverse_requires_any)
5957+
.highlight(inverse->getConstraint()->getSourceRange())
5958+
.fixItInsert(anyLoc, "any ");
5959+
return Action::SkipNode();
5960+
}
5961+
59205962
reprStack.push_back(T);
59215963

59225964
auto *declRefTR = dyn_cast<DeclRefTypeRepr>(T);

lib/Sema/TypeCheckType.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ enum class TypeResolverContext : uint8_t {
202202

203203
/// Whether this is a custom attribute.
204204
CustomAttr,
205+
206+
/// Whether this is the argument of an inverted constraint (~).
207+
Inverted,
205208
};
206209

207210
/// Options that determine how type resolution should work.
@@ -298,6 +301,7 @@ class TypeResolutionOptions {
298301
case Context::GenericParameterInherited:
299302
case Context::AssociatedTypeInherited:
300303
case Context::CustomAttr:
304+
case Context::Inverted:
301305
return false;
302306
}
303307
llvm_unreachable("unhandled kind");
@@ -316,6 +320,7 @@ class TypeResolutionOptions {
316320
case Context::GenericRequirement:
317321
case Context::ExistentialConstraint:
318322
case Context::MetatypeBase:
323+
case Context::Inverted:
319324
return false;
320325
case Context::None:
321326
case Context::ScalarGenericArgument:
@@ -353,6 +358,7 @@ class TypeResolutionOptions {
353358
case Context::PackElement:
354359
case Context::TupleElement:
355360
case Context::VariadicGenericArgument:
361+
case Context::Inverted:
356362
return true;
357363
case Context::None:
358364
case Context::PatternBindingDecl:
@@ -425,6 +431,7 @@ class TypeResolutionOptions {
425431
case Context::ImmediateOptionalTypeArgument:
426432
case Context::AbstractFunctionDecl:
427433
case Context::CustomAttr:
434+
case Context::Inverted:
428435
return false;
429436
}
430437
}

test/IRGen/existential_shape_metadata_noncopyable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ public protocol QNC<A>: ~Copyable {
1111
public struct NCStruct: ~Copyable { }
1212

1313

14-
public func testNoncopyableConcrete() -> (Any & ~Copyable).Type {
14+
public func testNoncopyableConcrete() -> any ~Copyable.Type {
1515
return (any QNC<NCStruct>).self
1616
}

test/Parse/inverses.swift

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,18 @@ func ownership2(_ t: ~ borrowing Int) {} // expected-error {{cannot find type 'b
8181

8282
func ownership3(_ t: consuming some ~Clone) {}
8383

84-
func what(one: ~Copyable..., // expected-error {{type 'any Copyable' cannot be suppressed}}
84+
func what(one: ~Copyable..., // expected-error {{noncopyable type '~Copyable' cannot be used within a variadic type yet}}
8585
two: ~(Copyable...) // expected-error {{variadic parameter cannot appear outside of a function parameter list}}
86-
// expected-error@-1 {{type 'any Copyable' cannot be suppressed}}
86+
// expected-error@-1 {{parameter of noncopyable type '~Copyable' must specify ownership}}
87+
// expected-note@-2{{add 'borrowing' for an immutable reference}}
88+
// expected-note@-3{{add 'inout' for a mutable reference}}
89+
// expected-note@-4{{add 'consuming' to take the value from the caller}}
8790
) {}
8891

8992
struct A { struct B { struct C {} } }
9093

91-
typealias Z1 = (~Copyable).Type // FIXME: should be an error
92-
typealias Z1 = ~Copyable.Type // expected-error {{type 'any Copyable.Type' cannot be suppressed}}
94+
typealias Z0 = (~Copyable).Type // expected-error{{constraint that suppresses conformance requires 'any'}}{{17-17=any }}
95+
typealias Z1 = ~Copyable.Type // expected-error{{constraint that suppresses conformance requires 'any'}}{{16-16=any }}
9396
typealias Z2 = ~A.B.C // expected-error {{type 'A.B.C' cannot be suppressed}}
9497
typealias Z3 = ~A? // expected-error {{type 'A?' cannot be suppressed}}
9598
typealias Z4 = ~Rope<Int> // expected-error {{type 'Rope<Int>' cannot be suppressed}}
@@ -101,4 +104,27 @@ typealias Z8 = ~Copyable & Hashable // expected-error {{composition cannot conta
101104

102105
struct NotAProtocol {}
103106

104-
struct Bad: ~NotAProtocol {} // expected-error {{type 'NotAProtocol' cannot be suppressed}}
107+
struct Bad: ~NotAProtocol {} // expected-error {{type 'NotAProtocol' cannot be suppressed}}
108+
109+
struct X<T: ~Copyable>: ~Copyable { }
110+
111+
func typeInExpression() {
112+
_ = [~Copyable]() // expected-error{{type '~Copyable' does not conform to protocol 'Copyable'}}
113+
_ = X<any ~Copyable>()
114+
115+
_ = X<any ~Copyable & Foo>()
116+
_ = X<any Foo & ~Copyable>()
117+
118+
_ = X<(borrowing any ~Copyable) -> Void>()
119+
120+
_ = ~Copyable.self // expected-error{{unary operator '~' cannot be applied to an operand of type '(any Copyable).Type'}}
121+
_ = (~Copyable).self // expected-error{{constraint that suppresses conformance requires 'any'}}{{8-8=any }}
122+
_ = (any ~Copyable).self
123+
}
124+
125+
func param1(_ t: borrowing ~Copyable) {} // expected-error{{constraint that suppresses conformance requires 'any'}}{{28-28=any }}
126+
func param2(_ t: ~Copyable.Type) {} // expected-error{{constraint that suppresses conformance requires 'any'}}{{18-18=any }}
127+
func param3(_ t: borrowing any ~Copyable) {}
128+
func param4(_ t: any ~Copyable.Type) {}
129+
130+
func param3(_ t: borrowing ExtraNoncopyProto & ~Copyable) {} // expected-error{{constraint that suppresses conformance requires 'any'}}{{28-28=any }}

0 commit comments

Comments
 (0)