Skip to content

Commit a3cc692

Browse files
authored
Merge pull request #40378 from etcwilde/ewilde/concurrency/unavailablefromasync-messages
Adding optional message to `_unavailableFromAsync`
2 parents e02836b + 691cf19 commit a3cc692

File tree

13 files changed

+151
-10
lines changed

13 files changed

+151
-10
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(_const, CompileTimeConst,
699699
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
700700
126)
701701

702-
SIMPLE_DECL_ATTR(_unavailableFromAsync, UnavailableFromAsync,
702+
DECL_ATTR(_unavailableFromAsync, UnavailableFromAsync,
703703
OnFunc | OnConstructor | UserInaccessible |
704704
ABIStableToAdd | ABIStableToRemove |
705705
APIBreakingToAdd | APIStableToRemove,

include/swift/AST/Attr.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,6 +2085,26 @@ class TypeSequenceAttr : public DeclAttribute {
20852085
}
20862086
};
20872087

2088+
/// The @_unavailableFromAsync attribute, used to make function declarations
2089+
/// unavailable from async contexts.
2090+
class UnavailableFromAsyncAttr : public DeclAttribute {
2091+
public:
2092+
UnavailableFromAsyncAttr(StringRef Message, SourceLoc AtLoc,
2093+
SourceRange Range, bool Implicit)
2094+
: DeclAttribute(DAK_UnavailableFromAsync, AtLoc, Range, Implicit),
2095+
Message(Message) {}
2096+
UnavailableFromAsyncAttr(StringRef Message, bool Implicit)
2097+
: UnavailableFromAsyncAttr(Message, SourceLoc(), SourceRange(),
2098+
Implicit) {}
2099+
const StringRef Message;
2100+
2101+
bool hasMessage() const { return !Message.empty(); }
2102+
2103+
static bool classof(const DeclAttribute *DA) {
2104+
return DA->getKind() == DAK_UnavailableFromAsync;
2105+
}
2106+
};
2107+
20882108
/// Attributes that may be applied to declarations.
20892109
class DeclAttributes {
20902110
/// Linked list of declaration attributes.

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,8 @@ ERROR(attr_missing_label,PointsToFirstBadToken,
14351435
"missing label '%0:' in '@%1' attribute", (StringRef, StringRef))
14361436
ERROR(attr_expected_label,none,
14371437
"expected label '%0:' in '@%1' attribute", (StringRef, StringRef))
1438+
ERROR(attr_expected_colon_after_label,none,
1439+
"expected ':' after label '%0'", (StringRef))
14381440

14391441
ERROR(alignment_must_be_positive_integer,none,
14401442
"alignment value must be a positive integer literal", ())

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4719,7 +4719,8 @@ ERROR(async_named_decl_must_be_available_from_async,none,
47194719
"asynchronous %0 %1 must be available from asynchronous contexts",
47204720
(DescriptiveDeclKind, DeclName))
47214721
ERROR(async_unavailable_decl,none,
4722-
"%0 %1 is unavailable from asynchronous contexts", (DescriptiveDeclKind, DeclBaseName))
4722+
"%0 %1 is unavailable from asynchronous contexts%select{|; %3}2",
4723+
(DescriptiveDeclKind, DeclBaseName, bool, StringRef))
47234724

47244725
//------------------------------------------------------------------------------
47254726
// MARK: Type Check Types

lib/AST/Attr.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,17 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
11641164
break;
11651165
}
11661166

1167+
case DAK_UnavailableFromAsync: {
1168+
Printer.printAttrName("@_unavailableFromAsync");
1169+
const UnavailableFromAsyncAttr *attr = cast<UnavailableFromAsyncAttr>(this);
1170+
if (attr->hasMessage()) {
1171+
Printer << "(message: \"";
1172+
Printer << attr->Message;
1173+
Printer << "\")";
1174+
}
1175+
break;
1176+
}
1177+
11671178
case DAK_Count:
11681179
llvm_unreachable("exceed declaration attribute kinds");
11691180

