Если мы правильно перевели сообщение о языке, то имеет место такой факт:
Цитата:
Хотя тип SIZE - со знаком, а ADDRESS - без знака, эти типы считаются совместимыми по присваиванию в обоих направлениях.
Учитывая, что тип SIZE интенсивно применяется, получается дыра в типобезопасности размером почти с адресную арифметику, а то и шире. У меня уже был один случай, когда из-за этого мой код не работал (перепутал параметр и вместо кода записи в реестре строк передавал адрес записи, или наоборот). С учётом того, что эта проблема была закопана и проявлялась только косвенно, это доставило немало неприятных минут (или часов, не помню уже).
По смыслу SIZE - это разность адресов и именно так она у меня и переведёна в настоящее время. Сам по себе адрес отрицательным быть не может. Поэтому присваивание от SIZE к адресу смысла не имеет: отрицательные адреса ничему не соответствуют, а положительные не покрывают всего диапазона адресов. Обратная ситуация не лучше: адреса, превышающие MAX(SIGNED32), превратятся в отрицательные значения SIZE. Полагаясь на поведение при переполнении, всё же этим типом иногда можно пользоваться. Хотя в языке я не заметил, чтобы поведение при переполнении было определено, значит, по-хорошему, им пользоваться нельзя.
Кроме использования для вычислений над адресами и размерами объектов в байтах, SIZE используется в качестве счётчика. Таким образом, мы можем написать что-то такое:
Код:
PROCEDURE z(ptr: POINTER TO myRecord, arr: ARRAY OF INTEGER);
VAR counter : SIZE;
VAR adrOfNextRecord : SIZE;
BEGIN
adrOfNextRecord := ADDRESS(ptr); (* или VAL(ADDRESS,ptr), не помню и не суть.
Но случайно результат оказался MAX(INTEGER)-1 *)
FOR counter := 0 TO LEN(arr)-1 DO
adrOfNextRecord := adr + counter (* ошибка размерности: сложили счётчик с адресом *)
(* неопределённое поведение: переполнение целого числа *)
END;
RETURN VAL(POINTER TO myRecord, adrOfNextRecord)
END z
Я не каждый месяц использую VAL и ADDRESS, поэтому мог ошибиться с преобразованиями,
но я думаю, смысл ясен: совместимость по присваиванию порождает ряд ошибок.
Как починить? Я бы сделал следующее:
- делим тип SIZE на два. Один называется "счётчик" и полностью эквивалентен INTEGER и используется для счётчиков. Хотя я бы сделал рискованный шаг и сделал бы его эквивалентным SIGNED32 - ведь плохо, когда счётчик является платформо-зависимым - такой счётчик нужен лишь в отдельных случаях. Второй тип называется "разностьАдресов"
- Значению типа ADDRESS (адресВПамяти) можно присваивать только другой тип ADDRESS.
- Можно явно преобразовывать беззнаковые типы к типу ADDRESS
- К типу адресВПамяти можно прибавлять, а можно вычитать из него значение типа "разностьАдресов", и результатом является адресВПамяти
- Разность адресов можно складывать с разностьюАдресов и умножать на целое число - получится опять разностьАдресов
- SIZEOF () возвращает неотрицательную разностьАдресов (как следствие, никакой объект не может занимать более половины адресного пространства - это чуднО, но более-менее терпимо. Можно решить за счёт дальнейшего усложнения системы типов или установить правила переполнения).
Далее придётся написать инструмент для автотрансляции, который для каждого применения типа SIZE решит, в какой из двух типов его превратить. Пока не уверен, что это будет легко. Общая идея - смотреть, как применяется данная переменная и решать, превратить ли её в SIGNED32 (если это чисто счётчик) или в "разностьАдресов" (если она участвует в вычислениях с адресами). В последнем случае трудность состоит в анализе вызовов процедуры, что требует глобального анализа всей кодовой базы. Мой текущий инструментарий и с первой задачей едва справится, а со второй - вообще не справится.
SIZE встречается в соврменных исходниках ЯОС 13000 раз, а ADDRESS - 8000 раз. Т.е. делать вручную малореально.
Как более простой вариант - во всех местах, где происходит присвоение SIZE к ADDRESS и обратно, вставить явное преобразование - это должно быть нетрудно, а далее грепом найти эти преобразования и, возможно, руками или автоматически разметить, что в этом месте нужно превратить SIZE в разностьАдресов. В остальных случаях превратить в SIGNED32. Но при этом не трогать сигнатуры процедур.