Skip to content

Commit 859d325

Browse files
authored
Merge pull request #72748 from DougGregor/suppressed-existentials-parsing
2 parents 0e2234f + caf78cd commit 859d325

File tree

7 files changed

+89
-16
lines changed

7 files changed

+89
-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/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: 19 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}}
@@ -106,11 +109,22 @@ struct Bad: ~NotAProtocol {} // expected-error {{type 'NotAProtocol' cannot be s
106109
struct X<T: ~Copyable>: ~Copyable { }
107110

108111
func typeInExpression() {
109-
_ = [~Copyable]() // expected-error{{type 'any Copyable' cannot be suppressed}}
112+
_ = [~Copyable]() // expected-error{{type '~Copyable' does not conform to protocol 'Copyable'}}
110113
_ = X<any ~Copyable>()
111114

112115
_ = X<any ~Copyable & Foo>()
113116
_ = X<any Foo & ~Copyable>()
114117

115118
_ = 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
116123
}
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)