Skip to content

Commit 7dfc2a2

Browse files
committed
Parse: New syntax for primary associated types
1 parent 06dabe9 commit 7dfc2a2

File tree

8 files changed

+188
-24
lines changed

8 files changed

+188
-24
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,8 +1797,14 @@ ERROR(redundant_class_requirement,none,
17971797
ERROR(late_class_requirement,none,
17981798
"'class' must come first in the requirement list", ())
17991799
ERROR(where_inside_brackets,none,
1800-
"'where' clause next to generic parameters is obsolete, "
1801-
"must be written following the declaration's type", ())
1800+
"'where' clause next to generic parameters is obsolete, "
1801+
"must be written following the declaration's type", ())
1802+
1803+
1804+
ERROR(expected_rangle_primary_associated_type_list,PointsToFirstBadToken,
1805+
"expected '>' to complete primary associated type list", ())
1806+
ERROR(expected_primary_associated_type_name,PointsToFirstBadToken,
1807+
"expected an identifier to name primary associated type", ())
18021808

18031809
//------------------------------------------------------------------------------
18041810
// MARK: Conditional compilation parsing diagnostics

include/swift/Parse/Parser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,9 @@ class Parser {
11991199
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD);
12001200
BodyAndFingerprint
12011201
parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD);
1202+
1203+
ParserStatus parsePrimaryAssociatedTypes(
1204+
SmallVectorImpl<AssociatedTypeDecl *> &AssocTypes);
12021205
ParserResult<ProtocolDecl> parseDeclProtocol(ParseDeclOptions Flags,
12031206
DeclAttributes &Attributes);
12041207

lib/Parse/ParseDecl.cpp

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7963,14 +7963,125 @@ ParserResult<ClassDecl> Parser::parseDeclClass(ParseDeclOptions Flags,
79637963
return DCC.fixupParserResult(Status, CD);
79647964
}
79657965

