+ goto $handler;
+}
+
+=head2 C<is_deeply $got, $expected [, $desc ]>
+
+See L<Test::More/is_deeply>.
+
+=cut
+
+sub _deep_ref_check {
+ my ($x, $y, $ry) = @_;
+
+ no warnings qw<numeric uninitialized>;
+
+ if ($ry eq 'ARRAY') {
+ return 0 unless $#$x == $#$y;
+
+ my ($ex, $ey);
+ for (0 .. $#$y) {
+ $ex = $x->[$_];
+ $ey = $y->[$_];
+
+ # Inline the beginning of _deep_check
+ return 0 if defined $ex xor defined $ey;
+
+ next if not(ref $ex xor ref $ey) and $ex eq $ey;
+
+ $ry = Scalar::Util::reftype($ey);
+ return 0 if Scalar::Util::reftype($ex) ne $ry;
+
+ return 0 unless $ry and _deep_ref_check($ex, $ey, $ry);
+ }
+
+ return 1;
+ } elsif ($ry eq 'HASH') {
+ return 0 unless keys(%$x) == keys(%$y);
+
+ my ($ex, $ey);
+ for (keys %$y) {
+ return 0 unless exists $x->{$_};
+ $ex = $x->{$_};
+ $ey = $y->{$_};
+
+ # Inline the beginning of _deep_check
+ return 0 if defined $ex xor defined $ey;
+
+ next if not(ref $ex xor ref $ey) and $ex eq $ey;
+
+ $ry = Scalar::Util::reftype($ey);
+ return 0 if Scalar::Util::reftype($ex) ne $ry;
+
+ return 0 unless $ry and _deep_ref_check($ex, $ey, $ry);
+ }
+
+ return 1;
+ } elsif ($ry eq 'SCALAR' or $ry eq 'REF') {
+ return _deep_check($$x, $$y);
+ }
+
+ return 0;
+}
+
+sub _deep_check {
+ my ($x, $y) = @_;
+
+ no warnings qw<numeric uninitialized>;
+
+ return 0 if defined $x xor defined $y;
+
+ # Try object identity/eq overloading first. It also covers the case where
+ # $x and $y are both undefined.
+ # If either $x or $y is overloaded but none has eq overloading, the test will
+ # break at that point.
+ return 1 if not(ref $x xor ref $y) and $x eq $y;
+
+ # Test::More::is_deeply happily breaks encapsulation if the objects aren't
+ # overloaded.
+ my $ry = Scalar::Util::reftype($y);
+ return 0 if Scalar::Util::reftype($x) ne $ry;
+
+ # Shortcut if $x and $y are both not references and failed the previous
+ # $x eq $y test.
+ return 0 unless $ry;
+
+ # We know that $x and $y are both references of type $ry, without overloading.
+ _deep_ref_check($x, $y, $ry);
+}
+
+sub is_deeply {
+ @_ = (
+ &_deep_check,
+ $_[2],
+ );
+ goto &ok;