Руководство по языку Ада (Ada 2022)
3.2.4 Предикаты подтипов
Определяемые языком предикатные аспекты Static_Predicate и Dynamic_Predicate могут использоваться для определения свойств подтипов. Спецификация предиката — это спецификация_аспекта для одного из двух предикатных аспектов. Общие правила для аспектов и спецификаций_аспектов приведены в разделе 13 (13.1 и 13.1.1 соответственно). Предикатные аспекты являются аспектами утверждений (см. 11.4.2). Предикатные аспекты не наследуются, но их эффекты являются аддитивными, как определено ниже.
Правила разрешения имен
Ожидаемый тип для предикатного аспекта выражение — любой булевский тип.
Статическая семантика
Спецификация предиката может быть задана для объявления_типа или объявления_подтипа и применяется к объявленному подтипу. Кроме того, спецификации предикатов применяются к некоторым другим подтипам:
- Для (первого) подтипа, определенного объявлением типа, применяются любые предикаты родительских или предковых подтипов.
- Для подтипа, созданного указанием_подтипа, применяется предикат подтипа, обозначенного меткой_подтипа.
Этот абзац был удален.
Проверки предикатов определяются как активированные или деактивированные для данного подтипа следующим образом:
- Если подтип объявлен объявлением_типа или объявлением_подтипа, включающим спецификацию предиката, то:
если выполнение проверок требуется политикой утверждения Static_Predicate (см. 11.4.2), и объявление включает спецификацию Static_Predicate, то проверки предикатов активированы для подтипа;
если выполнение проверок требуется политикой утверждения Dynamic_Predicate (см. 11.4.2), и объявление включает спецификацию Dynamic_Predicate, то проверки предикатов активированы для подтипа;
в противном случае проверки предикатов деактивированы для подтипа, независимо от того, активированы ли проверки предикатов для других подтипов, упомянутых в объявлении;
- Если подтип определен объявлением типа, не содержащим спецификации предиката, то проверки предикатов активированы для подтипа, если и только если любые проверки предикатов активированы для родительских или предковых подтипов;
- Если подтип создан указанием_подтипа, отличным от одного из предыдущих случаев, то проверки предикатов активированы для подтипа, если и только если проверки предикатов активированы для подтипа, обозначенного меткой_подтипа;
- В противном случае проверки предикатов деактивированы для данного подтипа.
Для подтипа с прямо указанным предикатным аспектом можно указать следующий дополнительный определяемый языком аспект с помощью спецификации_аспекта (см. 13.1.1):
Predicate_Failure
Этот аспект должен быть указан выражением, который определяет действие, которое будет выполнено, когда проверка предиката завершается неудачно, потому что прямо указанный предикат аспект подтипа принимает значение False, как объяснено ниже.
Правила разрешения имен
Ожидаемый тип для выражения Predicate_Failure — String.
Правила законности
Выражение спецификации Static_Predicate должно быть статическим предикатом; то есть одно из следующих:
- статическое выражение;
- тест на принадлежность, в котором тестируемое_простое_выражение является текущим экземпляром, а его список_выбора_принадлежности соответствует требованиям статического теста на принадлежность (см. 4.9);
- выражение_с_выбором, в котором выбираемое_выражение является текущим экземпляром, а зависимые_выражения — статическими выражениями;
- вызов предопределенного оператора равенства или упорядочения, где один операнд — текущий экземпляр, а другой — статическое выражение;
- вызов предопределенного булева оператора и, или, исключающее_или или не, где каждый операнд является статическим предикатом;
- краткая форма управления, где оба операнда — статические предикаты; или
- выражение статического предиката в скобках выражение.
Для неполного подтипа предикат не должен быть указан.
Если предикат относится к подтипу, то этот предикат не должен упоминать ни один другой подтип, к которому применяется тот же предикат.
Подтип индекса, дискретный_диапазон ограничения_индекса или срез, или дискретное_определение_подтипа ограниченного_массива, объявления_входной_точки или спецификации_индекса_входной_точки не должны обозначать подтип, к которому применяются спецификации предикатов.
Префикс префикс ссылки_на_атрибут, у которого обозначитель_атрибута — First, Last или Range, не должен обозначать скалярный подтип, к которому применяются спецификации предикатов.
Дискретное определение подтипа спецификации_параметра_цикла не должно обозначать нестатический подтип, к которому применяются спецификации предикатов, или подтип, к которому применяются спецификации Dynamic_Predicate.
дискретный_выбор явного_массива_агрегат не должен обозначать нестатический подтип, к которому применяются спецификации предикатов.
В дополнение к местам, где обычно применяются Правила законности (см. 12.3), эти правила также применяются в закрытой части экземпляра генерируемого блока.
Динамическая семантика
Если любое из вышеуказанных Правил законности нарушено в экземпляре генерируемого блока, Program_Error поднимается в точке нарушения.
Для определения того, удовлетворяет ли значение предикатам подтипа S, выполняются следующие тесты в следующем порядке, до тех пор, пока один из тестов не завершится неудачно. В этом случае предикаты не удовлетворяются, и дальнейшие тесты не выполняются, или все тесты завершаются успешно, в этом случае предикаты удовлетворяются:
- сначала значение проверяется на соответствие ограничениям или исключениям нулевых значений S;
- затем:
если S — первый подтип, значение проверяется на соответствие предикатам родительских и предковых подтипов (если таковые имеются) S (в произвольном порядке), после преобразования значения к соответствующему родительскому или предковому типу;
если S определен указанием_подтипа, значение проверяется на соответствие предикатам подтипа, обозначенного меткой_подтипа указания подтипа;
- наконец, если S определен объявлением, к которому применяется одна или несколько спецификаций предикатов, предикаты оцениваются (в произвольном порядке) для проверки того, что все они возвращают True для данного значения.
Если проверки предикатов активированы для данного подтипа, то:
При преобразовании подтипа выполняется проверка, удовлетворяет ли операнд предикатам целевого подтипа, за исключением определенных преобразований представлений (см. 4.6). Кроме того, после нормального завершения и выхода из подпрограммы для каждого параметра in out или out, переданного по ссылке, выполняется проверка, удовлетворяет ли значение параметра предикатам подтипа фактического параметра. Для объекта, созданного с помощью object_declaration без явного инициализации expression, или с помощью неинициализированного allocator, если типы каких-либо частей имеют заданные аспекты Default_Value или Default_Component_Value, или какие-либо подкомпоненты имеют default_expressions, выполняется проверка, удовлетворяет ли значение созданного объекта предикатам номинального подтипа.
Если какая-либо из проверок предиката завершается неудачно, возникает исключение Assertion_Error, за исключением случаев, когда подтип, предикат которого напрямую задан и имеет значение False, также имеет аспект Predicate_Failure. В этом случае вычисляется указанное выражение Predicate_Failure expression; если вычисление выражения Predicate_Failure expression порождает исключение, то это исключение распространяется для указанной неудачи проверки предиката; в противном случае возникает исключение Assertion_Error с связанной строкой сообщения, определённой значением выражения Predicate_Failure expression. При отсутствии такого аспекта Predicate_Failure, для исключения Assertion_Error используется строка сообщения, определяемая реализацией.
Абзацы 32 и 33 были перемещены выше
ПРИМЕЧАНИЕ 1 Спецификация предиката не приводит к тому, что подтип считается ограниченным.
ПРИМЕЧАНИЕ 2 Статический предикат Static_Predicate, подобно ограничению, всегда имеет значение True для всех объектов подтипа, за исключением случаев неинициализированных переменных и других недопустимых значений. Динамический предикат Dynamic_Predicate, с другой стороны, проверяется, как указано выше, но может стать False в другое время. Например, предикат подтипа записи не проверяется при изменении подкомпонента.
ПРИМЕЧАНИЕ 3 Для базового подтипа скалярного типа предикаты не применяются; каждое значение скалярного типа T считается удовлетворяющим предикатам T'Base.
ПРИМЕЧАНИЕ 4 Выражения Predicate_Failure expression никогда не вычисляются во время вычисления теста на принадлежность (см. 4.5.2) или атрибута Valid (см. 13.9.2).
Примеры
Примеры применений предикатов к скалярным типам:
subtype Basic_Letter is Character -- См. A.3.2 для "basic letter".
with Static_Predicate => Basic_Letter in 'A'..'Z' | 'a'..'z' | 'Æ' |
'æ' | 'Ð' | 'ð' | 'Þ' | 'þ' | 'ß';
with Static_Predicate => Basic_Letter in 'A'..'Z' | 'a'..'z' | 'Æ' |
'æ' | 'Ð' | 'ð' | 'Þ' | 'þ' | 'ß';
subtype Even_Integer is Integer
with Dynamic_Predicate => Even_Integer mod 2 = 0,
Predicate_Failure => "Even_Integer должен быть кратен 2";
with Dynamic_Predicate => Even_Integer mod 2 = 0,
Predicate_Failure => "Even_Integer должен быть кратен 2";
Text_IO (см. A.10.1) мог бы использовать предикаты для описания некоторых распространенных исключительных ситуаций следующим образом:
with Ada.IO_Exceptions;
package Ada.Text_IO is
package Ada.Text_IO is
type File_Type is limited private;
subtype Open_File_Type is File_Type
with Dynamic_Predicate => Is_Open (Open_File_Type),
Predicate_Failure => raise Status_Error with "Файл не открыт";
subtype Input_File_Type is Open_File_Type
with Dynamic_Predicate => Mode (Input_File_Type) = In_File,
Predicate_Failure => raise Mode_Error
with "Невозможно прочитать файл: " & Name (Input_File_Type);
subtype Output_File_Type is Open_File_Type
with Dynamic_Predicate => Mode (Output_File_Type) /= In_File,
Predicate_Failure => raise Mode_Error
with "Невозможно записать в файл: " & Name (Output_File_Type);
with Dynamic_Predicate => Is_Open (Open_File_Type),
Predicate_Failure => raise Status_Error with "Файл не открыт";
subtype Input_File_Type is Open_File_Type
with Dynamic_Predicate => Mode (Input_File_Type) = In_File,
Predicate_Failure => raise Mode_Error
with "Невозможно прочитать файл: " & Name (Input_File_Type);
subtype Output_File_Type is Open_File_Type
with Dynamic_Predicate => Mode (Output_File_Type) /= In_File,
Predicate_Failure => raise Mode_Error
with "Невозможно записать в файл: " & Name (Output_File_Type);
...
function Mode (File : in Open_File_Type) return File_Mode;
function Name (File : in Open_File_Type) return String;
function Form (File : in Open_File_Type) return String;
function Name (File : in Open_File_Type) return String;
function Form (File : in Open_File_Type) return String;
...
procedure Get (File : in Input_File_Type; Item : out Character);
procedure Put (File : in Output_File_Type; Item : in Character);
...
-- Аналогично для всех остальных подпрограмм ввода и вывода.