Seems like you are looking for "nested exceptions" somewhat like what Java gives.  For your requirement of scoping it, how about doing a set -e at the beginning of the function and making sure to run set +e before returning from it?
Another idea, which is not efficient or convenient, is to call your function in a subshell:
# some code
(set -e; my_function)
if [[ $? -ne 0 ]]; then
  # the function didn't succeed...
fi
# more code
In any case, please be aware that set -e is not the greatest way to handle errors in a shell script.  There are way too many issues making it quite unreliable.  See these related posts:
The approach I take for large scripts that need to exist for a long time in a production environment is:
- create a library of functions to do all the standard stuff
- the library will have a wrapper around each standard action (say, mv,cp,mkdir,ln,rm, etc.) that would validate the arguments carefully and also handle exceptions
- upon exception, the wrapper exits with a clear error message
- the exit itself could be a library function, somewhat like this:
--
# library of common functions
trap '_error_handler' ERR
trap '_exit_handler'  EXIT
trap '_int_handler'   SIGINT
_error_handler() {
  # appropriate code
}
# other handlers go here...
#
exit_if_error() {
  error_code=${1:-0}
  error_message=${2:-"Uknown error"}
  [[ $error_code == 0 ]] && return 0  # it is all good
  # this can be enhanced to print out the "stack trace"
  >&2 printf "%s\n" $error_message
  # out of here
  my_exit $error_code
}
my_exit() {
  exit_code=${1:-0}
  _global_graceful_exit=1  # this can be checked by the "EXIT" trap handler
  exit $exit_code
}
# simple wrapper for cp
my_cp() {
  # add code to check arguments more effectively
  cp $1 $2
  exit_if_error $? "cp of '$1' to '$2' failed"
}
# main code
source /path/to/library.sh
...
my_cp file1 file2
# clutter-free code
This, along with effective use of trap to take action on ERR and EXIT events, would be a good way to write reliable shell scripts.