Skip to content

Commit 2b0def5

Browse files
committed
Replace @hasAsyncAlt with @completionHandlerAsync
This patch replaces the @hasAsyncAlternative attribute with @completionHandlerAsync. The @completionHandlerAsync attribute takes the function decl name of the async function and optionally the index of the completion hander parameter in the function that it's attached to. If the completion handler index is not provided, it's assumed to be the last parameter in the parameter list. We resolve the async function while typechecking the attribute. Before resolving, we verify that the function the attribute is attached to isn't async, that it has enough parameters to at least have the indicated completion handler referenced by the index, that the completion handler is an escaping non-auto-closure function that returns Void. The async function declaration resolution isn't perfect yet, but I want to get this patch up and we can refine it later. It pulls all of the delcs with the specified declname in the same context as the function that the attribute is attached to. Going through that list, it keeps any that are async functions. If there are none, we emit an error saying that there are no viable functions, if there are multiple we emit an error saying that the decl name is ambiguous, and if there is one function, we keep that as the resolve async function declaration. This does not take into account the data types of the completion handler or the async function. There are some complexities to making this mapping. Here are the pieces: - If the completion handler takes a single data type, the async function should return that type. (easy case) - If the completion handler takes a `Result<T, Error>`, the async function is a throwing function that returns a T. (Medium difficulty) - If the completion handler looks like `(T?, Error?) -> Void`, we have an ambiguous situation between the following async functions: - func foo() async throws -> T - func foo() async throws -> T? That is, we cannot tell whether the `T?` in the completion handler is optional because it will be nil on an error, or if it is intended to be optional. This can be done later if it becomes a problem.
1 parent 19dd906 commit 2b0def5

File tree

11 files changed

+315
-63
lines changed

11 files changed

+315
-63
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ SIMPLE_DECL_ATTR(reasync, AtReasync,
624624
ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,
625625
110)
626626

627-
DECL_ATTR(hasAsyncAlternative, HasAsyncAlternative,
627+
DECL_ATTR(completionHandlerAsync, CompletionHandlerAsync,
628628
OnAbstractFunction | ConcurrencyOnly |
629629
ABIStableToAdd | ABIStableToRemove |
630630
APIStableToAdd | APIStableToRemove,

include/swift/AST/Attr.h

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,27 +2147,44 @@ class TransposeAttr final
21472147
}
21482148
};
21492149