@@ -1317,6 +1328,8 @@ StringRef DeclAttribute::getAttrName() const {
13171328
return "transpose";
13181329
case DAK_TypeSequence:
13191330
return "_typeSequence";
1331+
case DAK_UnavailableFromAsync:
1332+
return "_unavailableFromAsync";
13201333
}
13211334
llvm_unreachable("bad DeclAttrKind");
13221335
}

lib/Parse/ParseDecl.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,6 +2749,53 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
27492749
Attributes.add(TypeSequenceAttr::create(Context, AtLoc, range));
27502750
break;
27512751
}
2752+
2753+
case DAK_UnavailableFromAsync: {
2754+
StringRef message;
2755+
if (consumeIf(tok::l_paren)) {
2756+
if (!Tok.is(tok::identifier)) {
2757+
llvm_unreachable("Flag must start with an indentifier");
2758+
}
2759+
2760+
StringRef flag = Tok.getText();
2761+
2762+
if (flag != "message") {
2763+
diagnose(Tok.getLoc(), diag::attr_unknown_option, flag, AttrName);
2764+
return true;
2765+
}
2766+
consumeToken();
2767+
if (!consumeIf(tok::colon)) {
2768+
if (!Tok.is(tok::equal)) {
2769+
diagnose(Tok.getLoc(), diag::attr_expected_colon_after_label, flag);
2770+
return false;
2771+
}
2772+
diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value)
2773+
.fixItReplace(Tok.getLoc(), ": ");
2774+
consumeToken();
2775+
}
2776+
if (!Tok.is(tok::string_literal)) {
2777+
diagnose(Tok.getLoc(), diag::attr_expected_string_literal, AttrName);
2778+
return false;
2779+
}
2780+
2781+
Optional<StringRef> value = getStringLiteralIfNotInterpolated(
2782+
Tok.getLoc(), flag);
2783+
if (!value)
2784+
return false;
2785+
Token stringTok = Tok;
2786+
consumeToken();
2787+
message = *value;
2788+
2789+
if (!consumeIf(tok::r_paren))
2790+
diagnose(stringTok.getRange().getEnd(), diag::attr_expected_rparen,
2791+
AttrName, /*isModifiler*/false)
2792+
.fixItInsertAfter(stringTok.getLoc(), ")");
2793+
}
2794+
2795+
Attributes.add(new (Context) UnavailableFromAsyncAttr(
2796+
message, AtLoc, SourceRange(Loc, Tok.getLoc()), false));
2797+
break;
2798+
}
27522799
}
27532800

27542801
if (DuplicateAttribute) {

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3149,10 +3149,12 @@ diagnoseDeclUnavailableFromAsync(const ValueDecl *D, SourceRange R,
31493149
return false;
31503150

31513151
ASTContext &ctx = Where.getDeclContext()->getASTContext();
3152+
const UnavailableFromAsyncAttr *attr =
3153+
D->getAttrs().getAttribute<UnavailableFromAsyncAttr>();
31523154
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
31533155
ctx.Diags
31543156
.diagnose(diagLoc, diag::async_unavailable_decl, D->getDescriptiveKind(),
3155-
D->getBaseName())
3157+
D->getBaseName(), attr->hasMessage(), attr->Message)
31563158
.warnUntilSwiftVersion(6);
31573159
D->diagnose(diag::decl_declared_here, D->getName());
31583160
return true;

lib/Serialization/Deserialization.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4747,6 +4747,14 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
47474747
break;
47484748
}
47494749

4750+
case decls_block::UnavailableFromAsync_DECL_ATTR: {
4751+
bool isImplicit;
4752+
serialization::decls_block::UnavailableFromAsyncDeclAttrLayout::
4753+
readRecord(scratch, isImplicit);
4754+
Attr = new (ctx) UnavailableFromAsyncAttr(blobData, isImplicit);
4755+
break;
4756+
}
4757+
47504758
#define SIMPLE_DECL_ATTR(NAME, CLASS, ...) \
47514759
case decls_block::CLASS##_DECL_ATTR: { \
47524760
bool isImplicit; \