7966+
ParserStatus Parser::parsePrimaryAssociatedTypes(
7967+
SmallVectorImpl<AssociatedTypeDecl *> &AssocTypes) {
7968+
SourceLoc LAngleLoc = consumeStartingLess();
7969+
7970+
ParserStatus Result;
7971+
SyntaxParsingContext PATContext(SyntaxContext,
7972+
SyntaxKind::PrimaryAssociatedTypeList);
7973+
7974+
bool HasNextParam = false;
7975+
do {
7976+
SyntaxParsingContext ATContext(SyntaxContext,
7977+
SyntaxKind::PrimaryAssociatedType);
7978+
7979+
// Note that we're parsing a declaration.
7980+
StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(),
7981+
StructureMarkerKind::Declaration);
7982+
7983+
// Parse attributes.
7984+
DeclAttributes Attrs;
7985+
if (Tok.hasComment())
7986+
Attrs.add(new (Context) RawDocCommentAttr(Tok.getCommentRange()));
7987+
parseDeclAttributeList(Attrs);
7988+
7989+
// Parse the name of the parameter.
7990+
Identifier Name;
7991+
SourceLoc NameLoc;
7992+
if (parseIdentifier(Name, NameLoc, /*diagnoseDollarPrefix=*/true,
7993+
diag::expected_primary_associated_type_name)) {
7994+
Result.setIsParseError();
7995+
break;
7996+
}
7997+
7998+
// Parse the ':' followed by a type.
7999+
SmallVector<InheritedEntry, 1> Inherited;
8000+
if (Tok.is(tok::colon)) {
8001+
(void)consumeToken();
8002+
ParserResult<TypeRepr> Ty;
8003+
8004+
if (Tok.isAny(tok::identifier, tok::code_complete, tok::kw_protocol,
8005+
tok::kw_Any)) {
8006+
Ty = parseType();
8007+
} else if (Tok.is(tok::kw_class)) {
8008+
diagnose(Tok, diag::unexpected_class_constraint);
8009+
diagnose(Tok, diag::suggest_anyobject)
8010+
.fixItReplace(Tok.getLoc(), "AnyObject");
8011+
consumeToken();
8012+
Result.setIsParseError();
8013+
} else {
8014+
diagnose(Tok, diag::expected_generics_type_restriction, Name);
8015+
Result.setIsParseError();
8016+
}
8017+
8018+
if (Ty.hasCodeCompletion())
8019+
return makeParserCodeCompletionStatus();
8020+
8021+
if (Ty.isNonNull())
8022+
Inherited.push_back({Ty.get()});
8023+
}
8024+
8025+
ParserResult<TypeRepr> UnderlyingTy;
8026+
if (Tok.is(tok::equal)) {
8027+
SyntaxParsingContext InitContext(SyntaxContext,
8028+
SyntaxKind::TypeInitializerClause);
8029+
consumeToken(tok::equal);
8030+
UnderlyingTy = parseType(diag::expected_type_in_associatedtype);
8031+
Result |= UnderlyingTy;
8032+
if (UnderlyingTy.isNull())
8033+
return Result;
8034+
}
8035+
8036+
auto *AssocType = new (Context)
8037+
AssociatedTypeDecl(CurDeclContext, NameLoc, Name, NameLoc,
8038+
UnderlyingTy.getPtrOrNull(),
8039+
/*trailingWhere=*/nullptr);
8040+
AssocType->getAttrs() = Attrs;
8041+
if (!Inherited.empty())
8042+
AssocType->setInherited(Context.AllocateCopy(Inherited));
8043+
AssocType->getAttrs().add(new (Context) PrimaryAssociatedTypeAttr(
8044+
/*Implicit=*/true));
8045+
8046+
AssocTypes.push_back(AssocType);
8047+
8048+
// Parse the comma, if the list continues.
8049+
HasNextParam = consumeIf(tok::comma);
8050+
} while (HasNextParam);
8051+
8052+
// Parse the closing '>'.
8053+
SourceLoc RAngleLoc;
8054+
if (startsWithGreater(Tok)) {
8055+
RAngleLoc = consumeStartingGreater();
8056+
} else {
8057+
diagnose(Tok, diag::expected_rangle_primary_associated_type_list);
8058+
diagnose(LAngleLoc, diag::opening_angle);
8059+
8060+
// Skip until we hit the '>'.
8061+
RAngleLoc = skipUntilGreaterInTypeList();
8062+
}
8063+
8064+
return Result;
8065+
}
8066+
79668067
/// Parse a 'protocol' declaration, doing no token skipping on error.
79678068
///
79688069
/// \verbatim
79698070
/// decl-protocol:
79708071
/// protocol-head '{' protocol-member* '}'
79718072
///
79728073
/// protocol-head:
7973-
/// 'protocol' attribute-list identifier inheritance?
8074+
/// attribute-list 'protocol' identifier primary-associated-type-list?
8075+
/// inheritance?
8076+
///
8077+
/// primary-associated-type-list:
8078+
/// '<' primary-associated-type+ '>'
8079+
///
8080+
/// primary-associated-type:
8081+
/// identifier inheritance? default-associated-type
8082+
///
8083+
/// default-associated-type:
8084+
/// '=' type
79748085
///
79758086
/// protocol-member:
79768087
/// decl-func
@@ -7991,12 +8102,20 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) {
79918102
if (Status.isErrorOrHasCompletion())
79928103
return Status;
79938104

7994-
// Protocols don't support generic parameters, but people often want them and
7995-
// we want to have good error recovery if they try them out. Parse them and
7996-
// produce a specific diagnostic if present.
7997-
if (startsWithLess(Tok)) {
7998-
diagnose(Tok, diag::generic_arguments_protocol);
7999-
maybeParseGenericParams();
8105+
SmallVector<AssociatedTypeDecl *, 2> PrimaryAssociatedTypes;
8106+
8107+
if (Context.LangOpts.EnableParameterizedProtocolTypes) {
8108+
if (startsWithLess(Tok)) {
8109+
Status |= parsePrimaryAssociatedTypes(PrimaryAssociatedTypes);
8110+
}
8111+
} else {
8112+
// Protocols don't support generic parameters, but people often want them and
8113+
// we want to have good error recovery if they try them out. Parse them and
8114+
// produce a specific diagnostic if present.
8115+
if (startsWithLess(Tok)) {
8116+
diagnose(Tok, diag::generic_arguments_protocol);
8117+
maybeParseGenericParams();
8118+
}
80008119
}
80018120

80028121
DebuggerContextChange DCC (*this);
@@ -8035,6 +8154,12 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) {
80358154

80368155
ContextChange CC(*this, Proto);
80378156

8157+
// Re-parent the primary associated type declarations into the protocol.
8158+
for (auto *AssocType : PrimaryAssociatedTypes) {
8159+
AssocType->setDeclContext(Proto);
8160+
Proto->addMember(AssocType);
8161+
}
8162+
80388163
// Parse the body.
80398164
{
80408165
SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock);

test/Constraints/opened_existentials.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@ func testMagic(pt: any P.Type) {
7272
}
7373

7474
// --- With primary associated types and opaque parameter types
75-
protocol CollectionOf: Collection {
76-
@_primaryAssociatedType associatedtype Element
77-
}
75+
protocol CollectionOf<Element>: Collection { }
7876

7977
extension Array: CollectionOf { }
8078
extension Set: CollectionOf { }

test/type/opaque_parameters.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ func testAnyDictionary(numberNames: [Int: String]) {
6868
}
6969

7070
// Combine with parameterized protocol types
71-
protocol PrimaryCollection: Collection {
72-
@_primaryAssociatedType associatedtype Element
73-
}
71+
protocol PrimaryCollection<Element>: Collection {}
7472

7573
extension Array: PrimaryCollection { }
7674
extension Set: PrimaryCollection { }

test/type/parameterized_protocol.swift

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,30 @@
33
// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=verify -enable-parameterized-protocol-types -requirement-machine-inferred-signatures=verify -disable-availability-checking 2>&1 | %FileCheck %s
44

55

6-
protocol Sequence {
7-
@_primaryAssociatedType associatedtype Element
6+
/// Test some invalid syntax first
7+
8+
protocol Invalid1<> {}
9+
// expected-error@-1 {{expected an identifier to name primary associated type}}
10+
11+
protocol Invalid2<A B> {}
12+
// expected-error@-1 {{expected '>' to complete primary associated type list}}
13+
// expected-note@-2 {{to match this opening '<'}}
14+
15+
protocol Invalid3<Element, +> {}
16+
// expected-error@-1 {{expected an identifier to name primary associated type}}
17+
// expected-error@-2 {{expected '>' to complete primary associated type list}}
18+
// expected-note@-3 {{to match this opening '<'}}
19+
20+
21+
/// Test semantics
22+
23+
protocol Sequence<Element> {
824
// expected-note@-1 {{protocol requires nested type 'Element'; do you want to add it?}}
925
}
1026

1127
struct ConcreteSequence<Element> : Sequence {}
1228

13-
protocol EquatableSequence {
14-
@_primaryAssociatedType associatedtype Element : Equatable
15-
}
29+
protocol EquatableSequence<Element : Equatable> {}
1630

1731
struct ConcreteEquatableSequence<Element : Equatable> : EquatableSequence {}
1832

@@ -86,10 +100,7 @@ protocol SequenceWrapperProtocol2 {
86100

87101

88102
/// Multiple primary associated types
89-
protocol Collection {
90-
@_primaryAssociatedType associatedtype Element
91-
@_primaryAssociatedType associatedtype Index
92-
}
103+
protocol Collection<Element, Index> {}
93104

94105
// CHECK-LABEL: .testCollection1@
95106
// CHECK-NEXT: Generic signature: <T where T : Collection, T.[Collection]Element == String>

utils/gyb_syntax_support/GenericNodes.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,27 @@
6464
is_optional=True),
6565
]),
6666

67+
Node('PrimaryAssociatedTypeList', kind='SyntaxCollection',
68+
element='PrimaryAssociatedType'),
69+
70+
# primary-associated-type -> type-name
71+
# | type-name (: type-identifier)? (= type)?
72+
Node('PrimaryAssociatedType', kind='Syntax',
73+
traits=['WithTrailingComma'],
74+
children=[
75+
Child('Attributes', kind='AttributeList',
76+
collection_element_name='Attribute', is_optional=True),
77+
Child('Name', kind='IdentifierToken'),
78+
Child('Colon', kind='ColonToken',
79+
is_optional=True),
80+
Child('InheritedType', kind='Type',
81+
is_optional=True),
82+
Child('Initializer', kind='TypeInitializerClause',
83+
is_optional=True),
84+
Child('TrailingComma', kind='CommaToken',
85+
is_optional=True),
86+
]),
87+
6788
# generic-parameter-clause -> '<' generic-parameter-list '>'
6889
Node('GenericParameterClause', kind='Syntax',
6990
children=[

utils/gyb_syntax_support/NodeSerializationCodes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@
255255
'UnavailabilityCondition': 251,
256256
'AvailabilityEntry' : 252,
257257
'RegexLiteralExpr': 253,
258+
'PrimaryAssociatedTypeList' : 254,
259+
'PrimaryAssociatedType' : 255,
258260
}
259261

260262

0 commit comments

Comments
 (0)