2150-
/// The `@hasAsyncAlternative` attribute marks a function as having an async
2150+
/// The `@completionHandlerAsync` attribute marks a function as having an async
21512151
/// alternative, optionally providing a name (for cases when the alternative
21522152
/// has a different name).
2153-
class HasAsyncAlternativeAttr final : public DeclAttribute {
2153+
class CompletionHandlerAsyncAttr final : public DeclAttribute {
21542154
public:
2155-
/// An optional name of the async alternative function, where the name of the
2156-
/// attributed function is used otherwise.
2157-
const DeclNameRef Name;
2155+
/// Source location of the async function name in the attribute
2156+
const SourceLoc AsyncFunctionNameLoc;
21582157

2159-
HasAsyncAlternativeAttr(DeclNameRef Name, SourceLoc AtLoc, SourceRange Range)
2160-
: DeclAttribute(DAK_HasAsyncAlternative, AtLoc, Range, false),
2161-
Name(Name) {}
2158+
/// DeclName of the async function in the attribute
2159+
const DeclNameRef AsyncFunctionName;
21622160

2163-
HasAsyncAlternativeAttr(SourceLoc AtLoc, SourceRange Range)
2164-
: DeclAttribute(DAK_HasAsyncAlternative, AtLoc, Range, false) {}
2161+
/// Source location of the completion handler index passed to the index
2162+
const SourceLoc CompletionHandlerIndexLoc;
21652163

2166-
/// Determine whether this attribute has a name associated with it.
2167-
bool hasName() const { return !Name.getBaseName().empty(); }
2164+
/// The index of the completion handler
2165+
const size_t CompletionHandlerIndex;
2166+
2167+
/// True when the completion handler was specified explicitly
2168+
const bool ExplicitCompletionHandlerIndex;
2169+
2170+
AbstractFunctionDecl *AsyncFunctionDecl = nullptr;
2171+
2172+
CompletionHandlerAsyncAttr(DeclNameRef asyncFunctionName,
2173+
SourceLoc asyncFunctionNameLoc,
2174+
bool explicitCompletionHandlerIndex,
2175+
size_t completionHandlerIndex,
2176+
SourceLoc completionHandlerIndexLoc,
2177+
SourceLoc atLoc, SourceRange range)
2178+
: DeclAttribute(DAK_CompletionHandlerAsync, atLoc, range,
2179+
/*implicit*/ false),
2180+
AsyncFunctionNameLoc(asyncFunctionNameLoc),
2181+
AsyncFunctionName(asyncFunctionName),
2182+
CompletionHandlerIndexLoc(completionHandlerIndexLoc),
2183+
CompletionHandlerIndex(completionHandlerIndex),
2184+
ExplicitCompletionHandlerIndex(explicitCompletionHandlerIndex) {}
21682185

21692186
static bool classof(const DeclAttribute *DA) {
2170-
return DA->getKind() == DAK_HasAsyncAlternative;
2187+
return DA->getKind() == DAK_CompletionHandlerAsync;
21712188
}
21722189
};
21732190

include/swift/AST/DiagnosticsParse.def

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,9 @@ ERROR(attr_expected_comma,none,
13761376
ERROR(attr_expected_string_literal,none,
13771377
"expected string literal in '%0' attribute", (StringRef))
13781378

1379+
ERROR(attr_expected_integer_literal,none,
1380+
"expected integer literal in '%0' attribute", (StringRef))
1381+
13791382
ERROR(attr_expected_option_such_as,none,
13801383
"expected '%0' option such as '%1'", (StringRef, StringRef))
13811384

@@ -1675,8 +1678,8 @@ ERROR(sil_inst_autodiff_invalid_witness_generic_signature,PointsToFirstBadToken,
16751678
"parameters as original function generic signature '%1'",
16761679
(StringRef, StringRef))
16771680

1678-
// hasAsyncAlternative
1679-
ERROR(has_async_alternative_invalid_name, none,
1681+
// completionHandlerAsync
1682+
ERROR(attr_completion_handler_async_invalid_name, none,
16801683
"argument of '%0' attribute must be an identifier or full function name",
16811684
(StringRef))
16821685

include/swift/AST/DiagnosticsSema.def

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3340,6 +3340,33 @@ ERROR(diff_params_clause_param_not_differentiable,none,
33403340
"can only differentiate with respect to parameters that conform to "
33413341
"'Differentiable', but %0 does not conform to 'Differentiable'", (Type))
33423342

3343+
// completionHanderAsync attribute
3344+
ERROR(attr_completion_handler_async_handler_not_func,none,
3345+
"'%0' should be attached to a non-async completion-handler function",
3346+
(DeclAttribute))
3347+
3348+
NOTE(note_attr_function_declared_async,none,
3349+
"function declared async", ())
3350+
3351+
NOTE(note_attr_completion_function_must_return_void,none,
3352+
"completion handler must return 'Void'", ())
3353+
3354+
NOTE(note_attr_completion_handler_async_type_is_not_function,none,
3355+
"%0 is not a function type", (Type))
3356+
3357+
NOTE(note_attr_completion_handler_async_handler_attr_req,none,
3358+
"completion handler must%select{ not|}0 be '@%1'",
3359+
(bool, StringRef))
3360+
3361+
ERROR(attr_completion_handler_async_handler_out_of_range,none,
3362+
"completion handler index out of range of the function parameters", ())
3363+
3364+
ERROR(attr_completion_handler_async_ambiguous_function,none,
3365+
"ambiguous '%0' async function %1", (DeclAttribute, DeclNameRef))
3366+
3367+
ERROR(attr_completion_handler_async_no_suitable_function,none,
3368+
"no corresponding async function named %0", (DeclNameRef))
3369+
33433370
//------------------------------------------------------------------------------
33443371
// MARK: Type Check Expressions
33453372
//------------------------------------------------------------------------------

lib/AST/Attr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,8 +1225,8 @@ StringRef DeclAttribute::getAttrName() const {
12251225
return "derivative";
12261226
case DAK_Transpose:
12271227
return "transpose";
1228-
case DAK_HasAsyncAlternative:
1229-
return "hasAsyncAlternative";
1228+
case DAK_CompletionHandlerAsync:
1229+
return "completionHandlerAsync";
12301230
}
12311231
llvm_unreachable("bad DeclAttrKind");
12321232
}

lib/Parse/ParseDecl.cpp

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,42 +1570,68 @@ void Parser::parseAllAvailabilityMacroArguments() {
15701570
AvailabilityMacrosComputed = true;
15711571
}
15721572

1573-
static HasAsyncAlternativeAttr *parseAsyncAlternativeAttribute(
1574-
Parser &P, StringRef AttrName, SourceLoc AtLoc, DeclAttrKind DK) {
1573+
static CompletionHandlerAsyncAttr *
1574+
parseCompletionHandlerAsyncAttribute(Parser &P, StringRef AttrName,
1575+
SourceLoc AtLoc, DeclAttrKind DK) {
15751576
SourceLoc Loc = P.PreviousLoc;
15761577

1577-
// Unnamed @hasAsyncAlternative attribute
1578-
if (P.Tok.isNot(tok::l_paren))
1579-
return new (P.Context) HasAsyncAlternativeAttr(AtLoc, Loc);
1580-
1581-
P.consumeToken(tok::l_paren);
1578+
if (!P.consumeIf(tok::l_paren)) {
1579+
P.diagnose(P.getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName,
1580+
DeclAttribute::isDeclModifier(DK));
1581+
return nullptr;
1582+
}
15821583

15831584
if (!P.Tok.is(tok::string_literal)) {
15841585
P.diagnose(Loc, diag::attr_expected_string_literal, AttrName);
15851586
return nullptr;
15861587
}
15871588

1588-
auto Value = P.getStringLiteralIfNotInterpolated(
1589-
Loc, ("argument of '" + AttrName + "'").str());
1589+
SourceLoc nameLoc = P.Tok.getLoc();
1590+
Optional<StringRef> asyncFunctionName = P.getStringLiteralIfNotInterpolated(
1591+
nameLoc, ("argument of '" + AttrName + "'").str());
15901592
P.consumeToken(tok::string_literal);
1591-
if (!Value)
1593+
1594+
if (!asyncFunctionName)
15921595
return nullptr;
15931596

1594-
ParsedDeclName parsedName = parseDeclName(Value.getValue());
1595-
if (!parsedName || !parsedName.ContextName.empty()) {
1596-
P.diagnose(AtLoc, diag::has_async_alternative_invalid_name, AttrName);;
1597+
ParsedDeclName parsedAsyncName = parseDeclName(*asyncFunctionName);
1598+
if (!parsedAsyncName || !parsedAsyncName.ContextName.empty()) {
1599+
P.diagnose(nameLoc, diag::attr_completion_handler_async_invalid_name,
1600+
AttrName);
15971601
return nullptr;
15981602
}
15991603

1604+
size_t handlerIndex = 0;
1605+
SourceLoc handlerIndexLoc = SourceLoc();
1606+
if (P.consumeIf(tok::comma)) {
1607+
// The completion handler is explicitly specified, parse it
1608+
if (P.parseSpecificIdentifier("completionHandlerIndex",
1609+
diag::attr_missing_label,
1610+
"completionHandlerIndex", AttrName) ||
1611+
P.parseToken(tok::colon, diag::expected_colon_after_label,
1612+
"completionHandlerIndex")) {
1613+
return nullptr;
1614+
}
1615+
1616+
if (P.Tok.getText().getAsInteger(0, handlerIndex)) {
1617+
P.diagnose(P.Tok.getLoc(), diag::attr_expected_integer_literal, AttrName);
1618+
return nullptr;
1619+
}
1620+
1621+
handlerIndexLoc = P.consumeToken(tok::integer_literal);
1622+
}
1623+
16001624
SourceRange AttrRange = SourceRange(Loc, P.Tok.getRange().getStart());
16011625
if (!P.consumeIf(tok::r_paren)) {
1602-
P.diagnose(Loc, diag::attr_expected_rparen, AttrName,
1626+
P.diagnose(P.getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName,
16031627
DeclAttribute::isDeclModifier(DK));
16041628
return nullptr;
16051629
}
16061630

1607-
return new (P.Context) HasAsyncAlternativeAttr(
1608-
parsedName.formDeclNameRef(P.Context), AtLoc, AttrRange);
1631+
return new (P.Context) CompletionHandlerAsyncAttr(
1632+
parsedAsyncName.formDeclNameRef(P.Context), nameLoc,
1633+
handlerIndexLoc.isValid(), handlerIndex, handlerIndexLoc, AtLoc,
1634+
AttrRange);
16091635
}
16101636

16111637
bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
@@ -2686,8 +2712,9 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
26862712
name, AtLoc, range, /*implicit*/ false));
26872713
break;
26882714
}
2689-
case DAK_HasAsyncAlternative: {
2690-
auto *attr = parseAsyncAlternativeAttribute(*this, AttrName, AtLoc, DK);
2715+
case DAK_CompletionHandlerAsync: {
2716+
auto *attr =
2717+
parseCompletionHandlerAsyncAttribute(*this, AttrName, AtLoc, DK);
26912718
if (!attr) {
26922719
skipUntilDeclStmtRBrace(tok::r_paren);
26932720
consumeIf(tok::r_paren);

0 commit comments

Comments
 (0)