Skip to content

Commit 915a59f

Browse files
authored
Merge pull request #4698 from milseman/3_0_noescape
[3.0] better diagnostics for @escaping related problems
2 parents b3c7894 + 0f6e0db commit 915a59f

File tree

5 files changed

+46
-4
lines changed

5 files changed

+46
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,6 +1879,11 @@ ERROR(noescape_conflicts_escaping_autoclosure,none,
18791879

18801880
ERROR(escaping_function_type,none,
18811881
"@escaping may only be applied to parameters of function type", ())
1882+
ERROR(escaping_non_function_parameter,none,
1883+
"@escaping attribute may only be used in function parameter position", ())
1884+
1885+
NOTE(escaping_optional_type_argument, none,
1886+
"closure is already escaping in optional type argument", ())
18821887

18831888
// NSManaged attribute
18841889
ERROR(attr_NSManaged_not_instance_member,none,

lib/Sema/TypeCheckType.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,8 +2010,13 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
20102010
loc.getAdvancedLoc(-1),
20112011
Lexer::getLocForEndOfToken(SM, loc));
20122012

2013-
TC.diagnose(loc, diag::escaping_function_type)
2013+
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);
@@ -2432,6 +2437,8 @@ Type TypeResolver::resolveDictionaryType(DictionaryTypeRepr *repr,
24322437
// Check the requirements on the generic arguments.
24332438
auto unboundTy = dictDecl->getDeclaredType();
24342439
TypeLoc args[2] = { TypeLoc(repr->getKey()), TypeLoc(repr->getValue()) };
2440+
args[0].setType(keyTy, true);
2441+
args[1].setType(valueTy, true);
24352442

24362443
if (!TC.applyUnboundGenericArguments(
24372444
unboundTy, dictDecl, repr->getStartLoc(), DC, args,
@@ -2452,9 +2459,12 @@ Type TypeResolver::resolveDictionaryType(DictionaryTypeRepr *repr,
24522459

24532460
Type TypeResolver::resolveOptionalType(OptionalTypeRepr *repr,
24542461
TypeResolutionOptions options) {
2462+
auto elementOptions = withoutContext(options);
2463+
elementOptions |= TR_ImmediateOptionalTypeArgument;
2464+
24552465
// The T in T? is a generic type argument and therefore always an AST type.
24562466
// FIXME: diagnose non-materializability of element type!
2457-
Type baseTy = resolveType(repr->getBase(), withoutContext(options));
2467+
Type baseTy = resolveType(repr->getBase(), elementOptions);
24582468
if (!baseTy || baseTy->is<ErrorType>()) return baseTy;
24592469

24602470
auto optionalTy = TC.getOptionalType(repr->getQuestionLoc(), baseTy);
@@ -2466,9 +2476,12 @@ Type TypeResolver::resolveOptionalType(OptionalTypeRepr *repr,
24662476
Type TypeResolver::resolveImplicitlyUnwrappedOptionalType(
24672477
ImplicitlyUnwrappedOptionalTypeRepr *repr,
24682478
TypeResolutionOptions options) {
2479+
auto elementOptions = withoutContext(options);
2480+
elementOptions |= TR_ImmediateOptionalTypeArgument;
2481+
24692482
// The T in T! is a generic type argument and therefore always an AST type.
24702483
// FIXME: diagnose non-materializability of element type!
2471-
Type baseTy = resolveType(repr->getBase(), withoutContext(options));
2484+
Type baseTy = resolveType(repr->getBase(), elementOptions);
24722485
if (!baseTy || baseTy->is<ErrorType>()) return baseTy;
24732486

24742487
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: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,26 @@ func callEscapingAutoclosureWithNoEscape_3(_ fn: @autoclosure () -> Int) {
7070
takesEscapingAutoclosure(fn()) // expected-error{{closure use of non-escaping parameter 'fn' may allow it to escape}}
7171
}
7272

73+
let foo: @escaping (Int) -> Int // expected-error{{@escaping attribute may only be used in function parameter position}} {{10-20=}}
74+
75+
struct GenericStruct<T> {}
76+
77+
func misuseEscaping(_ a: @escaping Int) {} // expected-error{{@escaping attribute only applies to function types}}
78+
func misuseEscaping(_ a: (@escaping Int)?) {} // expected-error{{@escaping attribute only applies to function types}}
79+
80+
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=}}
88+
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+
func misuseEscaping(_ a: GenericStruct<@escaping (Int) -> Int>) {} // expected-error{{@escaping attribute may only be used in function parameter position}} {{40-50=}}
7393

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

test/attr/attr_noescape.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ func escapeNoEscapeResult(_ x: [Int]) -> (@noescape (Int) -> Int) -> Int { // ex
271271
typealias CompletionHandlerNE = @noescape (_ success: Bool) -> () // expected-warning{{@noescape is the default and is deprecated}} {{33-43=}}
272272

273273
// Explicit @escaping is not allowed here
274-
typealias CompletionHandlerE = @escaping (_ success: Bool) -> () // expected-error{{@escaping may only be applied to parameters of function type}}
274+
typealias CompletionHandlerE = @escaping (_ success: Bool) -> () // expected-error{{@escaping attribute may only be used in function parameter position}} {{32-42=}}
275275

276276
// No @escaping -- it's implicit from context
277277
typealias CompletionHandler = (_ success: Bool) -> ()

0 commit comments

Comments
 (0)