Colons invalidate BEGIN and END blocks

| 4 Comments

Take a look at this block of code and see if you can guess what it does.

END: {
    print "Exiting...\n";
}

print "s = $s\n";

BEGIN: {
    $s = 'Hello from the BEGIN phase';
}

You'd expect that the BEGIN block sets $s at compile time, so then that gets printed in the print statement, and then the END block executes at the end of the program. Yes, it would, except that those aren't BEGIN & END blocks. They're normal blocks that just happen to be named BEGIN and END.

Credit Randy Lauen for bringing this to my attention. Today at my day job he found a horrifying code construct, one that I'd been using (incorrectly) for quite some time now. You see, these two blocks are not the same:

BEGIN {
    # Here's some code that gets executed at compile time
}

BEGIN: {
    # This code gets executed at run time, because the colon
    # after BEGIN means it's just a plain old label
}

Randy had been tracking down a bug for over an hour and couldn't figure out why his variables weren't getting set at compile time. Turns out the BEGIN block he was modifying was actually a not-special-at-all BEGIN: block. And he found 40+ instances of the spurious colon in the codebase. Of course, INIT, CHECK, UNITCHECK and END are also rendered non-special by a colon as well.

Most of the time, this is invisible. People tend to put their BEGIN blocks at the top of the program, so the difference between a real BEGIN block and a block that happens to be named BEGIN are not noticed. Still, it's a a bug waiting to happen. Sounds like an ideal check for Perl::Critic policy, no? Mike O'Regan jumped to it, and submitted a patch to Perl::Critic, so those of you using Perl::Critic (and if you're not, you should be) can get warned of using colons on those specially-named blocks once a new version is released, shortly I hope.

I'm not the only one to have been doing this. Google Code Search turns up over 100 instances, including some in the core Perl tests (heck, maybe from me!), as well as mailman and POE.

Check your codebases. If you're using ack, it's just:

$ ack --perl '^(BEGIN|END|INIT|CHECK|UNITCHECK):\s*{'

Have you fallen prey to this? Let me know.

4 Comments

Maybe 'use warnings' should issue a warning for these?

just a quick note, the Perl::Critic link above should be perlcritic.tigris.org not .com

Fixed in POE::Component::IRC. Thanks. :)

Keep in mind whitespace between the label and the colon:


$ ack --perl '^(BEGIN|END|INIT|CHECK|UNITCHECK)\s*:\s*{'

Leave a comment

Job hunting for programmers


Land the Tech Job You Love, Andy Lester's guide to job hunting for programmers and other technical professionals, is available in PDF, ePub and .mobi formats, all DRM-free, as well as good old-fashioned paper.