Skip to content

Commit eed34d1

Browse files
authored
Sema: Explicitly allow Optional-to-IUO when converting functions. (#7153)
We limit Optional-to-IUO as an implicit conversion to avoid making common expressions ambiguous. However, this runs into trouble with function-to-function conversions, which always look for a "Subtype" relationship for their inputs and outputs. This is a conservative way for Sema to avoid emitting conversions that SILGen cannot handle. The problem case here is converting a closure with type '(Any!) -> Void' to a value of type '(Any?) -> Void': let f: ((Any?) -> Void) = { (arg: Any!) in } This is clearly a safe conversion, since 'Any!' and 'Any?' have the same representation at run time, but historically we've disallowed it because of the above rules. However, in Swift 3.0 this particular case was permitted, with the type checker deciding that the 'Any?' argument to 'f' could first itself be put into an 'Any', then /that/ value could go through a value-to-optional conversion to make 'Any!'. Fortunately the emitted code didn't follow this incorrect conversion path. This patch doesn't even try to emulate the old behavior. Instead, it just weakens the restriction on Optional-to-IUO via a new type matching flag that only applies within the context of matching function types. We can consider opening up function conversions in Swift 4 to anything that supports conversion---not just subtyping---now that SILGen knows how to automatically reabstract most such things. But whether we do or not, Optional/IUO is a special case. https://bugs.swift.org/browse/SR-3758
1 parent bcdb4cf commit eed34d1

File tree

3 files changed

+22
-4
lines changed

3 files changed

+22
-4
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,7 @@ static bool isPotentiallyMoreOptionalThan(Type objType1,
12321232
/// Enumerate all of the applicable optional conversion restrictions
12331233
static void enumerateOptionalConversionRestrictions(
12341234
Type type1, Type type2,
1235-
ConstraintKind kind,
1235+
ConstraintKind kind, ConstraintLocatorBuilder locator,
12361236
llvm::function_ref<void(ConversionRestrictionKind)> fn) {
12371237
SmallVector<Type, 2> optionals1;
12381238
Type objType1 = type1->lookThroughAllAnyOptionalTypes(optionals1);
@@ -1251,6 +1251,7 @@ static void enumerateOptionalConversionRestrictions(
12511251
// Break cyclic conversions between T? and U! by only allowing it for
12521252
// conversion constraints.
12531253
if (kind >= ConstraintKind::Conversion ||
1254+
locator.isFunctionConversion() ||
12541255
!(optionalKind1 == OTK_Optional &&
12551256
optionalKind2 == OTK_ImplicitlyUnwrappedOptional))
12561257
fn(ConversionRestrictionKind::OptionalToOptional);
@@ -2055,9 +2056,10 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
20552056
// A value of type T, T?, or T! can be converted to type U? or U! if
20562057
// T is convertible to U.
20572058
if (concrete && kind >= ConstraintKind::Subtype) {
2058-
enumerateOptionalConversionRestrictions(type1, type2, kind,
2059-
[&](ConversionRestrictionKind restriction) {
2060-
conversionsOrFixes.push_back(restriction);
2059+
enumerateOptionalConversionRestrictions(
2060+
type1, type2, kind, locator,
2061+
[&](ConversionRestrictionKind restriction) {
2062+
conversionsOrFixes.push_back(restriction);
20612063
});
20622064
}
20632065

test/Constraints/closures.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,8 @@ func sr3497_unfold<A, B>(_ a0: A, next: (inout A) -> B) {}
437437
func sr3497() {
438438
let _ = sr3497_unfold((0, 0)) { s in 0 } // ok
439439
}
440+
441+
// SR-3758: Swift 3.1 fails to compile 3.0 code involving closures and IUOs
442+
let _: ((Any?) -> Void) = { (arg: Any!) in }
443+
// This example was rejected in 3.0 as well, but accepting it is correct.
444+
let _: ((Int?) -> Void) = { (arg: Int!) in }

test/SILGen/implicitly_unwrapped_optional.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,14 @@ func return_any() -> AnyObject! { return nil }
5959
func bind_any() {
6060
let object : AnyObject? = return_any()
6161
}
62+
63+
// CHECK-LABEL: sil hidden @_T029implicitly_unwrapped_optional6sr3758yyF
64+
func sr3758() {
65+
// Verify that there are no additional reabstractions introduced.
66+
// CHECK: [[CLOSURE:%.+]] = function_ref @_T029implicitly_unwrapped_optional6sr3758yyFySQyypGcfU_ : $@convention(thin) (@in Optional<Any>) -> ()
67+
// CHECK: [[F:%.+]] = thin_to_thick_function [[CLOSURE]] : $@convention(thin) (@in Optional<Any>) -> () to $@callee_owned (@in Optional<Any>) -> ()
68+
// CHECK: [[CALLEE:%.+]] = copy_value [[F]] : $@callee_owned (@in Optional<Any>) -> ()
69+
// CHECK: = apply [[CALLEE]]({{%.+}}) : $@callee_owned (@in Optional<Any>) -> ()
70+
let f: ((Any?) -> Void) = { (arg: Any!) in }
71+
f(nil)
72+
} // CHECK: end sil function '_T029implicitly_unwrapped_optional6sr3758yyF'

0 commit comments

Comments
 (0)