lib/Serialization/ModuleFormat.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5656
/// describe what change you made. The content of this comment isn't important;
5757
/// it just ensures a conflict if two people change the module format.
5858
/// Don't worry about adhering to the 80-column limit for this line.
59-
const uint16_t SWIFTMODULE_VERSION_MINOR = 648; // mark_unresolved_move_addr
59+
const uint16_t SWIFTMODULE_VERSION_MINOR = 649; // _unavailableFromAsync message
6060

6161
/// A standard hash seed used for all string hashes in a serialized module.
6262
///
@@ -2022,6 +2022,12 @@ namespace decls_block {
20222022
TypeIDField, // type referenced by this custom attribute
20232023
BCFixed<1> // is the argument (unsafe)
20242024
>;
2025+
2026+
using UnavailableFromAsyncDeclAttrLayout = BCRecordLayout<
2027+
UnavailableFromAsync_DECL_ATTR,
2028+
BCFixed<1>, // Implicit flag
2029+
BCBlob // Message
2030+
>;
20252031
}
20262032

20272033
/// Returns the encoding kind for the given decl.

lib/Serialization/Serialization.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,6 +2754,16 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
27542754
TypeSequenceDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode);
27552755
return;
27562756
}
2757+
2758+
case DAK_UnavailableFromAsync: {
2759+
auto abbrCode =
2760+
S.DeclTypeAbbrCodes[UnavailableFromAsyncDeclAttrLayout::Code];
2761+
auto *theAttr = cast<UnavailableFromAsyncAttr>(DA);
2762+
UnavailableFromAsyncDeclAttrLayout::emitRecord(
2763+
S.Out, S.ScratchRecord, abbrCode, theAttr->isImplicit(),
2764+
theAttr->Message);
2765+
return;
2766+
}
27572767
}
27582768
}
27592769

