FYI, I *initially* had it working this way in Type::Tiny, but it made inlining stupidly complex so I switched to just throwing from the child type constraint.
But with the build up to 0.006 I'm working on an $exception->explain method which re-examines why the value failed the type constraint, step-by-step.
For example, checking [1,2,[3]] against ArrayRef[Int] gives an explanation like this:
[
'[1,2,[3]] did not pass type constraint "ArrayRef[Int]"',
'"Int" is a subtype of "Num"',
'"Num" is a subtype of "Str"',
'"Str" is a subtype of "Value"',
'[3] did not pass type constraint "Value" (in $_->[2])',
'"Value" is defined as: (defined($_) and not ref($_))',
],
This means that the extensive examination is avoided on "hot" code paths (i.e. where the value passes the type constraint), but can be explicitly called in, say, a catch block.
Anyway, that's just an idea of a different way this can be done.