Skip to content

Commit 228112f

Browse files
authored
Merge pull request #36737 from slavapestov/nil-default-arg-rethrows
Sema: Teach rethrows checking about 'nil' default arguments
2 parents db97d27 + ef2995b commit 228112f

File tree

8 files changed

+69
-6
lines changed

8 files changed

+69
-6
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@ CHANGELOG
2929
Swift 5.5
3030
---------
3131

32+
* The determination of whether a call to a `rethrows` function can throw now considers default arguments of `Optional` type.
33+
34+
In Swift 5.4, such default arguments were ignored entirely by `rethrows` checking. This meant that the following example was accepted:
35+
36+
```swift
37+
func foo(_: (() throws -> ())? = nil) rethrows {}
38+
foo() // no 'try' needed
39+
```
40+
41+
However, it also meant that the following was accepted, even though the call to `foo()` can throw and the call site is not marked with `try`:
42+
43+
```swift
44+
func foo(_: (() throws -> ())? = { throw myError }) rethrows {}
45+
foo() // 'try' *should* be required here
46+
```
47+
48+
The new behavior is that the first example is accepted because the default argument is syntactically written as `nil`, which is known not to throw. The second example is correctly rejected, on account of missing a `try` since the default argument *can* throw.
49+
3250
* [SE-0293][]:
3351

3452
Property wrappers can now be applied to function and closure parameters:

