From dad3acebd74d0ffbd0eff10b89bd5fb23cdab0f7 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Fri, 31 Oct 2025 23:33:46 +0000 Subject: [PATCH 1/2] [test] Add some extra variants in `capture_order.swift` The existing tests we have here I'm hoping to reject in Sema, add equivalent variants that use local functions which will continue to be caught in SILGen. --- test/SILGen/capture_order.swift | 137 ++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 8 deletions(-) diff --git a/test/SILGen/capture_order.swift b/test/SILGen/capture_order.swift index b957f2e03e264..d03beda59270d 100644 --- a/test/SILGen/capture_order.swift +++ b/test/SILGen/capture_order.swift @@ -224,14 +224,6 @@ func forward_declared_let_captures() { // expected-note@-3 {{captured value declared here}} } - do { - func bad_local_f() -> Any { bad } - // expected-error@-1 {{closure captures 'bad' before it is declared}} - // expected-note@-2 {{captured here}} - let bad = bad_local_f() - // expected-note@-1 {{captured value declared here}} - } - do { let bad = global_fwd { bad } // expected-error@-1 {{closure captures 'bad' before it is declared}} @@ -320,6 +312,135 @@ func forward_declared_let_captures() { } } +func forward_declared_let_captures_local_fn() { + do { + func bad_local_f() -> Any { bad } + // expected-error@-1 {{closure captures 'bad' before it is declared}} + // expected-note@-2 {{captured here}} + let bad = bad_local_f() + // expected-note@-1 {{captured value declared here}} + } + + do { + func fwd(_ i: () -> Any) -> Any { i() } + func badFn() -> Any { + // expected-error@-1 {{closure captures 'bad' before it is declared}} + fwd { bad } + // expected-note@-1 {{captured here}} + } + let bad = badFn() + // expected-note@-1 {{captured value declared here}} + } + + do { + func badFn() -> Any { + // expected-error@-1 {{closure captures 'bad' before it is declared}} + global_gen_fwd { bad } + // expected-note@-1 {{captured here}} + } + let bad = badFn() + // expected-note@-1 {{captured value declared here}} + } + + do { + func badFn() -> Any { + // expected-error@-1 {{closure captures 'bad' before it is declared}} + E.static_gen_fwd { bad } + // expected-note@-1 {{captured here}} + } + let bad = badFn() + // expected-note@-1 {{captured value declared here}} + } + + do { + func badFn() -> Any { + // expected-error@-1 {{closure captures 'badNested' before it is declared}} + global_fwd { { [badNested] in badNested }() } + // expected-note@-1 {{captured here}} + } + let badNested = badFn() + // expected-note@-1 {{captured value declared here}} + } + + do { + func badFn() -> Any? { + // expected-error@-1 {{closure captures 'badOpt' before it is declared}} + { () -> Any? in badOpt }() + // expected-note@-1 {{captured here}} + } + let badOpt = badFn() + // expected-note@-1 {{captured value declared here}} + } + + do { + func badFn() -> (Any, Any) { + // expected-error@-1 {{closure captures 'badTup' before it is declared}} + { (badTup.0, badTup.1) }() + // expected-note@-1 {{captured here}} + } + let badTup = badFn() + // expected-note@-1 {{captured value declared here}} + } + + do { + func badFn() -> (Int, Any) { + // expected-error@-1 {{closure captures 'badTup' before it is declared}} + { (badTup.0, badTup.1) }() + // expected-note@-1 {{captured here}} + } + let badTup = badFn() + // expected-note@-1 {{captured value declared here}} + } + + do { + func badFn() -> (Any, Any) { + // expected-error@-1 {{closure captures 'badTup3' before it is declared}} + // expected-error@-2 {{closure captures 'badTup4' before it is declared}} + { (badTup4, badTup3) }() + // expected-note@-1 2{{captured here}} + } + let (badTup3, badTup4) = badFn() + // expected-note@-1 2{{captured value declared here}} + } + + do { + struct S { var p: Any } + func badFn() -> S { + // expected-error@-1 {{closure captures 'badStruct' before it is declared}} + { S(p: badStruct.p) }() + // expected-note@-1 {{captured here}} + } + let badStruct = badFn() + // expected-note@-1 {{captured value declared here}} + } + + do { + enum EE { + case boring + case weird(Any) + case strange(Any) + } + + func badFn() -> EE { + // expected-error@-1 {{closure captures 'badEnum' before it is declared}} + { .weird(EE.strange(badEnum)) }() + // expected-note@-1 {{captured here}} + } + let badEnum = badFn() + // expected-note@-1 {{captured value declared here}} + } + + do { + func badFn() -> any P { + // expected-error@-1 {{closure captures 'badproto' before it is declared}} + global_fwd_p { badproto } + // expected-note@-1 {{captured here}} + } + let badproto = badFn() + // expected-note@-1 {{captured value declared here}} + } +} + // FIXME: should/could these be diagnosed instead of accepted? func forward_declared_local_lazy_captures() { // runtime stack overflow, maybe UB? From 828b10abd93aab946e7710a9181dcc64ffce47ef Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sat, 1 Nov 2025 17:38:03 +0000 Subject: [PATCH 2/2] [test] Remove a FIXME These cases are odd, but well defined. --- test/SILGen/capture_order.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/SILGen/capture_order.swift b/test/SILGen/capture_order.swift index d03beda59270d..564224e5dbfd0 100644 --- a/test/SILGen/capture_order.swift +++ b/test/SILGen/capture_order.swift @@ -441,12 +441,10 @@ func forward_declared_let_captures_local_fn() { } } -// FIXME: should/could these be diagnosed instead of accepted? func forward_declared_local_lazy_captures() { - // runtime stack overflow, maybe UB? + // runtime stack overflow lazy var infiniteRecurse: Any = { infiniteRecurse }() - // ??? not entirely sure what's going on here... - // this can be called and doesn't seem to trip the sanitizers... + // function that returns itself lazy var hmm: () -> Any = { hmm } }