Skip to content

Commit

Permalink
Generalize the @NoEscape attribute to be a type attribute allowed
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lattner committed Mar 3, 2016
1 parent ceb1069 commit c3c6bea
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 11 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, U>(@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.

Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
8 changes: 2 additions & 6 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
7 changes: 4 additions & 3 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<NoEscapeAttr>())
// This only cares about declarations of noescape function type.
auto AFT = DRE->getDecl()->getType()->getAs<AnyFunctionType>();
if (!AFT || !AFT->isNoEscape())
return;

// Only diagnose this once. If we check and accept this use higher up in
Expand All @@ -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<ParamDecl>(DRE->getDecl()));
if (DRE->getDecl()->getAttrs().hasAttribute<AutoClosureAttr>() &&
DRE->getDecl()->getAttrs().getAttribute<NoEscapeAttr>()->isImplicit())
TC.diagnose(DRE->getDecl()->getLoc(), diag::noescape_autoclosure,
Expand Down
63 changes: 63 additions & 0 deletions test/attr/attr_noescape.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<A, B>(x: [A]) -> (@noescape (A) -> [B]) -> [B] {
return { f in
x.flatMap(f)
}
}

func curriedFlatMap2<A, B>(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
}

// <rdar://problem/19997680> @noescape doesn't work on parameters of function type
func apply<T, U>(@noescape f: T -> U, @noescape g: (@noescape T -> U) -> U) -> U {
return g(f)
}

// <rdar://problem/19997577> @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<Result>(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())
}
}
}


0 comments on commit c3c6bea

Please sign in to comment.