Thanks again for pointing to a dark corner in my proposal.

For the template problem I can see at least 2 solutions (and would propose allowing both in the language).

1. Traditional-style: if the templates be the problem, let the templates be the solution:
============================================================
class storage_exception { ... };
class database_exception { ... };

template <class T> struct storage_traits
{
  const int max_record_length = 1000;
  using service_exception = storage_exception;
};

template <> struct storage_traits<database_storage>
{
  const int max_record_length = 2000;
  using service_exception = database_exception;
};

class file_storage
{
  bool read_record(char record[1000]) throw(storage_exception);
};

class database_storage
{
  bool read_record(char record[2000]) throw(database_exception);
};

template <class T>
void validate(T & storage) throw(typename storage_traits<T>::service_exception)
{
  char record[storage_traits<T>::max_record_length];
  while (storage.read_record(record)) ...
}
============================================================

2. New-style: let the compiler deduce (the "auto" keyword already means "let compiler determine the type"!)
============================================================
class storage_exception { ... };
class database_exception { ... };

template <class T> struct storage_traits
{
  const int max_record_length = 1000;
}

template <> struct storage_traits<database_storage>
{
  const int max_record_length = 2000;
}

class file_storage
{
  bool read_record(char record[1000]) throw(storage_exception);
}

class database_storage
{
  bool read_record(char record[2000]) throw(database_exception);
}

template <class T>
void validate(T & storage) throw(auto)  // the compiler "knows" which "checked" exceptions are not handled
{
  char record[storage_traits<T>::max_record_length];
  while (storage.read_record(record)) ... // the compiler "knows" what is "checked" for "T::readRecord()"
}
============================================================

What do you think?

I agree when you say "C++ exceptions are what you use when you want the set of yieldable things to be open-ended", and I'm not proposing to change that. I just want the programmer to be able to choose between open-ended-ness and safety on a per-function basis.

    Andrey Kapustin

On 21/06/2020 23:15, Arthur O'Dwyer via Std-Proposals wrote:
On Sun, Jun 21, 2020 at 5:10 PM Michael Hava via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
To quote the recent HOPL paper by Bjarne:

Page 62: "There was always a group of people who wanted compile-time checks of which exceptions a
function could throw. That, of course, works well in type theory, with small programs, with fast
compilers, and with full control of the source code. The committee repeatedly rejected that idea on
the grounds that it doesn’t scale to million-line programs developed and maintained by dozens (or
more) organizations."

Concretely, one of the ways C++ lets you write big programs "without full control of the source code" is templates.
Andrey, you'd have to figure out what to do about templates:

struct ErrA {};
struct ErrB {};
struct SA {
    void foo() throw(ErrA);
};
struct SB {
    void foo() throw(ErrB);
};
template<class T>
void foo(T t) throw(???) {
     t.foo();
}

C++ already has several properties that can be propagated from callee to caller with enough effort: noexceptness, well-formedness (SFINAE).
It has a few that cannot be propagated without loss: explicitness, overload-resolution-priority, constrainedness (C++20).
At the moment, you're asking to add another property that can't be propagated. Can you fix that somehow?

Herb Sutter's "Herbceptions" have basically the same problem with templates (last I checked): they add a new property that we'd like to propagate but it is ugly and/or inefficient to do so. I refer to these kinds of property as "colorations"; see e.g.
https://quuxplusone.github.io/blog/2018/03/16/async-roundup/#the-concept-of-function-coloring (less about herbceptions)
https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/A05A5feSZYI (more about herbceptions)

In general I kind of like the idea of statically checking "this function may yield a result or an ErrA, and so all callers must handle ErrA"; but if you're actually in that situation today, why not just return a `variant` or `Expected` type instead? C++ exceptions are what you use when you want the set of yieldable things to be open-ended.

–Arthur