From c3c6beac72bc0368030f06d52c46b6444fc48dbd Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 3 Mar 2016 13:48:09 -0800 Subject: [PATCH] Generalize the @noescape attribute to be a type attribute allowed in arbitrary places. This fixes a regression caught by SR-770 that would otherwise be introduced by us removing automatic currying syntax, it allows the use of @noescape on typealiases (resolving SR-824), allows @noescape on nested function types (fixing rdar://19997680) and allows @noescape to be used on local variables (fixing rdar://19997577). At this point, @noescape should stop being a decl attribute, but I'll bring that up on swift-evolution. --- CHANGELOG.md | 11 +++++ include/swift/AST/Attr.def | 2 +- include/swift/AST/DiagnosticsSema.def | 3 +- lib/Parse/ParseDecl.cpp | 8 +--- lib/Sema/MiscDiagnostics.cpp | 7 +-- test/attr/attr_noescape.swift | 63 +++++++++++++++++++++++++++ 6 files changed, 83 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f452a88765a7c..2c1f4e0e61a09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ Swift 3 ------- +* The @noescape attribute has been extended to be a more general type attribute. + You can now declare values of @noescape function type, e.g. in manually + curried function signatures. You can now also declare local variables of + @noescape type, and use @noescape in typealiases. For example, this is now + valid code: + + func apply(@noescape f: T -> U, + @noescape g: (@noescape T -> U) -> U) -> U { + return g(f) + } + * Curried function syntax has been removed, and now produces a compile-time error. diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index e87775cafa3ee..180337429a6d4 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -46,7 +46,7 @@ TYPE_ATTR(in) TYPE_ATTR(inout) TYPE_ATTR(inout_aliasable) TYPE_ATTR(in_guaranteed) -TYPE_ATTR(noescape) // Only valid in sil mode. +TYPE_ATTR(noescape) TYPE_ATTR(owned) TYPE_ATTR(unowned_inner_pointer) TYPE_ATTR(guaranteed) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 62946798ddb51..e35a6ce09bdfb 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2045,7 +2045,8 @@ NOTE(fix_unqualified_access_top_level_multi,none, ERROR(type_of_metatype,none, "'.dynamicType' is not allowed after a type name", ()) ERROR(invalid_noescape_use,none, - "@noescape parameter %0 may only be called", (Identifier)) + "@noescape %select{value|parameter}1 %0 may only be called", + (Identifier, bool)) NOTE(noescape_autoclosure,none, "parameter %0 is implicitly @noescape because it was declared @autoclosure", (Identifier)) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b4b4b9d712121..b56411a2b48c0 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1423,10 +1423,6 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) { // Determine which attribute it is, and diagnose it if unknown. TypeAttrKind attr = TypeAttributes::getAttrKindFromString(Tok.getText()); - // noescape is only valid as a type attribute in SIL mode. - if (attr == TAK_noescape && !isInSILMode()) - attr = TAK_Count; - if (attr == TAK_Count) { if (justChecking) return true; @@ -1635,8 +1631,8 @@ bool Parser::parseDeclAttributeList(DeclAttributes &Attributes, Token next = peekToken(); auto Kind = TypeAttributes::getAttrKindFromString(next.getText()); - // noescape is only valid as a decl attribute and type attribute (in SIL - // mode) but we disambiguate it as a decl attribute. + // noescape is only valid as a decl attribute and type attribute (in + // parameter lists) but we disambiguate it as a decl attribute. if (Kind == TAK_noescape) Kind = TAK_Count; diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 0c07b54a5b3ac..9c49e22da34ed 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -424,8 +424,9 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, /// The DRE argument is a reference to a noescape parameter. Verify that /// its uses are ok. void checkNoEscapeParameterUse(DeclRefExpr *DRE, Expr *ParentExpr=nullptr) { - // This only cares about declarations marked noescape. - if (!DRE->getDecl()->getAttrs().hasAttribute()) + // This only cares about declarations of noescape function type. + auto AFT = DRE->getDecl()->getType()->getAs(); + if (!AFT || !AFT->isNoEscape()) return; // Only diagnose this once. If we check and accept this use higher up in @@ -440,7 +441,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, return; TC.diagnose(DRE->getStartLoc(), diag::invalid_noescape_use, - DRE->getDecl()->getName()); + DRE->getDecl()->getName(), isa(DRE->getDecl())); if (DRE->getDecl()->getAttrs().hasAttribute() && DRE->getDecl()->getAttrs().getAttribute()->isImplicit()) TC.diagnose(DRE->getDecl()->getLoc(), diag::noescape_autoclosure, diff --git a/test/attr/attr_noescape.swift b/test/attr/attr_noescape.swift index c8c7c3f1f6622..e91417b1dfb16 100644 --- a/test/attr/attr_noescape.swift +++ b/test/attr/attr_noescape.swift @@ -230,3 +230,66 @@ public func XCTAssert( @autoclosure expression: () -> BooleanType, _ message: St } + +/// SR-770 - Currying and `noescape`/`rethrows` don't work together anymore +func curriedFlatMap(x: [A]) -> (@noescape (A) -> [B]) -> [B] { + return { f in + x.flatMap(f) + } +} + +func curriedFlatMap2(x: [A]) -> (@noescape (A) -> [B]) -> [B] { + return { (f : @noescape (A) -> [B]) in + x.flatMap(f) + } +} + +func bad(a : (Int)-> Int) -> Int { return 42 } +func escapeNoEscapeResult(x: [Int]) -> (@noescape (Int) -> Int) -> Int { + return { f in + bad(f) // expected-error {{invalid conversion from non-escaping function of type '@noescape (Int) -> Int' to potentially escaping function type '(Int) -> Int'}} + } +} + + +// SR-824 - @noescape for Type Aliased Closures +// +typealias CompletionHandlerNE = @noescape (success: Bool) -> () +typealias CompletionHandler = (success: Bool) -> () +var escape : CompletionHandlerNE +func doThing1(@noescape completion: CompletionHandler) { + // expected-error @+2 {{@noescape value 'escape' may only be called}} + // expected-error @+1 {{@noescape parameter 'completion' may only be called}} + escape = completion +} +func doThing2(completion: CompletionHandlerNE) { + // expected-error @+2 {{@noescape value 'escape' may only be called}} + // expected-error @+1 {{@noescape parameter 'completion' may only be called}} + escape = completion +} + +// @noescape doesn't work on parameters of function type +func apply(@noescape f: T -> U, @noescape g: (@noescape T -> U) -> U) -> U { + return g(f) +} + +// @noescape cannot be applied to locals, leading to duplication of code +enum r19997577Type { + case Unit + case Function(() -> r19997577Type, () -> r19997577Type) + case Sum(() -> r19997577Type, () -> r19997577Type) + + func reduce(initial: Result, @noescape _ combine: (Result, r19997577Type) -> Result) -> Result { + let binary: @noescape (r19997577Type, r19997577Type) -> Result = { combine(combine(combine(initial, self), $0), $1) } + switch self { + case Unit: + return combine(initial, self) + case let Function(t1, t2): + return binary(t1(), t2()) + case let Sum(t1, t2): + return binary(t1(), t2()) + } + } +} + +