Skip to content

Commit

Permalink
Merge pull request #5025 from milseman/escaping_setter
Browse files Browse the repository at this point in the history
[TypeCheckType] Setters are escaping by default only at top level.
  • Loading branch information
milseman committed Sep 28, 2016
2 parents 896fc4f + 2c5065d commit e53d0e2
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 10 deletions.
21 changes: 11 additions & 10 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1400,13 +1400,6 @@ static bool diagnoseAvailability(Type ty, IdentTypeRepr *IdType, SourceLoc Loc,
return false;
}

/// Whether the given DC is a noescape-by-default context, i.e. not a property
/// setter
static bool isDefaultNoEscapeContext(const DeclContext *DC) {
auto funcDecl = dyn_cast<FuncDecl>(DC);
return !funcDecl || !funcDecl->isSetter();
}

// Hack to apply context-specific @escaping to an AST function type.
static Type applyNonEscapingFromContext(DeclContext *DC,
Type ty,
Expand All @@ -1416,7 +1409,7 @@ static Type applyNonEscapingFromContext(DeclContext *DC,
options.contains(TR_FunctionInput) ||
options.contains(TR_ImmediateFunctionInput);

bool defaultNoEscape = isFunctionParam && isDefaultNoEscapeContext(DC);
bool defaultNoEscape = isFunctionParam;

// Desugar here
auto *funcTy = ty->castTo<FunctionType>();
Expand Down Expand Up @@ -1566,6 +1559,10 @@ bool TypeChecker::validateType(TypeLoc &Loc, DeclContext *DC,
// Raise error if we parse an IUO type in an illegal position.
checkForIllegalIUOs(*this, Loc.getTypeRepr(), options);

// Special case: in computed property setter, newValue closure is escaping
if (isa<FuncDecl>(DC) && cast<FuncDecl>(DC)->isSetter())
options |= TR_ImmediateSetterNewValue;

auto type = resolveType(Loc.getTypeRepr(), DC, options, resolver,
unsatisfiedDependency);
if (!type) {
Expand Down Expand Up @@ -1702,6 +1699,9 @@ Type TypeResolver::resolveType(TypeRepr *repr, TypeResolutionOptions options) {
options -= TR_FunctionInput;
}

bool isImmediateSetterNewValue = options.contains(TR_ImmediateSetterNewValue);
options -= TR_ImmediateSetterNewValue;

if (Context.LangOpts.DisableAvailabilityChecking)
options |= TR_AllowUnavailable;

Expand All @@ -1724,8 +1724,9 @@ Type TypeResolver::resolveType(TypeRepr *repr, TypeResolutionOptions options) {
case TypeReprKind::Function:
if (!(options & TR_SILType)) {
// Default non-escaping for closure parameters
auto result = resolveASTFunctionType(cast<FunctionTypeRepr>(repr), options);
if (result && result->is<FunctionType>())
auto result =
resolveASTFunctionType(cast<FunctionTypeRepr>(repr), options);
if (result && result->is<FunctionType>() && !isImmediateSetterNewValue)
return applyNonEscapingFromContext(DC, result, options);
return result;
}
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,9 @@ enum TypeResolutionFlags : unsigned {

/// Whether we are in a type argument for an optional
TR_ImmediateOptionalTypeArgument = 0x800000,

/// Whether we are checking the outermost type of a computed property setter's newValue
TR_ImmediateSetterNewValue = 0x1000000,
};

/// Option set describing how type resolution should work.
Expand Down
16 changes: 16 additions & 0 deletions test/IDE/print_ast_tc_decls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,22 @@ public func ParamAttrs4(a : @escaping () -> ()) {
a()
}

// Setter
// PASS_PRINT_AST: class FooClassComputed {
class FooClassComputed {

// PASS_PRINT_AST: var stored: (((Int) -> Int) -> Int)?
var stored : (((Int) -> Int) -> Int)? = nil

// PASS_PRINT_AST: var computed: ((Int) -> Int) -> Int { get set }
var computed : ((Int) -> Int) -> Int {
get { return stored! }
set { stored = newValue }
}

// PASS_PRINT_AST: }
}

// Protocol extensions

protocol ProtocolToExtend {
Expand Down
17 changes: 17 additions & 0 deletions test/SILGen/materializeForSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,23 @@ func inoutAccessOfLazyFinalClassProperty(l: inout LazyFinalClassProperty) {
increment(&l.cat)
}

// Make sure the below doesn't crash SILGen
struct FooClosure {
var computed: (((Int) -> Int) -> Int)? {
get { return stored }
set {}
}
var stored: (((Int) -> Int) -> Int)? = nil
}

// CHECK-LABEL: _TF17materializeForSet22testMaterializedSetterFT_T_
func testMaterializedSetter() {
// CHECK: function_ref @_TFV17materializeForSet10FooClosureCfT_S0_
var f = FooClosure()
// CHECK: function_ref @_TFV17materializeForSet10FooClosureg8computedGSqFFSiSiSi_
// CHECK: function_ref @_TFV17materializeForSet10FooClosures8computedGSqFFSiSiSi_
f.computed = f.computed
}

// CHECK-LABEL: sil_witness_table hidden Bill: Totalled module materializeForSet {
// CHECK: method #Totalled.total!getter.1: @_TTWV17materializeForSet4BillS_8TotalledS_FS1_g5totalSi
Expand Down
13 changes: 13 additions & 0 deletions test/attr/attr_escaping.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,16 @@ func takesVarargsOfFunctions(fns: () -> ()...) {
func takesNoEscapeFunction(fn: () -> ()) { // expected-note {{parameter 'fn' is implicitly non-escaping}}
takesVarargsOfFunctions(fns: fn) // expected-error {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
}


class FooClass {
var stored : Optional<(()->Int)->Void> = nil
var computed : (()->Int)->Void {
get { return stored! }
set(newValue) { stored = newValue } // ok
}
var computedEscaping : (@escaping ()->Int)->Void {
get { return stored! }
set(newValue) { stored = newValue } // expected-error{{cannot assign value of type '(@escaping () -> Int) -> Void' to type 'Optional<(() -> Int) -> Void>' (aka 'Optional<(() -> Int) -> ()>')}}
}
}

0 comments on commit e53d0e2

Please sign in to comment.