Skip to content

Commit 077d96c

Browse files
authored
Merge pull request #36012 from DougGregor/explicit-concurrent-closure
Add support for @Concurrent attribute on closures.
2 parents 7fe1c9c + 7c336ea commit 077d96c

File tree

13 files changed

+113
-11
lines changed

13 files changed

+113
-11
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ ERROR(incorrect_explicit_closure_result,none,
276276
"declared closure result %0 is incompatible with contextual type %1",
277277
(Type, Type))
278278

279+
ERROR(unsupported_closure_attr,none,
280+
"%select{attribute |}0 '%1' is not supported on a closure",
281+
(bool, StringRef))
282+
279283
NOTE(suggest_expected_match,none,
280284
"%select{expected an argument list|produces result}0 of type '%1'",
281285
(bool, StringRef))

include/swift/AST/Expr.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef SWIFT_AST_EXPR_H
1818
#define SWIFT_AST_EXPR_H
1919

20+
#include "swift/AST/Attr.h"
2021
#include "swift/AST/CaptureInfo.h"
2122
#include "swift/AST/ConcreteDeclRef.h"
2223
#include "swift/AST/DeclContext.h"
@@ -3771,6 +3772,9 @@ class ClosureExpr : public AbstractClosureExpr {
37713772
};
37723773

37733774
private:
3775+
/// The attributes attached to the closure.
3776+
DeclAttributes Attributes;
3777+
37743778
/// The range of the brackets of the capture list, if present.
37753779
SourceRange BracketRange;
37763780

