Skip to content

Commit 897fc83

Browse files
committed
[noescape-by-default] Diagnostic notes for closure type arguments
Adds in helpful notes when a closure type argument is already escaping, and thus doesn't need annotation. The common case targeted now is Optional and IUO, which is the biggest bang for our buck without needlessly complicating the type options. Test cases included for the new note, and potential interactions. In the future we'd like some kind of parent pointer, or context stack to give better diagnostics universally. For now, we hack it by having ImmediateOptionalTypeArgument as a special flag.
1 parent 95b6de2 commit 897fc83

File tree

4 files changed

+35
-2
lines changed

4 files changed

+35
-2
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,9 @@ ERROR(escaping_function_type,none,
18821882
ERROR(escaping_non_function_parameter,none,
18831883
"@escaping attribute may only be used in function parameter position", ())
18841884

1885+
NOTE(escaping_optional_type_argument, none,
1886+
"closure is already escaping in optional type argument", ())
1887+
18851888
// NSManaged attribute
18861889
ERROR(attr_NSManaged_not_instance_member,none,
18871890
"@NSManaged only allowed on an instance property or method", ())

lib/Sema/TypeCheckType.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,6 +2012,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
20122012

20132013
TC.diagnose(loc, diag::escaping_non_function_parameter)
20142014
.fixItRemove(attrRange);
2015+
2016+
// Try to find a helpful note based on how the type is being used
2017+
if (options.contains(TR_ImmediateOptionalTypeArgument)) {
2018+
TC.diagnose(repr->getLoc(), diag::escaping_optional_type_argument);
2019+
}
20152020
}
20162021

20172022
attrs.clearAttribute(TAK_escaping);
@@ -2452,9 +2457,12 @@ Type TypeResolver::resolveDictionaryType(DictionaryTypeRepr *repr,
24522457

24532458
Type TypeResolver::resolveOptionalType(OptionalTypeRepr *repr,
24542459
TypeResolutionOptions options) {
2460+
auto elementOptions = withoutContext(options);
2461+
elementOptions |= TR_ImmediateOptionalTypeArgument;
2462+
24552463
// The T in T? is a generic type argument and therefore always an AST type.
24562464
// FIXME: diagnose non-materializability of element type!
2457-
Type baseTy = resolveType(repr->getBase(), withoutContext(options));
2465+
Type baseTy = resolveType(repr->getBase(), elementOptions);
24582466
if (!baseTy || baseTy->is<ErrorType>()) return baseTy;
24592467

24602468
auto optionalTy = TC.getOptionalType(repr->getQuestionLoc(), baseTy);
@@ -2466,9 +2474,12 @@ Type TypeResolver::resolveOptionalType(OptionalTypeRepr *repr,
24662474
Type TypeResolver::resolveImplicitlyUnwrappedOptionalType(
24672475
ImplicitlyUnwrappedOptionalTypeRepr *repr,
24682476
TypeResolutionOptions options) {
2477+
auto elementOptions = withoutContext(options);
2478+
elementOptions |= TR_ImmediateOptionalTypeArgument;
2479+
24692480
// The T in T! is a generic type argument and therefore always an AST type.
24702481
// FIXME: diagnose non-materializability of element type!
2471-
Type baseTy = resolveType(repr->getBase(), withoutContext(options));
2482+
Type baseTy = resolveType(repr->getBase(), elementOptions);
24722483
if (!baseTy || baseTy->is<ErrorType>()) return baseTy;
24732484

24742485
auto uncheckedOptionalTy =

lib/Sema/TypeChecker.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,9 @@ enum TypeResolutionFlags : unsigned {
375375

376376
/// Whether this is the type of an editor placeholder.
377377
TR_EditorPlaceholder = 0x200000,
378+
379+
/// Whether we are in a type argument for an optional
380+
TR_ImmediateOptionalTypeArgument = 0x400000,
378381
};
379382

380383
/// Option set describing how type resolution should work.
@@ -391,6 +394,7 @@ withoutContext(TypeResolutionOptions options) {
391394
options -= TR_ImmediateFunctionInput;
392395
options -= TR_FunctionInput;
393396
options -= TR_EnumCase;
397+
options -= TR_ImmediateOptionalTypeArgument;
394398
return options;
395399
}
396400

test/attr/attr_escaping.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,25 @@ func callEscapingAutoclosureWithNoEscape_3(_ fn: @autoclosure () -> Int) {
7272

7373
let foo: @escaping (Int) -> Int // expected-error{{@escaping attribute may only be used in function parameter position}} {{10-20=}}
7474

75+
struct GenericStruct<T> {}
76+
7577
func misuseEscaping(_ a: @escaping Int) {} // expected-error{{@escaping attribute only applies to function types}}
7678
func misuseEscaping(_ a: (@escaping Int)?) {} // expected-error{{@escaping attribute only applies to function types}}
79+
7780
func misuseEscaping(_ a: (@escaping (Int) -> Int)?) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{27-37=}}
81+
// expected-note@-1{{closure is already escaping in optional type argument}}
82+
func misuseEscaping(nest a: (((@escaping (Int) -> Int))?)) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{32-42=}}
83+
// expected-note@-1{{closure is already escaping in optional type argument}}
84+
func misuseEscaping(iuo a: (@escaping (Int) -> Int)!) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{29-39=}}
85+
// expected-note@-1{{closure is already escaping in optional type argument}}
86+
87+
func misuseEscaping(_ a: Optional<@escaping (Int) -> Int>, _ b: Int) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{35-45=}}
7888
func misuseEscaping(_ a: (@escaping (Int) -> Int, Int)) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{27-37=}}
89+
func misuseEscaping(_ a: [@escaping (Int) -> Int]) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{27-37=}}
90+
func misuseEscaping(_ a: [@escaping (Int) -> Int]?) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{27-37=}}
91+
func misuseEscaping(_ a: [Int : @escaping (Int) -> Int]) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{33-43=}}
92+
// expected-error@-1{{@escaping attribute may only be used in function parameter position}} {{33-43=}}
93+
func misuseEscaping(_ a: GenericStruct<@escaping (Int) -> Int>) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{40-50=}}
7994

8095
func takesEscapingGeneric<T>(_ fn: @escaping () -> T) {}
8196
func callEscapingGeneric<T>(_ fn: () -> T) { // expected-note {{parameter 'fn' is implicitly non-escaping}} {{35-35=@escaping }}

0 commit comments

Comments
 (0)