We used to compare `type(self.upper) == int` but the check was changed
to `isinstance(self.upper, int)`. Because bool is an instance of int but is not
the type int, the meaning of the checks changed to treat False as 0.
This patch specifically differentiates between False and 0.
- False means "no requirements for this character class".
- 0 means "exactly 0 of this character class".
Fixes#2093