This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Change docs display for PERL_UNUSED_foo
[perl5.git] / regen / warnings.pl
index 79be71f..99a765a 100644 (file)
 #
 # This script is normally invoked from regen.pl.
 
-$VERSION = '1.03';
+$VERSION = '1.48';
 
 BEGIN {
-    require 'regen/regen_lib.pl';
+    require './regen/regen_lib.pl';
     push @INC, './lib';
 }
 use strict ;
@@ -28,90 +28,106 @@ sub DEFAULT_ON  () { 1 }
 sub DEFAULT_OFF () { 2 }
 
 my $tree = {
-
 'all' => [ 5.008, {
-       'io'            => [ 5.008, {
-                               'pipe'          => [ 5.008, DEFAULT_OFF],
-                                       'unopened'      => [ 5.008, DEFAULT_OFF],
-                                       'closed'        => [ 5.008, DEFAULT_OFF],
-                                       'newline'       => [ 5.008, DEFAULT_OFF],
-                                       'exec'          => [ 5.008, DEFAULT_OFF],
-                                       'layer'         => [ 5.008, DEFAULT_OFF],
-                               'syscalls'      => [ 5.019, DEFAULT_OFF],
-                          }],
-       'syntax'        => [ 5.008, {
-                               'ambiguous'     => [ 5.008, DEFAULT_OFF],
-                               'semicolon'     => [ 5.008, DEFAULT_OFF],
-                               'precedence'    => [ 5.008, DEFAULT_OFF],
-                               'bareword'      => [ 5.008, DEFAULT_OFF],
-                               'reserved'      => [ 5.008, DEFAULT_OFF],
-                               'digit'         => [ 5.008, DEFAULT_OFF],
-                               'parenthesis'   => [ 5.008, DEFAULT_OFF],
-                                       'printf'        => [ 5.008, DEFAULT_OFF],
-                                       'prototype'     => [ 5.008, DEFAULT_OFF],
-                                       'qw'            => [ 5.008, DEFAULT_OFF],
+        'io'            => [ 5.008, {
+                                'pipe'          => [ 5.008, DEFAULT_OFF],
+                                'unopened'      => [ 5.008, DEFAULT_OFF],
+                                'closed'        => [ 5.008, DEFAULT_OFF],
+                                'newline'       => [ 5.008, DEFAULT_OFF],
+                                'exec'          => [ 5.008, DEFAULT_OFF],
+                                'layer'         => [ 5.008, DEFAULT_OFF],
+                                'syscalls'      => [ 5.019, DEFAULT_OFF],
+                           }],
+        'syntax'        => [ 5.008, {
+                                'ambiguous'     => [ 5.008, DEFAULT_OFF],
+                                'semicolon'     => [ 5.008, DEFAULT_OFF],
+                                'precedence'    => [ 5.008, DEFAULT_OFF],
+                                'bareword'      => [ 5.008, DEFAULT_OFF],
+                                'reserved'      => [ 5.008, DEFAULT_OFF],
+                                'digit'         => [ 5.008, DEFAULT_OFF],
+                                'parenthesis'   => [ 5.008, DEFAULT_OFF],
+                                'printf'        => [ 5.008, DEFAULT_OFF],
+                                'prototype'     => [ 5.008, DEFAULT_OFF],
+                                'qw'            => [ 5.008, DEFAULT_OFF],
                                 'illegalproto'  => [ 5.011, DEFAULT_OFF],
-                          }],
-               'severe'        => [ 5.008, {
-                               'inplace'       => [ 5.008, DEFAULT_ON],
-                               'internal'      => [ 5.008, DEFAULT_OFF],
-                               'debugging'     => [ 5.008, DEFAULT_ON],
-                               'malloc'        => [ 5.008, DEFAULT_ON],
-                          }],
-        'deprecated'   => [ 5.008, DEFAULT_ON],
-               'void'          => [ 5.008, DEFAULT_OFF],
-               'recursion'     => [ 5.008, DEFAULT_OFF],
-               'redefine'      => [ 5.008, DEFAULT_OFF],
-               'numeric'       => [ 5.008, DEFAULT_OFF],
-        'uninitialized'        => [ 5.008, DEFAULT_OFF],
-               'once'          => [ 5.008, DEFAULT_OFF],
-               'misc'          => [ 5.008, DEFAULT_OFF],
-               'regexp'        => [ 5.008, DEFAULT_OFF],
-               'glob'          => [ 5.008, DEFAULT_ON],
-               'untie'         => [ 5.008, DEFAULT_OFF],
-       'substr'        => [ 5.008, DEFAULT_OFF],
-       'taint'         => [ 5.008, DEFAULT_OFF],
-       'signal'        => [ 5.008, DEFAULT_OFF],
-       'closure'       => [ 5.008, DEFAULT_OFF],
-       'overflow'      => [ 5.008, DEFAULT_OFF],
-       'portable'      => [ 5.008, DEFAULT_OFF],
-       'utf8'          => [ 5.008, {
+                           }],
+        'severe'        => [ 5.008, {
+                                'inplace'       => [ 5.008, DEFAULT_ON],
+                                'internal'      => [ 5.008, DEFAULT_OFF],
+                                'debugging'     => [ 5.008, DEFAULT_ON],
+                                'malloc'        => [ 5.008, DEFAULT_ON],
+                           }],
+        'deprecated'    => [ 5.008, DEFAULT_ON],
+        'void'          => [ 5.008, DEFAULT_OFF],
+        'recursion'     => [ 5.008, DEFAULT_OFF],
+        'redefine'      => [ 5.008, DEFAULT_OFF],
+        'numeric'       => [ 5.008, DEFAULT_OFF],
+        'uninitialized' => [ 5.008, DEFAULT_OFF],
+        'once'          => [ 5.008, DEFAULT_OFF],
+        'misc'          => [ 5.008, DEFAULT_OFF],
+        'regexp'        => [ 5.008, DEFAULT_OFF],
+        'glob'          => [ 5.008, DEFAULT_ON],
+        'untie'         => [ 5.008, DEFAULT_OFF],
+        'substr'        => [ 5.008, DEFAULT_OFF],
+        'taint'         => [ 5.008, DEFAULT_OFF],
+        'signal'        => [ 5.008, DEFAULT_OFF],
+        'closure'       => [ 5.008, DEFAULT_OFF],
+        'overflow'      => [ 5.008, DEFAULT_OFF],
+        'portable'      => [ 5.008, DEFAULT_OFF],
+        'utf8'          => [ 5.008, {
                                 'surrogate' => [ 5.013, DEFAULT_OFF],
                                 'nonchar' => [ 5.013, DEFAULT_OFF],
                                 'non_unicode' => [ 5.013, DEFAULT_OFF],
                         }],
-               'exiting'       => [ 5.008, DEFAULT_OFF],
-               'pack'          => [ 5.008, DEFAULT_OFF],
-               'unpack'        => [ 5.008, DEFAULT_OFF],
-               'threads'       => [ 5.008, DEFAULT_OFF],
-               'imprecision'   => [ 5.011, DEFAULT_OFF],
-               'experimental'  => [ 5.017, {
+        'exiting'       => [ 5.008, DEFAULT_OFF],
+        'pack'          => [ 5.008, DEFAULT_OFF],
+        'unpack'        => [ 5.008, DEFAULT_OFF],
+        'threads'       => [ 5.008, DEFAULT_OFF],
+        'imprecision'   => [ 5.011, DEFAULT_OFF],
+        'experimental'  => [ 5.017, {
                                 'experimental::lexical_subs' =>
                                     [ 5.017, DEFAULT_ON ],
                                 'experimental::regex_sets' =>
                                     [ 5.017, DEFAULT_ON ],
-                                'experimental::lexical_topic' =>
-                                    [ 5.017, DEFAULT_ON ],
                                 'experimental::smartmatch' =>
                                     [ 5.017, DEFAULT_ON ],
                                 'experimental::postderef' =>
                                     [ 5.019, DEFAULT_ON ],
-                                'experimental::autoderef' =>
-                                    [ 5.019, DEFAULT_ON ],
                                 'experimental::signatures' =>
                                     [ 5.019, DEFAULT_ON ],
                                 'experimental::win32_perlio' =>
                                     [ 5.021, DEFAULT_ON ],
-                                'experimental::lvalue_refs' =>
+                                'experimental::refaliasing' =>
+                                    [ 5.021, DEFAULT_ON ],
+                                'experimental::re_strict' =>
+                                    [ 5.021, DEFAULT_ON ],
+                                'experimental::const_attr' =>
+                                    [ 5.021, DEFAULT_ON ],
+                                'experimental::bitwise' =>
                                     [ 5.021, DEFAULT_ON ],
+                                'experimental::declared_refs' =>
+                                    [ 5.025, DEFAULT_ON ],
+                                'experimental::script_run' =>
+                                    [ 5.027, DEFAULT_ON ],
+                                'experimental::alpha_assertions' =>
+                                    [ 5.027, DEFAULT_ON ],
+                                'experimental::private_use' =>
+                                    [ 5.029, DEFAULT_ON ],
+                                'experimental::uniprop_wildcards' =>
+                                    [ 5.029, DEFAULT_ON ],
+                                'experimental::vlb' =>
+                                    [ 5.029, DEFAULT_ON ],
+                                'experimental::isa' =>
+                                    [ 5.031, DEFAULT_ON ],
                         }],
 
-               'missing'       => [ 5.021, DEFAULT_OFF],
-               'redundant'     => [ 5.021, DEFAULT_OFF],
+        'missing'       => [ 5.021, DEFAULT_OFF],
+        'redundant'     => [ 5.021, DEFAULT_OFF],
+        'locale'        => [ 5.021, DEFAULT_ON],
+        'shadow'        => [ 5.027, DEFAULT_OFF],
 
-                #'default'     => [ 5.008, DEFAULT_ON ],
-       }],
-} ;
+         #'default'     => [ 5.008, DEFAULT_ON ],
+}]};
 
 my @def ;
 my %list ;
@@ -189,15 +205,15 @@ sub walk
 
 sub mkRange
 {
-    my @a = @_ ;
-    my @out = @a ;
+    my @in = @_ ;
+    my @out = @in ;
 
-    for my $i (1 .. @a - 1) {
+    for my $i (1 .. @in - 1) {
        $out[$i] = ".."
-          if $a[$i] == $a[$i - 1] + 1
-             && ($i >= @a  - 1 || $a[$i] + 1 == $a[$i + 1] );
+          if $in[$i] == $in[$i - 1] + 1
+             && ($i >= @in  - 1 || $in[$i] + 1 == $in[$i + 1] );
     }
-    $out[-1] = $a[-1] if $out[-1] eq "..";
+    $out[-1] = $in[-1] if $out[-1] eq "..";
 
     my $out = join(",",@out);
 
@@ -251,11 +267,11 @@ sub warningsTree
 
 sub mkHexOct
 {
-    my ($f, $max, @a) = @_ ;
+    my ($f, $max, @bits) = @_ ;
     my $mask = "\x00" x $max ;
     my $string = "" ;
 
-    foreach (@a) {
+    foreach (@bits) {
        vec($mask, $_, 1) = 1 ;
     }
 
@@ -272,14 +288,14 @@ sub mkHexOct
 
 sub mkHex
 {
-    my($max, @a) = @_;
-    return mkHexOct("x", $max, @a);
+    my($max, @bits) = @_;
+    return mkHexOct("x", $max, @bits);
 }
 
 sub mkOct
 {
-    my($max, @a) = @_;
-    return mkHexOct("o", $max, @a);
+    my($max, @bits) = @_;
+    return mkHexOct("o", $max, @bits);
 }
 
 ###########################################################################
@@ -314,8 +330,8 @@ my ($index, $warn_size);
 #define G_WARN_ALL_MASK                (G_WARN_ALL_ON|G_WARN_ALL_OFF)
 
 #define pWARN_STD              NULL
-#define pWARN_ALL              (((STRLEN*)0)+1)    /* use warnings 'all' */
-#define pWARN_NONE             (((STRLEN*)0)+2)    /* no  warnings 'all' */
+#define pWARN_ALL              (STRLEN *) &PL_WARN_ALL    /* use warnings 'all' */
+#define pWARN_NONE             (STRLEN *) &PL_WARN_NONE   /* no  warnings 'all' */
 
 #define specialWARN(x)         ((x) == pWARN_STD || (x) == pWARN_ALL ||        \
                                 (x) == pWARN_NONE)
@@ -335,38 +351,93 @@ Too many warnings categories -- max is 255
 EOM
 
   walk ($tree) ;
+  for (my $i = $index; $i & 3; $i++) {
+      push @{$list{all}}, $i;
+  }
 
   $index *= 2 ;
   $warn_size = int($index / 8) + ($index % 8 != 0) ;
 
   my $k ;
   my $last_ver = 0;
+  my @names;
   foreach $k (sort { $a <=> $b } keys %ValueToName) {
       my ($name, $version) = @{ $ValueToName{$k} };
       print $warn "\n/* Warnings Categories added in Perl $version */\n\n"
           if $last_ver != $version ;
       $name =~ y/:/_/;
-      print $warn tab(5, "#define WARN_$name"), " $k\n" ;
+      $name = "WARN_$name";
+      print $warn tab(6, "#define $name"), " $k\n" ;
+      push @names, $name;
       $last_ver = $version ;
   }
-  print $warn "\n" ;
 
-  print $warn tab(5, '#define WARNsize'),      "$warn_size\n" ;
-  print $warn tab(5, '#define WARN_ALLstring'), '"', ('\125' x $warn_size) , "\"\n" ;
-  print $warn tab(5, '#define WARN_NONEstring'), '"', ('\0' x $warn_size) , "\"\n" ;
+  print $warn tab(6, '#define WARNsize'),      " $warn_size\n" ;
+  print $warn tab(6, '#define WARN_ALLstring'), ' "', ('\125' x $warn_size) , "\"\n" ;
+  print $warn tab(6, '#define WARN_NONEstring'), ' "', ('\0' x $warn_size) , "\"\n" ;
 
   print $warn <<'EOM';
 
-#define isLEXWARN_on   (PL_curcop->cop_warnings != pWARN_STD)
-#define isLEXWARN_off  (PL_curcop->cop_warnings == pWARN_STD)
+#define isLEXWARN_on \
+       cBOOL(PL_curcop && PL_curcop->cop_warnings != pWARN_STD)
+#define isLEXWARN_off \
+       cBOOL(!PL_curcop || PL_curcop->cop_warnings == pWARN_STD)
 #define isWARN_ONCE    (PL_dowarn & (G_WARN_ON|G_WARN_ONCE))
 #define isWARN_on(c,x) (IsSet((U8 *)(c + 1), 2*(x)))
 #define isWARNf_on(c,x)        (IsSet((U8 *)(c + 1), 2*(x)+1))
 
-#define DUP_WARNINGS(p)                \
-    (specialWARN(p) ? (STRLEN*)(p)     \
-    : (STRLEN*)CopyD(p, PerlMemShared_malloc(sizeof(*p)+*p), sizeof(*p)+*p, \
-                                            char))
+#define DUP_WARNINGS(p) Perl_dup_warnings(aTHX_ p)
+
+#define free_and_set_cop_warnings(cmp,w) STMT_START { \
+  if (!specialWARN((cmp)->cop_warnings)) PerlMemShared_free((cmp)->cop_warnings); \
+  (cmp)->cop_warnings = w; \
+} STMT_END
+
+/*
+
+=head1 Warning and Dieing
+
+In all these calls, the C<U32 wI<n>> parameters are warning category
+constants.  You can see the ones currently available in
+L<warnings/Category Hierarchy>, just capitalize all letters in the names
+and prefix them by C<WARN_>.  So, for example, the category C<void> used in a
+perl program becomes C<WARN_VOID> when used in XS code and passed to one of
+the calls below.
+
+=for apidoc Am|bool|ckWARN|U32 w
+=for apidoc_item ||ckWARN2|U32 w1|U32 w2
+=for apidoc_item ||ckWARN3|U32 w1|U32 w2|U32 w3
+=for apidoc_item ||ckWARN4|U32 w1|U32 w2|U32 w3|U32 w4
+These return a boolean as to whether or not warnings are enabled for any of
+the warning category(ies) parameters:  C<w>, C<w1>, ....
+
+Should any of the categories by default be enabled even if not within the
+scope of S<C<use warnings>>, instead use the C<L</ckWARN_d>> macros.
+
+The categories must be completely independent, one may not be subclassed from
+the other.
+
+=for apidoc Am|bool|ckWARN_d|U32 w
+=for apidoc_item ||ckWARN2_d|U32 w1|U32 w2
+=for apidoc_item ||ckWARN3_d|U32 w1|U32 w2|U32 w3
+=for apidoc_item ||ckWARN4_d|U32 w1|U32 w2|U32 w3|U32 w4
+
+Like C<L</ckWARN>>, but for use if and only if the warning category(ies) is by
+default enabled even if not within the scope of S<C<use warnings>>.
+
+=for apidoc Am|U32|packWARN|U32 w1
+=for apidoc_item ||packWARN2|U32 w1|U32 w2
+=for apidoc_item ||packWARN3|U32 w1|U32 w2|U32 w3
+=for apidoc_item ||packWARN4|U32 w1|U32 w2|U32 w3|U32 w4
+
+These macros are used to pack warning categories into a single U32 to pass to
+macros and functions that take a warning category parameter.  The number of
+categories to pack is given by the name, with a corresponding number of
+category parameters passed.
+
+=cut
+
+*/
 
 #define ckWARN(w)              Perl_ckwarn(aTHX_ packWARN(w))
 
@@ -399,40 +470,54 @@ EOM
 #define unpackWARN4(x)         (((x) >>24) & 0xFF)
 
 #define ckDEAD(x)                                                      \
-          ( ! specialWARN(PL_curcop->cop_warnings) &&                  \
-           ( isWARNf_on(PL_curcop->cop_warnings, WARN_ALL) ||          \
-             isWARNf_on(PL_curcop->cop_warnings, unpackWARN1(x)) ||    \
-             isWARNf_on(PL_curcop->cop_warnings, unpackWARN2(x)) ||    \
-             isWARNf_on(PL_curcop->cop_warnings, unpackWARN3(x)) ||    \
-             isWARNf_on(PL_curcop->cop_warnings, unpackWARN4(x))))
-
-/* end of file warnings.h */
+   (PL_curcop &&                                                        \
+    !specialWARN(PL_curcop->cop_warnings) &&                           \
+    (isWARNf_on(PL_curcop->cop_warnings, unpackWARN1(x)) ||            \
+      (unpackWARN2(x) &&                                                \
+       (isWARNf_on(PL_curcop->cop_warnings, unpackWARN2(x)) ||         \
+         (unpackWARN3(x) &&                                            \
+           (isWARNf_on(PL_curcop->cop_warnings, unpackWARN3(x)) ||     \
+             (unpackWARN4(x) &&                                        \
+               isWARNf_on(PL_curcop->cop_warnings, unpackWARN4(x)))))))))
+
 EOM
 
+  print $warn "\n\n/*\n" ;
+  print $warn map { "=for apidoc Amnh||$_\n" } @names;
+  print $warn "\n=cut\n*/\n\n" ;
+  print $warn "/* end of file warnings.h */\n";
+
   read_only_bottom_close_and_rename($warn);
 }
 
 while (<DATA>) {
+    last if /^VERSION$/ ;
+    print $pm $_ ;
+}
+
+print $pm qq(our \$VERSION = "$::VERSION";\n);
+
+while (<DATA>) {
     last if /^KEYWORDS$/ ;
     print $pm $_ ;
 }
 
 my $last_ver = 0;
-print $pm "our %Offsets = (\n" ;
+print $pm "our %Offsets = (" ;
 foreach my $k (sort { $a <=> $b } keys %ValueToName) {
     my ($name, $version) = @{ $ValueToName{$k} };
     $name = lc $name;
     $k *= 2 ;
     if ( $last_ver != $version ) {
         print $pm "\n";
-        print $pm tab(4, "    # Warnings Categories added in Perl $version");
-        print $pm "\n\n";
+        print $pm tab(6, "    # Warnings Categories added in Perl $version");
+        print $pm "\n";
     }
-    print $pm tab(4, "    '$name'"), "=> $k,\n" ;
+    print $pm tab(6, "    '$name'"), "=> $k,\n" ;
     $last_ver = $version;
 }
 
-print $pm "  );\n\n" ;
+print $pm ");\n\n" ;
 
 print $pm "our %Bits = (\n" ;
 foreach my $k (sort keys  %list) {
@@ -440,12 +525,12 @@ foreach my $k (sort keys  %list) {
     my $v = $list{$k} ;
     my @list = sort { $a <=> $b } @$v ;
 
-    print $pm tab(4, "    '$k'"), '=> "',
+    print $pm tab(6, "    '$k'"), '=> "',
                mkHex($warn_size, map $_ * 2 , @list),
                '", # [', mkRange(@list), "]\n" ;
 }
 
-print $pm "  );\n\n" ;
+print $pm ");\n\n" ;
 
 print $pm "our %DeadBits = (\n" ;
 foreach my $k (sort keys  %list) {
@@ -453,17 +538,18 @@ foreach my $k (sort keys  %list) {
     my $v = $list{$k} ;
     my @list = sort { $a <=> $b } @$v ;
 
-    print $pm tab(4, "    '$k'"), '=> "',
+    print $pm tab(6, "    '$k'"), '=> "',
                mkHex($warn_size, map $_ * 2 + 1 , @list),
                '", # [', mkRange(@list), "]\n" ;
 }
 
-print $pm "  );\n\n" ;
-print $pm '$NONE     = "', ('\0' x $warn_size) , "\";\n" ;
-print $pm '$DEFAULT  = "', mkHex($warn_size, map $_ * 2, @def),
-                          '", # [', mkRange(@def), "]\n" ;
-print $pm '$LAST_BIT = ' . "$index ;\n" ;
-print $pm '$BYTES    = ' . "$warn_size ;\n" ;
+print $pm ");\n\n" ;
+print $pm "# These are used by various things, including our own tests\n";
+print $pm tab(6, 'our $NONE'), '=  "', ('\0' x $warn_size) , "\";\n" ;
+print $pm tab(6, 'our $DEFAULT'), '=  "', mkHex($warn_size, map $_ * 2, @def),
+                          '"; # [', mkRange(sort { $a <=> $b } @def), "]\n" ;
+print $pm tab(6, 'our $LAST_BIT'), '=  ' . "$index ;\n" ;
+print $pm tab(6, 'our $BYTES'),    '=  ' . "$warn_size ;\n" ;
 while (<DATA>) {
     if ($_ eq "=for warnings.pl tree-goes-here\n") {
       print $pm warningsTree($tree, "    ");
@@ -477,19 +563,18 @@ read_only_bottom_close_and_rename($pm);
 __END__
 package warnings;
 
-our $VERSION = '1.28';
+VERSION
 
 # Verify that we're called correctly so that warnings will work.
+# Can't use Carp, since Carp uses us!
+# String regexps because constant folding = smaller optree = less memory vs regexp literal
 # see also strict.pm.
-unless ( __FILE__ =~ /(^|[\/\\])\Q${\__PACKAGE__}\E\.pmc?$/ ) {
-    my (undef, $f, $l) = caller;
-    die("Incorrect use of pragma '${\__PACKAGE__}' at $f line $l.\n");
-}
+die sprintf "Incorrect use of pragma '%s' at %s line %d.\n", __PACKAGE__, +(caller)[1,2]
+    if __FILE__ !~ ( '(?x) \b     '.__PACKAGE__.'  \.pmc? \z' )
+    && __FILE__ =~ ( '(?x) \b (?i:'.__PACKAGE__.') \.pmc? \z' );
 
 KEYWORDS
 
-$All = "" ; vec($All, $Offsets{'all'}, 2) = 3 ;
-
 sub Croaker
 {
     require Carp; # this initializes %CarpInternal
@@ -498,12 +583,32 @@ sub Croaker
     Carp::croak(@_);
 }
 
+sub _expand_bits {
+    my $bits = shift;
+    my $want_len = ($LAST_BIT + 7) >> 3;
+    my $len = length($bits);
+    if ($len != $want_len) {
+       if ($bits eq "") {
+           $bits = "\x00" x $want_len;
+       } elsif ($len > $want_len) {
+           substr $bits, $want_len, $len-$want_len, "";
+       } else {
+           my $x = vec($bits, $Offsets{all} >> 1, 2);
+           $x |= $x << 2;
+           $x |= $x << 4;
+           $bits .= chr($x) x ($want_len - $len);
+       }
+    }
+    return $bits;
+}
+
 sub _bits {
     my $mask = shift ;
     my $catmask ;
     my $fatal = 0 ;
     my $no_fatal = 0 ;
 
+    $mask = _expand_bits($mask);
     foreach my $word ( @_ ) {
        if ($word eq 'FATAL') {
            $fatal = 1;
@@ -516,10 +621,10 @@ sub _bits {
        elsif ($catmask = $Bits{$word}) {
            $mask |= $catmask ;
            $mask |= $DeadBits{$word} if $fatal ;
-           $mask &= ~($DeadBits{$word}|$All) if $no_fatal ;
+           $mask = ~(~$mask | $DeadBits{$word}) if $no_fatal ;
        }
        else
-          { Croaker("Unknown warnings category '$word'")}
+         { Croaker("Unknown warnings category '$word'")}
     }
 
     return $mask ;
@@ -529,7 +634,7 @@ sub bits
 {
     # called from B::Deparse.pm
     push @_, 'all' unless @_ ;
-    return _bits(undef, @_) ;
+    return _bits("", @_) ;
 }
 
 sub import
@@ -538,16 +643,12 @@ sub import
 
     my $mask = ${^WARNING_BITS} // ($^W ? $Bits{all} : $DEFAULT) ;
 
-    if (vec($mask, $Offsets{'all'}, 1)) {
-        $mask |= $Bits{'all'} ;
-        $mask |= $DeadBits{'all'} if vec($mask, $Offsets{'all'}+1, 1);
-    }
-
-    # append 'all' when implied (after a lone "FATAL" or "NONFATAL")
-    push @_, 'all' if @_==1 && ( $_[0] eq 'FATAL' || $_[0] eq 'NONFATAL' );
+    # append 'all' when implied (empty import list or after a lone
+    # "FATAL" or "NONFATAL")
+    push @_, 'all'
+       if !@_ || (@_==1 && ($_[0] eq 'FATAL' || $_[0] eq 'NONFATAL'));
 
-    # Empty @_ is equivalent to @_ = 'all' ;
-    ${^WARNING_BITS} = @_ ? _bits($mask, @_) : $mask | $Bits{all} ;
+    ${^WARNING_BITS} = _bits($mask, @_);
 }
 
 sub unimport
@@ -557,23 +658,19 @@ sub unimport
     my $catmask ;
     my $mask = ${^WARNING_BITS} // ($^W ? $Bits{all} : $DEFAULT) ;
 
-    if (vec($mask, $Offsets{'all'}, 1)) {
-        $mask |= $Bits{'all'} ;
-        $mask |= $DeadBits{'all'} if vec($mask, $Offsets{'all'}+1, 1);
-    }
-
     # append 'all' when implied (empty import list or after a lone "FATAL")
     push @_, 'all' if !@_ || @_==1 && $_[0] eq 'FATAL';
 
+    $mask = _expand_bits($mask);
     foreach my $word ( @_ ) {
        if ($word eq 'FATAL') {
            next;
        }
        elsif ($catmask = $Bits{$word}) {
-           $mask &= ~($catmask | $DeadBits{$word} | $All);
+           $mask = ~(~$mask | $catmask | $DeadBits{$word});
        }
        else
-          { Croaker("Unknown warnings category '$word'")}
+         { Croaker("Unknown warnings category '$word'")}
     }
 
     ${^WARNING_BITS} = $mask ;
@@ -581,6 +678,7 @@ sub unimport
 
 my %builtin_type; @builtin_type{qw(SCALAR ARRAY HASH CODE REF GLOB LVALUE Regexp)} = ();
 
+sub LEVEL () { 8 };
 sub MESSAGE () { 4 };
 sub FATAL () { 2 };
 sub NORMAL () { 1 };
@@ -592,8 +690,18 @@ sub __chk
     my $isobj = 0 ;
     my $wanted = shift;
     my $has_message = $wanted & MESSAGE;
-
-    unless (@_ == 1 || @_ == ($has_message ? 2 : 0)) {
+    my $has_level   = $wanted & LEVEL  ;
+
+    if ($has_level) {
+       if (@_ != ($has_message ? 3 : 2)) {
+           my $sub = (caller 1)[3];
+           my $syntax = $has_message
+               ? "category, level, 'message'"
+               : 'category, level';
+           Croaker("Usage: $sub($syntax)");
+        }
+    }
+    elsif (not @_ == 1 || @_ == ($has_message ? 2 : 0)) {
        my $sub = (caller 1)[3];
        my $syntax = $has_message ? "[category,] 'message'" : '[category]';
        Croaker("Usage: $sub($syntax)");
@@ -602,37 +710,40 @@ sub __chk
     my $message = pop if $has_message;
 
     if (@_) {
-        # check the category supplied.
-        $category = shift ;
-        if (my $type = ref $category) {
-            Croaker("not an object")
-                if exists $builtin_type{$type};
+       # check the category supplied.
+       $category = shift ;
+       if (my $type = ref $category) {
+           Croaker("not an object")
+               if exists $builtin_type{$type};
            $category = $type;
-            $isobj = 1 ;
-        }
-        $offset = $Offsets{$category};
-        Croaker("Unknown warnings category '$category'")
+           $isobj = 1 ;
+       }
+       $offset = $Offsets{$category};
+       Croaker("Unknown warnings category '$category'")
            unless defined $offset;
     }
     else {
-        $category = (caller(1))[0] ;
-        $offset = $Offsets{$category};
-        Croaker("package '$category' not registered for warnings")
+       $category = (caller(1))[0] ;
+       $offset = $Offsets{$category};
+       Croaker("package '$category' not registered for warnings")
            unless defined $offset ;
     }
 
     my $i;
 
     if ($isobj) {
-        my $pkg;
-        $i = 2;
-        while (do { { package DB; $pkg = (caller($i++))[0] } } ) {
-            last unless @DB::args && $DB::args[0] =~ /^$category=/ ;
-        }
+       my $pkg;
+       $i = 2;
+       while (do { { package DB; $pkg = (caller($i++))[0] } } ) {
+           last unless @DB::args && $DB::args[0] =~ /^$category=/ ;
+       }
        $i -= 2 ;
     }
+    elsif ($has_level) {
+       $i = 2 + shift;
+    }
     else {
-        $i = _error_loc(); # see where Carp will allocate the error
+       $i = _error_loc(); # see where Carp will allocate the error
     }
 
     # Default to 0 if caller returns nothing.  Default to $DEFAULT if it
@@ -640,22 +751,35 @@ sub __chk
     my(@callers_bitmask) = (caller($i))[9] ;
     my $callers_bitmask =
         @callers_bitmask ? $callers_bitmask[0] // $DEFAULT : 0 ;
+    length($callers_bitmask) > ($offset >> 3) or $offset = $Offsets{all};
 
     my @results;
     foreach my $type (FATAL, NORMAL) {
        next unless $wanted & $type;
 
-       push @results, (vec($callers_bitmask, $offset + $type - 1, 1) ||
-                       vec($callers_bitmask, $Offsets{'all'} + $type - 1, 1));
+       push @results, vec($callers_bitmask, $offset + $type - 1, 1);
     }
 
     # &enabled and &fatal_enabled
     return $results[0] unless $has_message;
 
     # &warnif, and the category is neither enabled as warning nor as fatal
-    return if $wanted == (NORMAL | FATAL | MESSAGE)
+    return if ($wanted & (NORMAL | FATAL | MESSAGE))
+                     == (NORMAL | FATAL | MESSAGE)
        && !($results[0] || $results[1]);
 
+    # If we have an explicit level, bypass Carp.
+    if ($has_level and @callers_bitmask) {
+       # logic copied from util.c:mess_sv
+       my $stuff = " at " . join " line ", (caller $i)[1,2];
+       $stuff .= sprintf ", <%s> %s %d",
+                          *${^LAST_FH}{NAME},
+                          ($/ eq "\n" ? "line" : "chunk"), $.
+           if $. && ${^LAST_FH};
+       die "$message$stuff.\n" if $results[0];
+       return warn "$message$stuff.\n";
+    }
+
     require Carp;
     Carp::croak($message) if $results[0];
     # will always get here for &warn. will only get here for &warnif if the
@@ -678,14 +802,13 @@ sub register_categories
 
     for my $name (@names) {
        if (! defined $Bits{$name}) {
-           $Bits{$name}     = _mkMask($LAST_BIT);
-           vec($Bits{'all'}, $LAST_BIT, 1) = 1;
-           $Offsets{$name}  = $LAST_BIT ++;
-           foreach my $k (keys %Bits) {
-               vec($Bits{$k}, $LAST_BIT, 1) = 0;
+           $Offsets{$name}  = $LAST_BIT;
+           $Bits{$name}     = _mkMask($LAST_BIT++);
+           $DeadBits{$name} = _mkMask($LAST_BIT++);
+           if (length($Bits{$name}) > length($Bits{all})) {
+               $Bits{all} .= "\x55";
+               $DeadBits{all} .= "\xaa";
            }
-           $DeadBits{$name} = _mkMask($LAST_BIT);
-           vec($DeadBits{'all'}, $LAST_BIT++, 1) = 1;
        }
     }
 }
@@ -715,12 +838,33 @@ sub warnif
     return __chk(NORMAL | FATAL | MESSAGE, @_);
 }
 
+sub enabled_at_level
+{
+    return __chk(NORMAL | LEVEL, @_);
+}
+
+sub fatal_enabled_at_level
+{
+    return __chk(FATAL | LEVEL, @_);
+}
+
+sub warn_at_level
+{
+    return __chk(FATAL | MESSAGE | LEVEL, @_);
+}
+
+sub warnif_at_level
+{
+    return __chk(NORMAL | FATAL | MESSAGE | LEVEL, @_);
+}
+
 # These are not part of any public interface, so we can delete them to save
 # space.
-delete @warnings::{qw(NORMAL FATAL MESSAGE)};
+delete @warnings::{qw(NORMAL FATAL MESSAGE LEVEL)};
 
 1;
 __END__
+
 =head1 NAME
 
 warnings - Perl pragma to control optional warnings
@@ -780,40 +924,40 @@ Similarly all warnings are disabled in a block by either of these:
 For example, consider the code below:
 
     use warnings;
-    my @a;
+    my @x;
     {
         no warnings;
-       my $b = @a[0];
+       my $y = @x[0];
     }
-    my $c = @a[0];
+    my $z = @x[0];
 
 The code in the enclosing block has warnings enabled, but the inner
 block has them disabled.  In this case that means the assignment to the
-scalar C<$c> will trip the C<"Scalar value @a[0] better written as $a[0]">
-warning, but the assignment to the scalar C<$b> will not.
+scalar C<$z> will trip the C<"Scalar value @x[0] better written as $x[0]">
+warning, but the assignment to the scalar C<$y> will not.
 
 =head2 Default Warnings and Optional Warnings
 
 Before the introduction of lexical warnings, Perl had two classes of
-warnings: mandatory and optional. 
+warnings: mandatory and optional.
 
 As its name suggests, if your code tripped a mandatory warning, you
 would get a warning whether you wanted it or not.
 For example, the code below would always produce an C<"isn't numeric">
 warning about the "2:".
 
-    my $a = "2:" + 3;
+    my $x = "2:" + 3;
 
 With the introduction of lexical warnings, mandatory warnings now become
 I<default> warnings.  The difference is that although the previously
 mandatory warnings are still enabled by default, they can then be
 subsequently enabled or disabled with the lexical warning pragma.  For
 example, in the code below, an C<"isn't numeric"> warning will only
-be reported for the C<$a> variable.
+be reported for the C<$x> variable.
 
-    my $a = "2:" + 3;
+    my $x = "2:" + 3;
     no warnings;
-    my $b = "2:" + 3;
+    my $y = "2:" + 3;
 
 Note that neither the B<-w> flag or the C<$^W> can be used to
 disable/enable default warnings.  They are still mandatory in this case.
@@ -833,22 +977,26 @@ a block of code.  You might expect this to be enough to do the trick:
 
      {
          local ($^W) = 0;
-        my $a =+ 2;
-        my $b; chop $b;
+        my $x =+ 2;
+        my $y; chop $y;
      }
 
 When this code is run with the B<-w> flag, a warning will be produced
-for the C<$a> line:  C<"Reversed += operator">.
+for the C<$x> line:  C<"Reversed += operator">.
 
 The problem is that Perl has both compile-time and run-time warnings.  To
 disable compile-time warnings you need to rewrite the code like this:
 
      {
          BEGIN { $^W = 0 }
-        my $a =+ 2;
-        my $b; chop $b;
+        my $x =+ 2;
+        my $y; chop $y;
      }
 
+And note that unlike the first example, this will permanently set C<$^W>
+since it cannot both run during compile-time and be localized to a
+run-time block.
+
 The other big problem with C<$^W> is the way you can inadvertently
 change the warning setting in unexpected places in your code.  For example,
 when the code below is run (without the B<-w> flag), the second call
@@ -857,7 +1005,7 @@ the first will not.
 
     sub doit
     {
-        my $b; chop $b;
+        my $y; chop $y;
     }
 
     doit();
@@ -883,8 +1031,8 @@ warnings are (or aren't) produced:
 X<-w>
 
 This is  the existing flag.  If the lexical warnings pragma is B<not>
-used in any of you code, or any of the modules that you use, this flag
-will enable warnings everywhere.  See L<Backward Compatibility> for
+used in any of your code, or any of the modules that you use, this flag
+will enable warnings everywhere.  See L</Backward Compatibility> for
 details of how this flag interacts with lexical warnings.
 
 =item B<-W>
@@ -927,7 +1075,7 @@ will work unchanged.
 
 The B<-w> flag just sets the global C<$^W> variable as in 5.005.  This
 means that any legacy code that currently relies on manipulating C<$^W>
-to control warning behavior will still work as is. 
+to control warning behavior will still work as is.
 
 =item 3.
 
@@ -968,7 +1116,7 @@ Just like the "strict" pragma any of these categories can be combined
     no warnings qw(io syntax untie);
 
 Also like the "strict" pragma, if there is more than one instance of the
-C<warnings> pragma in a given scope the cumulative effect is additive. 
+C<warnings> pragma in a given scope the cumulative effect is additive.
 
     use warnings qw(void); # only "void" warnings enabled
     ...
@@ -990,9 +1138,55 @@ is now a top-level category in its own right.
 =head2 Fatal Warnings
 X<warning, fatal>
 
-The presence of the word "FATAL" in the category list will escalate any
-warnings detected from the categories specified in the lexical scope
-into fatal errors.  In the code below, the use of C<time>, C<length>
+The presence of the word "FATAL" in the category list will escalate
+warnings in those categories into fatal errors in that lexical scope.
+
+B<NOTE:> FATAL warnings should be used with care, particularly
+C<< FATAL => 'all' >>.
+
+Libraries using L<warnings::warn|/FUNCTIONS> for custom warning categories
+generally don't expect L<warnings::warn|/FUNCTIONS> to be fatal and can wind up
+in an unexpected state as a result.  For XS modules issuing categorized
+warnings, such unanticipated exceptions could also expose memory leak bugs.
+
+Moreover, the Perl interpreter itself has had serious bugs involving
+fatalized warnings.  For a summary of resolved and unresolved problems as
+of January 2015, please see
+L<this perl5-porters post|http://www.nntp.perl.org/group/perl.perl5.porters/2015/01/msg225235.html>.
+
+While some developers find fatalizing some warnings to be a useful
+defensive programming technique, using C<< FATAL => 'all' >> to fatalize
+all possible warning categories -- including custom ones -- is particularly
+risky.  Therefore, the use of C<< FATAL => 'all' >> is
+L<discouraged|perlpolicy/discouraged>.
+
+The L<strictures|strictures/VERSION-2> module on CPAN offers one example of
+a warnings subset that the module's authors believe is relatively safe to
+fatalize.
+
+B<NOTE:> users of FATAL warnings, especially those using
+C<< FATAL => 'all' >>, should be fully aware that they are risking future
+portability of their programs by doing so.  Perl makes absolutely no
+commitments to not introduce new warnings or warnings categories in the
+future; indeed, we explicitly reserve the right to do so.  Code that may
+not warn now may warn in a future release of Perl if the Perl5 development
+team deems it in the best interests of the community to do so.  Should code
+using FATAL warnings break due to the introduction of a new warning we will
+NOT consider it an incompatible change.  Users of FATAL warnings should
+take special caution during upgrades to check to see if their code triggers
+any new warnings and should pay particular attention to the fine print of
+the documentation of the features they use to ensure they do not exploit
+features that are documented as risky, deprecated, or unspecified, or where
+the documentation says "so don't do that", or anything with the same sense
+and spirit.  Use of such features in combination with FATAL warnings is
+ENTIRELY AT THE USER'S RISK.
+
+The following documentation describes how to use FATAL warnings but the
+perl5 porters strongly recommend that you understand the risks before doing
+so, especially for library code intended for use by others, as there is no
+way for downstream users to change the choice of fatal categories.
+
+In the code below, the use of C<time>, C<length>
 and C<join> can all produce a C<"Useless use of xxx in void context">
 warning.
 
@@ -1012,7 +1206,7 @@ warning.
 When run it produces this output
 
     Useless use of time in void context at fatal line 3.
-    Useless use of length in void context at fatal line 7.  
+    Useless use of length in void context at fatal line 7.
 
 The scope where C<length> is used has escalated the C<void> warnings
 category into a fatal error, so the program terminates immediately when it
@@ -1045,24 +1239,6 @@ C<< use warnings 'FATAL'; >>, C<< use warnings 'NONFATAL'; >> and
 C<< no warnings 'FATAL'; >> was unspecified; they did not behave as if
 they included the C<< => 'all' >> portion.  As of 5.20, they do.)
 
-B<NOTE:> Users of FATAL warnings, especially
-those using C<< FATAL => 'all' >>
-should be fully aware that they are risking future portability of their
-programs by doing so.  Perl makes absolutely no commitments to not
-introduce new warnings, or warnings categories in the future, and indeed
-we explicitly reserve the right to do so.  Code that may not warn now may
-warn in a future release of Perl if the Perl5 development team deems it
-in the best interests of the community to do so.  Should code using FATAL
-warnings break due to the introduction of a new warning we will NOT
-consider it an incompatible change.  Users of FATAL warnings should take
-special caution during upgrades to check to see if their code triggers
-any new warnings and should pay particular attention to the fine print of
-the documentation of the features they use to ensure they do not exploit
-features that are documented as risky, deprecated, or unspecified, or where
-the documentation says "so don't do that", or anything with the same sense
-and spirit.  Use of such features in combination with FATAL warnings is
-ENTIRELY AT THE USER'S RISK.
-
 =head2 Reporting Warnings from a Module
 X<warning, reporting> X<warning, registering>
 
@@ -1107,8 +1283,10 @@ this snippet of code:
     package MyMod::Abc;
 
     sub open {
-        warnings::warnif("deprecated", 
-                         "open is deprecated, use new instead");
+        if (warnings::enabled("deprecated")) {
+            warnings::warn("deprecated",
+                           "open is deprecated, use new instead");
+        }
         new(@_);
     }
 
@@ -1189,19 +1367,19 @@ Consider this example:
 
     1;
 
-The code below makes use of both modules, but it only enables warnings from 
+The code below makes use of both modules, but it only enables warnings from
 C<Derived>.
 
     use Original;
     use Derived;
     use warnings 'Derived';
-    my $a = Original->new();
-    $a->doit(1);
-    my $b = Derived->new();
-    $a->doit(1);
+    my $x = Original->new();
+    $x->doit(1);
+    my $y = Derived->new();
+    $x->doit(1);
 
-When this code is run only the C<Derived> object, C<$b>, will generate
-a warning. 
+When this code is run only the C<Derived> object, C<$y>, will generate
+a warning.
 
     Odd numbers are unsafe at main.pl line 7
 
@@ -1220,6 +1398,9 @@ warnings::register like this:
 
 =head1 FUNCTIONS
 
+Note: The functions with names ending in C<_at_level> were added in Perl
+5.28.
+
 =over 4
 
 =item use warnings::register
@@ -1249,6 +1430,11 @@ Return TRUE if that warnings category is enabled in the first scope
 where the object is used.
 Otherwise returns FALSE.
 
+=item warnings::enabled_at_level($category, $level)
+
+Like C<warnings::enabled>, but $level specifies the exact call frame, 0
+being the immediate caller.
+
 =item warnings::fatal_enabled()
 
 Return TRUE if the warnings category with the same name as the current
@@ -1270,6 +1456,11 @@ Return TRUE if that warnings category has been set to FATAL in the first
 scope where the object is used.
 Otherwise returns FALSE.
 
+=item warnings::fatal_enabled_at_level($category, $level)
+
+Like C<warnings::fatal_enabled>, but $level specifies the exact call frame,
+0 being the immediate caller.
+
 =item warnings::warn($message)
 
 Print C<$message> to STDERR.
@@ -1296,6 +1487,10 @@ warnings category.
 If that warnings category has been set to "FATAL" in the scope where C<$object>
 is first used then die. Otherwise return.
 
+=item warnings::warn_at_level($category, $level, $message)
+
+Like C<warnings::warn>, but $level specifies the exact call frame,
+0 being the immediate caller.
 
 =item warnings::warnif($message)
 
@@ -1318,6 +1513,11 @@ Equivalent to:
     if (warnings::enabled($object))
       { warnings::warn($object, $message) }
 
+=item warnings::warnif_at_level($category, $level, $message)
+
+Like C<warnings::warnif>, but $level specifies the exact call frame,
+0 being the immediate caller.
+
 =item warnings::register_categories(@names)
 
 This registers warning categories for the given names and is primarily for