Skip to content

[Parser] Add a contextual each keyword for pack references. #62509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5150,6 +5150,12 @@ ERROR(expansion_not_allowed,none,
"function result, tuple element or generic argument list", (Type))
ERROR(expansion_not_variadic,none,
"variadic expansion %0 must contain at least one variadic generic parameter", (Type))
ERROR(pack_reference_outside_expansion,none,
"pack reference %0 can only appear in pack expansion or generic requirement",
(Type))
ERROR(each_non_pack,none,
"'each' cannot be applied to non-pack type %0",
(Type))
ERROR(tuple_duplicate_label,none,
"cannot create a tuple with a duplicate element label", ())
ERROR(multiple_ellipsis_in_tuple,none,
Expand Down
36 changes: 36 additions & 0 deletions include/swift/AST/TypeRepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,41 @@ class PackExpansionTypeRepr final : public TypeRepr {
friend class TypeRepr;
};

/// A pack reference spelled with the \c each keyword.
///
/// Pack references can only appear inside pack expansions and in
/// generic requirements.
///
/// \code
/// struct Generic<T...> {
/// func f(value: (each T)...) where each T: P {}
/// }
/// \endcode
class PackReferenceTypeRepr: public TypeRepr {
TypeRepr *PackType;
SourceLoc EachLoc;

public:
PackReferenceTypeRepr(SourceLoc eachLoc, TypeRepr *packType)
: TypeRepr(TypeReprKind::PackReference), PackType(packType),
EachLoc(eachLoc) {}

TypeRepr *getPackType() const { return PackType; }
SourceLoc getEachLoc() const { return EachLoc; }

static bool classof(const TypeRepr *T) {
return T->getKind() == TypeReprKind::PackReference;
}
static bool classof(const PackReferenceTypeRepr *T) { return true; }

private:
SourceLoc getStartLocImpl() const { return EachLoc; }
SourceLoc getEndLocImpl() const { return PackType->getEndLoc(); }
SourceLoc getLocImpl() const { return EachLoc; }
void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const;
friend class TypeRepr;
};

/// A tuple type.
/// \code
/// (Foo, Bar)
Expand Down Expand Up @@ -1336,6 +1371,7 @@ inline bool TypeRepr::isSimple() const {
case TypeReprKind::OpaqueReturn:
case TypeReprKind::NamedOpaqueReturn:
case TypeReprKind::Existential:
case TypeReprKind::PackReference:
return false;
case TypeReprKind::SimpleIdent:
case TypeReprKind::GenericIdent:
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeReprNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ TYPEREPR(Protocol, TypeRepr)
TYPEREPR(OpaqueReturn, TypeRepr)
TYPEREPR(NamedOpaqueReturn, TypeRepr)
TYPEREPR(Existential, TypeRepr)
TYPEREPR(PackReference, TypeRepr)
TYPEREPR(Placeholder, TypeRepr)
ABSTRACT_TYPEREPR(Specifier, TypeRepr)
SPECIFIER_TYPEREPR(InOut, SpecifierTypeRepr)
Expand Down
6 changes: 6 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3141,6 +3141,12 @@ class PrintTypeRepr : public TypeReprVisitor<PrintTypeRepr> {
PrintWithColorRAII(OS, ParenthesisColor) << ')';
}

void visitPackReferenceTypeRepr(PackReferenceTypeRepr *T) {
printCommon("pack_reference");
printRec(T->getPackType());
PrintWithColorRAII(OS, ParenthesisColor) << ')';
}

void visitTupleTypeRepr(TupleTypeRepr *T) {
printCommon("type_tuple");

Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1995,6 +1995,10 @@ bool Traversal::visitPackExpansionTypeRepr(PackExpansionTypeRepr *T) {
return doIt(T->getPatternType());
}

bool Traversal::visitPackReferenceTypeRepr(PackReferenceTypeRepr *T) {
return doIt(T->getPackType());
}

bool Traversal::visitTupleTypeRepr(TupleTypeRepr *T) {
for (auto &elem : T->getElements()) {
if (doIt(elem.Type))
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2516,6 +2516,13 @@ directReferencesForTypeRepr(Evaluator &evaluator,
allowUsableFromInline);
}

case TypeReprKind::PackReference: {
auto packReferenceRepr = cast<PackReferenceTypeRepr>(typeRepr);
return directReferencesForTypeRepr(evaluator, ctx,
packReferenceRepr->getPackType(), dc,
allowUsableFromInline);
}

case TypeReprKind::Error:
case TypeReprKind::Function:
case TypeReprKind::InOut:
Expand Down
6 changes: 6 additions & 0 deletions lib/AST/TypeRepr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,12 @@ void PackExpansionTypeRepr::printImpl(ASTPrinter &Printer,
Printer << "...";
}

void PackReferenceTypeRepr::printImpl(ASTPrinter &Printer,
const PrintOptions &Opts) const {
Printer.printKeyword("each", Opts, /*Suffix=*/" ");
printTypeRepr(PackType, Printer, Opts);
}

void TupleTypeRepr::printImpl(ASTPrinter &Printer,
const PrintOptions &Opts) const {
Printer.callPrintStructurePre(PrintStructureKind::TupleType);
Expand Down
3 changes: 2 additions & 1 deletion lib/Parse/ParsePattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ bool Parser::startsParameterName(bool isClosure) {
// If the first name wasn't "isolated", we're done.
if (!Tok.isContextualKeyword("isolated") &&
!Tok.isContextualKeyword("some") &&
!Tok.isContextualKeyword("any"))
!Tok.isContextualKeyword("any") &&
!Tok.isContextualKeyword("each"))
return true;

// "isolated" can be an argument label, but it's also a contextual keyword,
Expand Down
16 changes: 14 additions & 2 deletions lib/Parse/ParseType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -780,8 +780,8 @@ Parser::parseTypeIdentifier(bool isParsingQualifiedDeclBaseType) {
/// type-composition '&' type-simple
ParserResult<TypeRepr>
Parser::parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason) {
// Check for the opaque modifier.
// This is only semantically allowed in certain contexts, but we parse it
// Check for the contextual keyword modifiers on types.
// These are only semantically allowed in certain contexts, but we parse it
// generally for diagnostics and recovery.
SourceLoc opaqueLoc;
SourceLoc anyLoc;
Expand All @@ -793,6 +793,16 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason) {
// Treat any as a keyword.
TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword);
anyLoc = consumeToken();
} else if (Tok.isContextualKeyword("each")) {
// Treat 'each' as a keyword.
TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword);
SourceLoc eachLoc = consumeToken();
ParserResult<TypeRepr> packRef = parseTypeSimple(MessageID, reason);
if (packRef.isNull())
return packRef;

auto *typeRepr = new (Context) PackReferenceTypeRepr(eachLoc, packRef.get());
return makeParserResult(ParserStatus(packRef), typeRepr);
}

auto applyOpaque = [&](TypeRepr *type) -> TypeRepr * {
Expand Down Expand Up @@ -1393,6 +1403,8 @@ bool Parser::canParseType() {
consumeToken();
} else if (Tok.isContextualKeyword("any")) {
consumeToken();
} else if (Tok.isContextualKeyword("each")) {
consumeToken();
}

switch (Tok.getKind()) {
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2261,6 +2261,7 @@ static Type validateParameterType(ParamDecl *decl) {
// @escaping in this case.
options.setContext(TypeResolverContext::VariadicFunctionInput);
options |= TypeResolutionFlags::Direct;
options |= TypeResolutionFlags::AllowPackReferences;

// FIXME: This duplicates code found elsewhere
auto *patternRepr = packExpansionRepr->getPatternType();
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,7 @@ RequirementRequest::evaluate(Evaluator &evaluator,
context = TypeResolverContext::GenericRequirement;
}
auto options = TypeResolutionOptions(context);
options |= TypeResolutionFlags::AllowPackReferences;
if (owner.dc->isInSpecializeExtensionContext())
options |= TypeResolutionFlags::AllowUsableFromInline;
Optional<TypeResolution> resolution;
Expand Down
32 changes: 32 additions & 0 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2088,6 +2088,8 @@ namespace {
TypeResolutionOptions options);
NeverNullType resolvePackExpansionType(PackExpansionTypeRepr *repr,
TypeResolutionOptions options);
NeverNullType resolvePackReference(PackReferenceTypeRepr *repr,
TypeResolutionOptions options);
NeverNullType resolveTupleType(TupleTypeRepr *repr,
TypeResolutionOptions options);
NeverNullType resolveCompositionType(CompositionTypeRepr *repr,
Expand Down Expand Up @@ -2277,6 +2279,9 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
case TypeReprKind::PackExpansion:
return resolvePackExpansionType(cast<PackExpansionTypeRepr>(repr), options);

case TypeReprKind::PackReference:
return resolvePackReference(cast<PackReferenceTypeRepr>(repr), options);

case TypeReprKind::Tuple:
return resolveTupleType(cast<TupleTypeRepr>(repr), options);

Expand Down Expand Up @@ -4182,6 +4187,7 @@ std::pair<Type, Type>
TypeResolver::maybeResolvePackExpansionType(PackExpansionTypeRepr *repr,
TypeResolutionOptions options) {
auto elementOptions = options;
elementOptions |= TypeResolutionFlags::AllowPackReferences;
auto patternTy = resolveType(repr->getPatternType(), elementOptions);
if (patternTy->hasError())
return std::make_pair(ErrorType::get(getASTContext()), Type());
Expand Down Expand Up @@ -4243,6 +4249,31 @@ NeverNullType TypeResolver::resolvePackExpansionType(PackExpansionTypeRepr *repr
return PackExpansionType::get(pair.first, pair.second);
}

NeverNullType TypeResolver::resolvePackReference(PackReferenceTypeRepr *repr,
TypeResolutionOptions options) {
auto &ctx = getASTContext();
auto packReference = resolveType(repr->getPackType(), options);

// If we already failed, don't diagnose again.
if (packReference->hasError())
return ErrorType::get(ctx);

if (!packReference->isParameterPack()) {
ctx.Diags.diagnose(repr->getLoc(), diag::each_non_pack,
packReference);
return packReference;
}

if (!options.contains(TypeResolutionFlags::AllowPackReferences)) {
ctx.Diags.diagnose(repr->getLoc(),
diag::pack_reference_outside_expansion,
packReference);
return ErrorType::get(ctx);
}

return packReference;
}

NeverNullType TypeResolver::resolveTupleType(TupleTypeRepr *repr,
TypeResolutionOptions options) {
auto &ctx = getASTContext();
Expand Down Expand Up @@ -4739,6 +4770,7 @@ class ExistentialTypeVisitor
case TypeReprKind::Placeholder:
case TypeReprKind::CompileTimeConst:
case TypeReprKind::PackExpansion:
case TypeReprKind::PackReference:
return false;
}
}
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/TypeCheckType.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ enum class TypeResolutionFlags : uint16_t {

/// We are in a `@preconcurrency` declaration.
Preconcurrency = 1 << 10,

/// Whether references to type parameter packs are allowed.
///
/// Pack references are only allowed inside pack expansions
/// and in generic requirements.
AllowPackReferences = 1 << 11,
};

/// Type resolution contexts that require special handling.
Expand Down
12 changes: 11 additions & 1 deletion test/type/pack_expansion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,14 @@ struct Outer<T...> {
struct AlsoGood<U...> {
typealias Value = ((T, E<U... >)...)
}
}
}

func packRef<T...>(_: (each T)...) where each T: P {}

func packMemberRef<T...>(_: (each T.T)...) where each T: P {}

// expected-error@+1 {{'each' cannot be applied to non-pack type 'Int'}}
func invalidPackRef(_: each Int) {}

// expected-error@+1 {{pack reference 'T' can only appear in pack expansion or generic requirement}}
func packRefOutsideExpansion<T...>(_: each T) {}