lib/Parse/ParsePattern.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ static DefaultArgumentKind getDefaultArgKind(Expr *init) {
3939
if (!init)
4040
return DefaultArgumentKind::None;
4141

42+
// Parse an as-written 'nil' expression as the special NilLiteral kind,
43+
// which is emitted by the caller and can participate in rethrows
44+
// checking.
45+
if (isa<NilLiteralExpr>(init))
46+
return DefaultArgumentKind::NilLiteral;
47+
4248
auto magic = dyn_cast<MagicIdentifierLiteralExpr>(init);
4349
if (!magic)
4450
return DefaultArgumentKind::Normal;

lib/Sema/TypeCheckEffects.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1244,7 +1244,16 @@ class ApplyClassifier {
12441244
Classification classifyArgument(Expr *arg, Type paramType, EffectKind kind) {
12451245
arg = arg->getValueProvidingExpr();
12461246

1247-
if (isa<DefaultArgumentExpr>(arg)) {
1247+
if (auto *defaultArg = dyn_cast<DefaultArgumentExpr>(arg)) {
1248+
// Special-case a 'nil' default argument, which is known not to throw.
1249+
if (defaultArg->isCallerSide()) {
1250+
auto *callerSideArg = defaultArg->getCallerSideDefaultExpr();
1251+
if (isa<NilLiteralExpr>(callerSideArg)) {
1252+
if (callerSideArg->getType()->getOptionalObjectType())
1253+
return Classification();
1254+
}
1255+
}
1256+
12481257
return classifyArgumentByType(arg->getType(),
12491258
PotentialEffectReason::forDefaultClosure(),
12501259
kind);

test/SILGen/constrained_extensions.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ extension Array where Element == Int {
4444

4545
// CHECK-LABEL: sil non_abi [serialized] [ossa] @$sSa22constrained_extensionsSiRszlE12staticMethod1eS2iSg_tFZfA_ : $@convention(thin) () -> Optional<Int>
4646
// CHECK-LABEL: sil [ossa] @$sSa22constrained_extensionsSiRszlE12staticMethod1eS2iSg_tFZ : $@convention(method) (Optional<Int>, @thin Array<Int>.Type) -> Int
47-
public static func staticMethod(e: Element? = nil) -> Element {
47+
public static func staticMethod(e: Element? = (nil)) -> Element {
4848
return e!
4949
}
5050

@@ -104,7 +104,7 @@ extension Dictionary where Key == Int {
104104
// CHECK-LABEL: sil non_abi [serialized] [ossa] @$sSD22constrained_extensionsSiRszrlE12staticMethod1k1vq_SiSg_q_SgtFZfA_ : $@convention(thin) <Key, Value where Key == Int> () -> Optional<Int>
105105
// CHECK-LABEL: sil non_abi [serialized] [ossa] @$sSD22constrained_extensionsSiRszrlE12staticMethod1k1vq_SiSg_q_SgtFZfA0_ : $@convention(thin) <Key, Value where Key == Int> () -> @out Optional<Value>
106106
// CHECK-LABEL: sil [ossa] @$sSD22constrained_extensionsSiRszrlE12staticMethod1k1vq_SiSg_q_SgtFZ : $@convention(method) <Key, Value where Key == Int> (Optional<Int>, @in_guaranteed Optional<Value>, @thin Dictionary<Int, Value>.Type) -> @out Value
107-
public static func staticMethod(k: Key? = nil, v: Value? = nil) -> Value {
107+
public static func staticMethod(k: Key? = (nil), v: Value? = (nil)) -> Value {
108108
return v!
109109
}
110110

@@ -191,7 +191,7 @@ extension Array where Element == Int {
191191

192192
// CHECK-LABEL: sil hidden [ossa] @$sSa22constrained_extensionsSiRszlE6NestedV10hasDefault1eySiSg_tFfA_ : $@convention(thin) () -> Optional<Int>
193193
// CHECK-LABEL: sil hidden [ossa] @$sSa22constrained_extensionsSiRszlE6NestedV10hasDefault1eySiSg_tF : $@convention(method) (Optional<Int>, @inout Array<Int>.Nested) -> ()
194-
mutating func hasDefault(e: Element? = nil) {
194+
mutating func hasDefault(e: Element? = (nil)) {
195195
self.e = e
196196
}
197197
}

test/SILGen/default_arguments.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ func test_r18400194() {
284284
// Don't add capture arguments to local default argument generators.
285285
func localFunctionWithDefaultArg() {
286286
var z = 5
287-
func bar(_ x: Int? = nil) {
287+
func bar(_ x: Int? = (nil)) {
288288
z += 1
289289
}
290290
bar()

test/SILGen/default_arguments_inherited.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// derived class generic signature.
88

99
class Puppy<T, U> {
10-
init(t: T? = nil, u: U? = nil) {}
10+
init(t: T? = (nil), u: U? = (nil)) {}
1111
}
1212

1313
class Chipmunk : Puppy<Int, String> {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
3+
public func takesAnInt(_: Int? = nil) {}
4+
5+
// CHECK-LABEL: sil [ossa] @$s21default_arguments_nil15callsTakesAnIntyyF : $@convention(thin) () -> () {
6+
// CHECK: [[NIL:%.*]] = enum $Optional<Int>, #Optional.none!enumelt
7+
// CHECK: [[FN:%.*]] = function_ref @$s21default_arguments_nil10takesAnIntyySiSgF
8+
// CHECK: apply [[FN]]([[NIL]]) : $@convention(thin) (Optional<Int>) -> ()
9+
// CHECK: return
10+
public func callsTakesAnInt() {
11+
takesAnInt()
12+
}

test/decl/func/rethrows.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,3 +628,21 @@ func throwableDefaultRethrows(_ f: () throws -> () = { throw SomeError.Badness }
628628
// This should always emit a diagnostic because we can statically know that default argument can throw.
629629
throwableDefaultRethrows() // expected-error {{call can throw but is not marked with 'try'}}
630630
// expected-note@-1 {{call is to 'rethrows' function, but a defaulted argument function can throw}}
631+
632+
// rdar://76169080 - rethrows -vs- Optional default arguments
633+
func optionalRethrowsDefaultArg1(_: (() throws -> ())? = nil) rethrows {}
634+
635+
func callsOptionalRethrowsDefaultArg1() throws {
636+
optionalRethrowsDefaultArg1()
637+
optionalRethrowsDefaultArg1(nil)
638+
try optionalRethrowsDefaultArg1 { throw SomeError.Badness }
639+
}
640+
641+
func optionalRethrowsDefaultArg2(_: (() throws -> ())? = { throw SomeError.Badness }) rethrows {}
642+
643+
func callsOptionalRethrowsDefaultArg2() throws {
644+
optionalRethrowsDefaultArg2() // expected-error {{call can throw but is not marked with 'try'}}
645+
// expected-note@-1 {{call is to 'rethrows' function, but a defaulted argument function can throw}}
646+
optionalRethrowsDefaultArg2(nil)
647+
try optionalRethrowsDefaultArg2 { throw SomeError.Badness }
648+
}

0 commit comments

Comments
 (0)