xref: /openbsd-src/gnu/usr.bin/perl/t/op/defer.t (revision f2a19305cfc49ea4d1a5feb55cd6c283c6f1e031)
1256a93a4Safresh1#!./perl
2256a93a4Safresh1
3256a93a4Safresh1BEGIN {
4256a93a4Safresh1    chdir 't' if -d 't';
5256a93a4Safresh1    require './test.pl';
6256a93a4Safresh1    set_up_inc('../lib');
7256a93a4Safresh1}
8256a93a4Safresh1
9*f2a19305Safresh1plan 28;
10256a93a4Safresh1
11256a93a4Safresh1use feature 'defer';
12256a93a4Safresh1no warnings 'experimental::defer';
13256a93a4Safresh1
14256a93a4Safresh1{
15256a93a4Safresh1    my $x = "";
16256a93a4Safresh1    {
17256a93a4Safresh1        defer { $x = "a" }
18256a93a4Safresh1    }
19256a93a4Safresh1    is($x, "a", 'defer block is invoked');
20256a93a4Safresh1
21256a93a4Safresh1    {
22256a93a4Safresh1        defer {
23256a93a4Safresh1            $x = "";
24256a93a4Safresh1            $x .= "abc";
25256a93a4Safresh1            $x .= "123";
26256a93a4Safresh1        }
27256a93a4Safresh1    }
28256a93a4Safresh1    is($x, "abc123", 'defer block can contain multiple statements');
29256a93a4Safresh1
30256a93a4Safresh1    {
31256a93a4Safresh1       defer {}
32256a93a4Safresh1    }
33256a93a4Safresh1    ok(1, 'Empty defer block parses OK');
34256a93a4Safresh1}
35256a93a4Safresh1
36256a93a4Safresh1{
37256a93a4Safresh1    my $x = "";
38256a93a4Safresh1    {
39256a93a4Safresh1        defer { $x .= "a" }
40256a93a4Safresh1        defer { $x .= "b" }
41256a93a4Safresh1        defer { $x .= "c" }
42256a93a4Safresh1    }
43256a93a4Safresh1    is($x, "cba", 'defer blocks happen in LIFO order');
44256a93a4Safresh1}
45256a93a4Safresh1
46256a93a4Safresh1{
47256a93a4Safresh1    my $x = "";
48256a93a4Safresh1
49256a93a4Safresh1    {
50256a93a4Safresh1        defer { $x .= "a" }
51256a93a4Safresh1        $x .= "A";
52256a93a4Safresh1    }
53256a93a4Safresh1
54256a93a4Safresh1    is($x, "Aa", 'defer blocks happen after the main body');
55256a93a4Safresh1}
56256a93a4Safresh1
57256a93a4Safresh1{
58256a93a4Safresh1    my $x = "";
59256a93a4Safresh1
60256a93a4Safresh1    foreach my $i (qw( a b c )) {
61256a93a4Safresh1        defer { $x .= $i }
62256a93a4Safresh1    }
63256a93a4Safresh1
64256a93a4Safresh1    is($x, "abc", 'defer block happens for every iteration of foreach');
65256a93a4Safresh1}
66256a93a4Safresh1
67256a93a4Safresh1{
68256a93a4Safresh1    my $x = "";
69256a93a4Safresh1
70256a93a4Safresh1    my $cond = 0;
71256a93a4Safresh1    if( $cond ) {
72256a93a4Safresh1        defer { $x .= "XXX" }
73256a93a4Safresh1    }
74256a93a4Safresh1
75256a93a4Safresh1    is($x, "", 'defer block does not happen inside non-taken conditional branch');
76256a93a4Safresh1}
77256a93a4Safresh1
78256a93a4Safresh1{
79256a93a4Safresh1    my $x = "";
80256a93a4Safresh1
81256a93a4Safresh1    while(1) {
82256a93a4Safresh1        last;
83256a93a4Safresh1        defer { $x .= "a" }
84256a93a4Safresh1    }
85256a93a4Safresh1
86256a93a4Safresh1    is($x, "", 'defer block does not happen if entered but unencountered');
87256a93a4Safresh1}
88256a93a4Safresh1
89256a93a4Safresh1{
90256a93a4Safresh1   my $x = "";
91256a93a4Safresh1
92256a93a4Safresh1   my $counter = 1;
93256a93a4Safresh1   {
94256a93a4Safresh1      defer { $x .= "A" }
95256a93a4Safresh1      redo if $counter++ < 5;
96256a93a4Safresh1   }
97256a93a4Safresh1
98256a93a4Safresh1   is($x, "AAAAA", 'defer block can happen multiple times');
99256a93a4Safresh1}
100256a93a4Safresh1
101256a93a4Safresh1{
102256a93a4Safresh1    my $x = "";
103256a93a4Safresh1
104256a93a4Safresh1    {
105256a93a4Safresh1        defer {
106256a93a4Safresh1            $x .= "a";
107256a93a4Safresh1            defer {
108256a93a4Safresh1                $x .= "b";
109256a93a4Safresh1            }
110256a93a4Safresh1        }
111256a93a4Safresh1    }
112256a93a4Safresh1
113256a93a4Safresh1    is($x, "ab", 'defer block can contain another defer');
114256a93a4Safresh1}
115256a93a4Safresh1
116256a93a4Safresh1{
117256a93a4Safresh1    my $x = "";
118256a93a4Safresh1    my $value = do {
119256a93a4Safresh1        defer { $x .= "before" }
120256a93a4Safresh1        "value";
121256a93a4Safresh1    };
122256a93a4Safresh1
123256a93a4Safresh1    is($x, "before", 'defer blocks run inside do { }');
124256a93a4Safresh1    is($value, "value", 'defer block does not disturb do { } value');
125256a93a4Safresh1}
126256a93a4Safresh1
127256a93a4Safresh1{
128256a93a4Safresh1    my $x = "";
129256a93a4Safresh1    my $sub = sub {
130256a93a4Safresh1        defer { $x .= "a" }
131256a93a4Safresh1    };
132256a93a4Safresh1
133256a93a4Safresh1    $sub->();
134256a93a4Safresh1    $sub->();
135256a93a4Safresh1    $sub->();
136256a93a4Safresh1
137256a93a4Safresh1    is($x, "aaa", 'defer block inside sub');
138256a93a4Safresh1}
139256a93a4Safresh1
140256a93a4Safresh1{
141256a93a4Safresh1    my $x = "";
142256a93a4Safresh1    my $sub = sub {
143256a93a4Safresh1        return;
144256a93a4Safresh1        defer { $x .= "a" }
145256a93a4Safresh1    };
146256a93a4Safresh1
147256a93a4Safresh1    $sub->();
148256a93a4Safresh1
149256a93a4Safresh1    is($x, "", 'defer block inside sub does not happen if entered but returned early');
150256a93a4Safresh1}
151256a93a4Safresh1
152256a93a4Safresh1{
153256a93a4Safresh1   my $x = "";
154256a93a4Safresh1
155256a93a4Safresh1   my sub after {
156256a93a4Safresh1      $x .= "c";
157256a93a4Safresh1   }
158256a93a4Safresh1
159256a93a4Safresh1   my sub before {
160256a93a4Safresh1      $x .= "a";
161256a93a4Safresh1      defer { $x .= "b" }
162256a93a4Safresh1      goto \&after;
163256a93a4Safresh1   }
164256a93a4Safresh1
165256a93a4Safresh1   before();
166256a93a4Safresh1
167256a93a4Safresh1   is($x, "abc", 'defer block invoked before tail-call');
168256a93a4Safresh1}
169256a93a4Safresh1
170256a93a4Safresh1# Sequencing with respect to variable cleanup
171256a93a4Safresh1
172256a93a4Safresh1{
173256a93a4Safresh1    my $var = "outer";
174256a93a4Safresh1    my $x;
175256a93a4Safresh1    {
176256a93a4Safresh1        my $var = "inner";
177256a93a4Safresh1        defer { $x = $var }
178256a93a4Safresh1    }
179256a93a4Safresh1
180256a93a4Safresh1    is($x, "inner", 'defer block captures live value of same-scope lexicals');
181256a93a4Safresh1}
182256a93a4Safresh1
183256a93a4Safresh1{
184256a93a4Safresh1    my $var = "outer";
185256a93a4Safresh1    my $x;
186256a93a4Safresh1    {
187256a93a4Safresh1        defer { $x = $var }
188256a93a4Safresh1        my $var = "inner";
189256a93a4Safresh1    }
190256a93a4Safresh1
191256a93a4Safresh1    is ($x, "outer", 'defer block correctly captures outer lexical when only shadowed afterwards');
192256a93a4Safresh1}
193256a93a4Safresh1
194256a93a4Safresh1{
195256a93a4Safresh1    our $var = "outer";
196256a93a4Safresh1    {
197256a93a4Safresh1        local $var = "inner";
198256a93a4Safresh1        defer { $var = "finally" }
199256a93a4Safresh1    }
200256a93a4Safresh1
201256a93a4Safresh1    is($var, "outer", 'defer after localization still unlocalizes');
202256a93a4Safresh1}
203256a93a4Safresh1
204256a93a4Safresh1{
205256a93a4Safresh1    our $var = "outer";
206256a93a4Safresh1    {
207256a93a4Safresh1        defer { $var = "finally" }
208256a93a4Safresh1        local $var = "inner";
209256a93a4Safresh1    }
210256a93a4Safresh1
211256a93a4Safresh1    is($var, "finally", 'defer before localization overwrites');
212256a93a4Safresh1}
213256a93a4Safresh1
214256a93a4Safresh1# Interactions with exceptions
215256a93a4Safresh1
216256a93a4Safresh1{
217256a93a4Safresh1    my $x = "";
218256a93a4Safresh1    my $sub = sub {
219256a93a4Safresh1        defer { $x .= "a" }
220256a93a4Safresh1        die "Oopsie\n";
221256a93a4Safresh1    };
222256a93a4Safresh1
223256a93a4Safresh1    my $e = defined eval { $sub->(); 1 } ? undef : $@;
224256a93a4Safresh1
225256a93a4Safresh1    is($x, "a", 'defer block still runs during exception unwind');
226256a93a4Safresh1    is($e, "Oopsie\n", 'Thrown exception still occurs after defer');
227256a93a4Safresh1}
228256a93a4Safresh1
229256a93a4Safresh1{
230256a93a4Safresh1    my $sub = sub {
231256a93a4Safresh1        defer { die "Oopsie\n"; }
232256a93a4Safresh1        return "retval";
233256a93a4Safresh1    };
234256a93a4Safresh1
235256a93a4Safresh1    my $e = defined eval { $sub->(); 1 } ? undef : $@;
236256a93a4Safresh1
237256a93a4Safresh1    is($e, "Oopsie\n", 'defer block can throw exception');
238256a93a4Safresh1}
239256a93a4Safresh1
240256a93a4Safresh1{
241256a93a4Safresh1    my $sub = sub {
242256a93a4Safresh1        defer { die "Oopsie 1\n"; }
243256a93a4Safresh1        die "Oopsie 2\n";
244256a93a4Safresh1    };
245256a93a4Safresh1
246256a93a4Safresh1    my $e = defined eval { $sub->(); 1 } ? undef : $@;
247256a93a4Safresh1
248256a93a4Safresh1    # TODO: Currently the first exception gets lost without even a warning
249256a93a4Safresh1    #   We should consider what the behaviour ought to be here
250256a93a4Safresh1    # This test is happy for either exception to be seen, does not care which
251256a93a4Safresh1    like($e, qr/^Oopsie \d\n/, 'defer block can throw exception during exception unwind');
252256a93a4Safresh1}
253256a93a4Safresh1
254*f2a19305Safresh1# goto
255256a93a4Safresh1{
256*f2a19305Safresh1    ok(defined eval 'sub { defer { goto HERE; HERE: 1; } }',
257*f2a19305Safresh1        'goto forwards within defer {} is permitted') or
258*f2a19305Safresh1        diag("Failure was $@");
259256a93a4Safresh1
260*f2a19305Safresh1    ok(defined eval 'sub { defer { HERE: 1; goto HERE; } }',
261*f2a19305Safresh1        'goto backwards within defer {} is permitted') or
262*f2a19305Safresh1        diag("Failure was $@");
263256a93a4Safresh1}
264256a93a4Safresh1
265256a93a4Safresh1{
266256a93a4Safresh1    my $sub = sub {
267256a93a4Safresh1        while(1) {
268256a93a4Safresh1            goto HERE;
269256a93a4Safresh1            defer { HERE: 1; }
270256a93a4Safresh1        }
271256a93a4Safresh1    };
272256a93a4Safresh1
273256a93a4Safresh1    my $e = defined eval { $sub->(); 1 } ? undef : $@;
274256a93a4Safresh1    like($e, qr/^Can't "goto" into a "defer" block /,
275256a93a4Safresh1        'Cannot goto into defer block');
276256a93a4Safresh1}
277256a93a4Safresh1
278256a93a4Safresh1{
279256a93a4Safresh1    # strictness failures are only checked at optree finalization time. This
280256a93a4Safresh1    # is a good way to test if that happens.
281256a93a4Safresh1    my $ok = eval 'defer { use strict; foo }';
282256a93a4Safresh1    my $e = $@;
283256a93a4Safresh1
284256a93a4Safresh1    ok(!$ok, 'defer BLOCK finalizes optree');
285256a93a4Safresh1    like($e, qr/^Bareword "foo" not allowed while "strict subs" in use at /,
286256a93a4Safresh1        'Error from finalization');
287256a93a4Safresh1}
288