test/ClangImporter/objc_async.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ func testSlowServer(slowServer: SlowServer) async throws {
4545
let _: Int = await slowServer.bestName("hello")
4646
let _: Int = await slowServer.customize("hello")
4747

48-
slowServer.unavailableMethod() // expected-warning{{'unavailableMethod' is unavailable from asynchronous contexts}}
48+
slowServer.unavailableMethod() // expected-warning{{instance method 'unavailableMethod' is unavailable from asynchronous contexts}}
49+
slowServer.unavailableMethodWithMessage() // expected-warning{{instance method 'unavailableMethodWithMessage' is unavailable from asynchronous contexts; Blarpy!}}
4950

5051
let _: String = await slowServer.dance("slide")
5152
let _: String = await slowServer.__leap(17)

test/Concurrency/unavailable_from_async.swift

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ actor Baz { }
3535

3636
@available(SwiftStdlib 5.1, *)
3737
struct Bop {
38-
@_unavailableFromAsync
38+
@_unavailableFromAsync(message: "Use Bop(a: Int) instead")
3939
init() {} // expected-note 4 {{'init()' declared here}}
4040

4141
init(a: Int) { }
@@ -59,7 +59,7 @@ func foo() {} // expected-note 4 {{'foo()' declared here}}
5959
func makeAsyncClosuresSynchronously(bop: inout Bop) -> (() async -> Void) {
6060
return { () async -> Void in
6161
// Unavailable methods
62-
_ = Bop() // expected-warning@:9{{'init' is unavailable from asynchronous contexts}}
62+
_ = Bop() // expected-warning@:9{{'init' is unavailable from asynchronous contexts; Use Bop(a: Int) instead}}
6363
_ = Bop(a: 32)
6464
bop.foo() // expected-warning@:9{{'foo' is unavailable from asynchronous contexts}}
6565
bop.muppet() // expected-warning@:9{{'muppet' is unavailable from asynchronous contexts}}
@@ -83,7 +83,7 @@ func makeAsyncClosuresSynchronously(bop: inout Bop) -> (() async -> Void) {
8383
func asyncFunc() async { // expected-error{{asynchronous global function 'asyncFunc()' must be available from asynchronous contexts}}
8484

8585
var bop = Bop(a: 32)
86-
_ = Bop() // expected-warning@:7{{'init' is unavailable from asynchronous contexts}}
86+
_ = Bop() // expected-warning@:7{{'init' is unavailable from asynchronous contexts; Use Bop(a: Int) instead}}
8787
bop.foo() // expected-warning@:7{{'foo' is unavailable from asynchronous contexts}}
8888
bop.muppet() // expected-warning@:7{{'muppet' is unavailable from asynchronous contexts}}
8989
unavailableFunction() // expected-warning@:3{{'unavailableFunction' is unavailable from asynchronous contexts}}
@@ -107,13 +107,13 @@ func asyncFunc() async { // expected-error{{asynchronous global function 'asyncF
107107
foo() // expected-warning@:7{{'foo' is unavailable from asynchronous contexts}}
108108
bop.foo() // expected-warning@:11{{'foo' is unavailable from asynchronous contexts}}
109109
bop.muppet() // expected-warning@:11{{'muppet' is unavailable from asynchronous contexts}}
110-
_ = Bop() // expected-warning@:11{{'init' is unavailable from asynchronous contexts}}
110+
_ = Bop() // expected-warning@:11{{'init' is unavailable from asynchronous contexts; Use Bop(a: Int) instead}}
111111
unavailableFunction() // expected-warning@:7{{'unavailableFunction' is unavailable from asynchronous contexts}}
112112
}
113113
}
114114

115115
_ = { () async -> Void in
116-
_ = Bop() // expected-warning@:9{{'init' is unavailable from asynchronous contexts}}
116+
_ = Bop() // expected-warning@:9{{'init' is unavailable from asynchronous contexts; Use Bop(a: Int) instead}}
117117
foo() // expected-warning@:5{{'foo' is unavailable from asynchronous contexts}}
118118
bop.foo() // expected-warning@:9{{'foo' is unavailable from asynchronous contexts}}
119119
bop.muppet() // expected-warning@:9{{'muppet' is unavailable from asynchronous contexts}}
@@ -128,3 +128,33 @@ func asyncFunc() async { // expected-error{{asynchronous global function 'asyncF
128128
}
129129

130130
}
131+
132+
// Parsing tests
133+
134+
// expected-error@+2 {{expected declaration}}
135+
// expected-error@+1:24{{unknown option 'nope' for attribute '_unavailableFromAsync'}}
136+
@_unavailableFromAsync(nope: "almost right, but not quite")
137+
func blarp1() {}
138+
139+
// expected-error@+2 {{expected declaration}}
140+
// expected-error@+1 {{expected ':' after label 'message'}}
141+
@_unavailableFromAsync(message; "almost right, but not quite")
142+
func blarp2() {}
143+
144+
// expected-error@+1:31 {{'=' has been replaced with ':' in attribute arguments}}{{31-32=: }}
145+
@_unavailableFromAsync(message="almost right, but not quite")
146+
func blarp3() {}
147+
148+
// expected-error@+2 {{expected declaration}}
149+
// expected-error@+1 {{expected string literal in '_unavailableFromAsync' attribute}}
150+
@_unavailableFromAsync(message: 32)
151+
func blarp4() {}
152+
153+
// expected-error@+2 {{expected declaration}}
154+
// expected-error@+1 {{message cannot be an interpolated string}}
155+
@_unavailableFromAsync(message: "blarppy blarp \(31 + 10)")
156+
func blarp5() {}
157+
158+
// expected-error@+1:48 {{expected ')' in '_unavailableFromAsync' attribute}}{{48-48=)}}
159+
@_unavailableFromAsync(message: "blarppy blarp"
160+
func blarp6() {}

test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res
6767
-(void)customizedWithString:(NSString *)operation completionHandler:(void (^)(NSInteger))handler __attribute__((swift_name("customize(with:completionHandler:)"))) __attribute__((swift_async_name("customize(_:)")));
6868

6969
-(void)unavailableMethod __attribute__((__swift_attr__("@_unavailableFromAsync")));
70+
-(void)unavailableMethodWithMessage __attribute__((__swift_attr__("@_unavailableFromAsync(message: \"Blarpy!\")")));
7071

7172
-(void)dance:(NSString *)step andThen:(void (^)(NSString *))doSomething __attribute__((swift_async(not_swift_private,2)));
7273
-(void)leap:(NSInteger)height andThen:(void (^)(NSString *))doSomething __attribute__((swift_async(swift_private,2)));

0 commit comments

Comments
 (0)