@@ -3803,13 +3807,14 @@ class ClosureExpr : public AbstractClosureExpr {
38033807
/// was originally just a single expression.
38043808
llvm::PointerIntPair<BraceStmt *, 1, bool> Body;
38053809
public:
3806-
ClosureExpr(SourceRange bracketRange, VarDecl *capturedSelfDecl,
3810+
ClosureExpr(const DeclAttributes &attributes,
3811+
SourceRange bracketRange, VarDecl *capturedSelfDecl,
38073812
ParameterList *params, SourceLoc asyncLoc, SourceLoc throwsLoc,
38083813
SourceLoc arrowLoc, SourceLoc inLoc, TypeExpr *explicitResultType,
38093814
unsigned discriminator, DeclContext *parent)
38103815
: AbstractClosureExpr(ExprKind::Closure, Type(), /*Implicit=*/false,
38113816
discriminator, parent),
3812-
BracketRange(bracketRange),
3817+
Attributes(attributes), BracketRange(bracketRange),
38133818
CapturedSelfDecl(capturedSelfDecl),
38143819
AsyncLoc(asyncLoc), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc),
38153820
InLoc(inLoc),
@@ -3830,6 +3835,9 @@ class ClosureExpr : public AbstractClosureExpr {
38303835
Body.setInt(isSingleExpression);
38313836
}
38323837

3838+
DeclAttributes &getAttrs() { return Attributes; }
3839+
const DeclAttributes &getAttrs() const { return Attributes; }
3840+
38333841
/// Determine whether the parameters of this closure are actually
38343842
/// anonymous closure variables.
38353843
bool hasAnonymousClosureVars() const {

include/swift/Parse/Parser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,9 @@ class Parser {
688688
/// Skip over SIL decls until we encounter the start of a Swift decl or eof.
689689
void skipSILUntilSwiftDecl();
690690

691+
/// Skip over any attribute.
692+
void skipAnyAttribute();
693+
691694
/// If the parser is generating only a syntax tree, try loading the current
692695
/// node from a previously generated syntax tree.
693696
/// Returns \c true if the node has been loaded and inserted into the current
@@ -1575,6 +1578,7 @@ class Parser {
15751578
/// \returns ParserStatus error if an error occurred. Success if no signature
15761579
/// is present or succssfully parsed.
15771580
ParserStatus parseClosureSignatureIfPresent(
1581+
DeclAttributes &attributes,
15781582
SourceRange &bracketRange,
15791583
SmallVectorImpl<CaptureListEntry> &captureList,
15801584
VarDecl *&capturedSelfParamDecl,

lib/Parse/ParseDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5728,6 +5728,15 @@ void Parser::skipSILUntilSwiftDecl() {
57285728
}
57295729
}
57305730

5731+
void Parser::skipAnyAttribute() {
5732+
consumeToken(tok::at_sign);
5733+
if (!consumeIf(tok::identifier))
5734+
return;
5735+
5736+
if (consumeIf(tok::l_paren))
5737+
skipUntil(tok::r_paren);
5738+
}
5739+
57315740
/// Returns a descriptive name for the given accessor/addressor kind.
57325741
static StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind,
57335742
bool article) {

lib/Parse/ParseExpr.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "swift/Parse/Parser.h"
1818
#include "swift/AST/ASTWalker.h"
19+
#include "swift/AST/Attr.h"
1920
#include "swift/AST/DiagnosticsParse.h"
2021
#include "swift/AST/TypeRepr.h"
2122
#include "swift/Basic/EditorPlaceholder.h"
@@ -2335,6 +2336,7 @@ static void printTupleNames(const TypeRepr *typeRepr, llvm::raw_ostream &OS) {
23352336
}
23362337

23372338
ParserStatus Parser::parseClosureSignatureIfPresent(
2339+
DeclAttributes &attributes,
23382340
SourceRange &bracketRange,
23392341
SmallVectorImpl<CaptureListEntry> &captureList,
23402342
VarDecl *&capturedSelfDecl,
@@ -2344,6 +2346,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
23442346
TypeExpr *&explicitResultType, SourceLoc &inLoc) {
23452347
// Clear out result parameters.
23462348
bracketRange = SourceRange();
2349+
attributes = DeclAttributes();
23472350
capturedSelfDecl = nullptr;
23482351
params = nullptr;
23492352
throwsLoc = SourceLoc();
@@ -2360,9 +2363,16 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
23602363

23612364
// If we have a leading token that may be part of the closure signature, do a
23622365
// speculative parse to validate it and look for 'in'.
2363-
if (Tok.isAny(tok::l_paren, tok::l_square, tok::identifier, tok::kw__)) {
2366+
if (Tok.isAny(
2367+
tok::at_sign, tok::l_paren, tok::l_square, tok::identifier,
2368+
tok::kw__)) {
23642369
BacktrackingScope backtrack(*this);
23652370

2371+
// Consume attributes.
2372+
while (Tok.is(tok::at_sign)) {
2373+
skipAnyAttribute();
2374+
}
2375+
23662376
// Skip by a closure capture list if present.
23672377
if (consumeIf(tok::l_square)) {
23682378
skipUntil(tok::r_square);
@@ -2425,6 +2435,9 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
24252435
}
24262436
ParserStatus status;
24272437
SyntaxParsingContext ClosureSigCtx(SyntaxContext, SyntaxKind::ClosureSignature);
2438+
2439+
(void)parseDeclAttributeList(attributes);
2440+
24282441
if (Tok.is(tok::l_square) && peekToken().is(tok::r_square)) {
24292442

24302443
SyntaxParsingContext CaptureCtx(SyntaxContext,
@@ -2721,6 +2734,7 @@ ParserResult<Expr> Parser::parseExprClosure() {
27212734
SourceLoc leftBrace = consumeToken();
27222735

27232736
// Parse the closure-signature, if present.
2737+
DeclAttributes attributes;
27242738
SourceRange bracketRange;
27252739
SmallVector<CaptureListEntry, 2> captureList;
27262740
VarDecl *capturedSelfDecl;
@@ -2731,8 +2745,8 @@ ParserResult<Expr> Parser::parseExprClosure() {
27312745
TypeExpr *explicitResultType;
27322746
SourceLoc inLoc;
27332747
Status |= parseClosureSignatureIfPresent(
2734-
bracketRange, captureList, capturedSelfDecl, params, asyncLoc, throwsLoc,
2735-
arrowLoc, explicitResultType, inLoc);
2748+
attributes, bracketRange, captureList, capturedSelfDecl, params, asyncLoc,
2749+
throwsLoc, arrowLoc, explicitResultType, inLoc);
27362750

27372751
// If the closure was created in the context of an array type signature's
27382752
// size expression, there will not be a local context. A parse error will
@@ -2748,8 +2762,8 @@ ParserResult<Expr> Parser::parseExprClosure() {
27482762

27492763
// Create the closure expression and enter its context.
27502764
auto *closure = new (Context) ClosureExpr(
2751-
bracketRange, capturedSelfDecl, params, asyncLoc, throwsLoc, arrowLoc,
2752-
inLoc, explicitResultType, discriminator, CurDeclContext);
2765+
attributes, bracketRange, capturedSelfDecl, params, asyncLoc, throwsLoc,
2766+
arrowLoc, inLoc, explicitResultType, discriminator, CurDeclContext);
27532767
ParseFunctionBody cc(*this, closure);
27542768

27552769
// Handle parameters.

lib/Sema/CSApply.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7640,8 +7640,12 @@ namespace {
76407640
});
76417641

76427642
switch (result) {
7643-
case SolutionApplicationToFunctionResult::Success:
7643+
case SolutionApplicationToFunctionResult::Success: {
7644+
if (auto closure = dyn_cast_or_null<ClosureExpr>(
7645+
fn.getAbstractClosureExpr()))
7646+
TypeChecker::checkClosureAttributes(closure);
76447647
return false;
7648+
}
76457649

76467650
case SolutionApplicationToFunctionResult::Failure:
76477651
return true;

lib/Sema/ConstraintSystem.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2358,17 +2358,19 @@ FunctionType::ExtInfo ConstraintSystem::closureEffects(ClosureExpr *expr) {
23582358
// set of effects.
23592359
bool throws = expr->getThrowsLoc().isValid();
23602360
bool async = expr->getAsyncLoc().isValid();
2361+
bool concurrent = expr->getAttrs().hasAttribute<ConcurrentAttr>();
23612362
if (throws || async) {
23622363
return ASTExtInfoBuilder()
23632364
.withThrows(throws)
23642365
.withAsync(async)
2366+
.withConcurrent(concurrent)
23652367
.build();
23662368
}
23672369

23682370
// Scan the body to determine the effects.
23692371
auto body = expr->getBody();
23702372
if (!body)
2371-
return FunctionType::ExtInfo();
2373+
return ASTExtInfoBuilder().withConcurrent(concurrent).build();
23722374

23732375
auto throwFinder = FindInnerThrows(*this, expr);
23742376
body->walk(throwFinder);
@@ -2377,6 +2379,7 @@ FunctionType::ExtInfo ConstraintSystem::closureEffects(ClosureExpr *expr) {
23772379
auto result = ASTExtInfoBuilder()
23782380
.withThrows(throwFinder.foundThrow())
23792381
.withAsync(asyncFinder.foundAsync())
2382+
.withConcurrent(concurrent)
23802383
.build();
23812384
closureEffectsCache[expr] = result;
23822385
return result;

lib/Sema/DebuggerTestingTransform.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ class DebuggerTestingTransform : public ASTWalker {
226226
// Create the closure.
227227
auto *Params = ParameterList::createEmpty(Ctx);
228228
auto *Closure = new (Ctx)
229-
ClosureExpr(SourceRange(), nullptr, Params, SourceLoc(), SourceLoc(),
229+
ClosureExpr(DeclAttributes(), SourceRange(), nullptr, Params,
230+
SourceLoc(), SourceLoc(),
230231
SourceLoc(), SourceLoc(), nullptr,
231232
DF.getNextDiscriminator(), getCurrentDeclContext());
232233
Closure->setImplicit(true);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5475,4 +5475,35 @@ void AttributeChecker::visitReasyncAttr(ReasyncAttr *attr) {
54755475
// FIXME
54765476
}
54775477

5478-
void AttributeChecker::visitAtReasyncAttr(AtReasyncAttr *attr) {}
5478+
void AttributeChecker::visitAtReasyncAttr(AtReasyncAttr *attr) {}
5479+
5480+
namespace {
5481+
5482+
class ClosureAttributeChecker
5483+
: public AttributeVisitor<ClosureAttributeChecker> {
5484+
ASTContext &ctx;
5485+
public:
5486+
ClosureAttributeChecker(ClosureExpr *closure)
5487+
: ctx(closure->getASTContext()) { }
5488+
5489+
void visitDeclAttribute(DeclAttribute *attr) {
5490+
ctx.Diags.diagnose(
5491+
attr->getLocation(), diag::unsupported_closure_attr,
5492+
attr->isDeclModifier(), attr->getAttrName())
5493+
.fixItRemove(attr->getRangeWithAt());
5494+
attr->setInvalid();
5495+
}
5496+
5497+
void visitConcurrentAttr(ConcurrentAttr *attr) {
5498+
// Nothing else to check.
5499+
}
5500+
};
5501+
5502+
}
5503+
5504+
void TypeChecker::checkClosureAttributes(ClosureExpr *closure) {
5505+
ClosureAttributeChecker checker(closure);
5506+
for (auto attr : closure->getAttrs()) {
5507+
checker.visit(attr);
5508+
}
5509+
}

lib/Sema/TypeCheckStmt.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2077,6 +2077,7 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator,
20772077
}
20782078

20792079
bool TypeChecker::typeCheckClosureBody(ClosureExpr *closure) {
2080+
TypeChecker::checkClosureAttributes(closure);
20802081
TypeChecker::checkParameterList(closure->getParameters(), closure);
20812082

20822083
BraceStmt *body = closure->getBody();

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ void typeCheckDecl(Decl *D);
451451

452452
void addImplicitDynamicAttribute(Decl *D);
453453
void checkDeclAttributes(Decl *D);
454+
void checkClosureAttributes(ClosureExpr *closure);
454455
void checkParameterList(ParameterList *params, DeclContext *owner);
455456

456457
void diagnoseDuplicateBoundVars(Pattern *pattern);

test/attr/attr_concurrent.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,10 @@ func testCaseNonTrivialValue() {
118118

119119
j = 17
120120
}
121+
122+
func testExplicitConcurrentClosure() {
123+
let fn = { @concurrent in
124+
17
125+
}
126+
let _: String = fn // expected-error{{cannot convert value of type '@concurrent () -> Int' to specified type 'String'}}
127+
}

test/attr/closures.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 5
2+
3+
func testNonacceptedClosures() {
4+
let fn = { @usableFromInline in // expected-error{{'usableFromInline' is not supported on a closure}}
5+
"hello"
6+
}
7+
8+
let fn2: (Int) -> Int = { @usableFromInline x in // expected-error{{'usableFromInline' is not supported on a closure}}
9+
print("hello")
10+
return x
11+
}
12+
13+
_ = fn
14+
_ = fn2
15+
}

0 commit comments

Comments
 (0)