OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Четверг, 28 Март, 2024 21:30

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 11 ] 
Автор Сообщение
 Заголовок сообщения: Как починить тип SIZE?
СообщениеДобавлено: Суббота, 02 Октябрь, 2021 14:40 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
Если мы правильно перевели сообщение о языке, то имеет место такой факт:
Цитата:
Хотя тип 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. Но при этом не трогать сигнатуры процедур.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Суббота, 02 Октябрь, 2021 17:11 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
Да, блин, задача какая-то эпическая. Мало того, что вряд ли обойдётся без глобального исследования (т.е. изучать процедуры и их вызовы), так ещё и семантически проблемы возникают. Например, на сегодня SIZEOF(T) возвращает SIZE. Но ведь куча - это тоже объект в памяти, и он может занимать более половины диапазона адресов. Но тогда у неё получается отрицательный размер. Это то, что мы имеем на сегодня, и чтобы в этом разобраться, нужно изучать устройство диапазонов допустимых адресов на всех платформах.

Это я даже не говорю про исправление размера объекта, а про те проблемы, которые есть уже сейчас с типом SIZE. Если же речь идёт об исправлении, то мы заходим в тупик с таким невинным кодом:
Код:
PROCEDURE sumOfElements(CONST arr: ARRAY OF CHAR);
VAR i: SIZE; VAR res : UNSIGNED64;
BEGIN
  res := 0;
  FOR i := 0 TO LEN(arr)-1 DO
    INC(res,ORD(arr[i])) END END sumOfElements

Т.е., нельзя иметь массив длиннее, чем MAX(INTEGER), и это только часть проблемы. Если мы попытаемся
превратить i в SIGNED32, то мы сломаемся на 64 разрядной платформе, если речь идёт о переборе объектов в памяти (например, если это алгоритм сборщика мусора). Но не только. Мы также сломаемся, если попытаемся создать строку размером более 2 или 4Гб (или сколько там получается?). А такие строки в природе можно себе представить - какие-нибудь базы данных в памяти вполне могут быть представлены как ARRAY OF CHAR.

Похоже, что решение этой задачи нужно дробить на много этапов, на каждом из которых придётся делать сколько-то ручной работы. Поэтому, видимо, нужно начать с того, чтобы изучить примерно следующее:
  • сколько есть массивов в 64-разрядных платформах, которые на практике могут иметь длину более MAX(SIGNED32) - это решается встраиванием отслеживания в оператор NEW (в простейшем случае ASSERT(size<MAX(SIGNED32)) - тогда программа сразу упадёт, когда столкнётся с такой ситуацией).
  • сколько случаев, когда в место типа ADDRESS записывается значение типа SIZE (это можно попробовать сделать модификацией компилятора, хотя я не уверен, что это будет легко)
С этими цифрами на руках можно попытаться понять, насколько достижима стратегия, при которой автоматически обрабатывается то, что возможно, а дальше ручной анализ и ручной рефакторинг.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Суббота, 02 Октябрь, 2021 22:46 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
В общем, на исходе дня я не вижу решения. Возможный вариант - перестать лицемерить, признать, что защиты от перепутывания числа с адресом нет, и не называть тип SIZE типом SIZE, поскольку он на самом деле не обозначает размер чего-то в памяти. Можно даже слить тип SIZE с типом INTEGER, поскольку разницы между ними всё равно никакой почти что нет (в меру моих незнаний). Чтобы энтропия не возрастала, можно перевести INTEGER "системноЗависимоеЦелое", а SIZE - "СистемноЗависимоеЦелое" (с большой буквы). Тогда они останутся разными типами. В перспективе автоматически вставить явные преобразования везде, где происходит превращение SIZE в ADDRESS и обратно.

Тогда остаётся из плохого (для ЯОС), что название ужасно длинное. Оно так и должно быть - потому что в прикладном коде зависимость арифметики от платформы должна быть выписана явно (во всяком случае, таково моё текущее мнение). Т.е. тот, кто применяет этот тип, должен постараться сначала этого избежатЬ, а не пихать этот тип везде. Короткие названия имеют типы платформо-независимые, такие, как цел32 и бцел32.

Вместо системно-зависимого можно было бы, слегка приврав, сказать "машинноеЦелое" или даже мцел, бмцел, или целШС (целое ширины слова). Но выбор перевода не относится к теме данного форума.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Суббота, 02 Октябрь, 2021 22:53 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
Для справки - ссылка на новость о переименовании числовых типов, которое было сделано в 2019 году.

http://cas.inf.ethz.ch/news/1

Коммит называется "Merged rename branch", от 10.09.2019

До этого важным коммитом было "Merged size branch", когда LEN стала возвращать тип SIZE, 20.08.2019 (и это я считаю большой ошибкой, когда был заложен фундамент того, что размер и индекс представлены одним типом).

Хотя, возможно, до меня начало доходить, откуда ноги растут:

https://stackoverflow.com/questions/255 ... ize-t-in-c

"As an implication, size_t is a type guaranteed to hold any array index"
Т.е. скопировали size_t с Си. Но у Оберона свой путь, поэтому его сделали знаковым, а не беззнаковым.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Воскресенье, 03 Октябрь, 2021 12:34 

Зарегистрирован: Пятница, 11 Январь, 2019 19:26
Сообщения: 293
Откуда: Russia
Зачем итерировать адреса через знаковый тип size?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Воскресенье, 03 Октябрь, 2021 14:02 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
Зачем вот такое компилируется без предупреждений?

Код:
MODULE Proba;

PROCEDURE z*;
VAR a :ADDRESS; b :SIZE;
BEGIN
  a:=b; b:=a;
END z;

END Proba.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Воскресенье, 03 Октябрь, 2021 14:42 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
Хотя и такое компилируется:
Код:
MODULE Proba;

PROCEDURE z*;
VAR a :ADDRESS; b :UNSIGNED32; c : SIZE; d : SIGNED32;
BEGIN
  a:=b; a:=c; a:=d;
  b:=a; b:=c; b:=d;
  c:=a; c:=b; c:=d;
  d:=a; d:=b; d:=c;
END z;

END Proba.

И только одна ошибка, на d:=b
Ясно теперь. Проблема в том, что я думал, что в сказку попал, а оказывается, не в сказку :lol:


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Воскресенье, 03 Октябрь, 2021 17:32 

Зарегистрирован: Пятница, 11 Январь, 2019 19:26
Сообщения: 293
Откуда: Russia
budden писал(а):
Зачем вот такое компилируется без предупреждений?
Затем, что эти типы совместимы по присваиванию в обоих направлениях
- отображаются друг на друга без потери точности.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Воскресенье, 03 Октябрь, 2021 17:38 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
Спасибо, всё понятно.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Воскресенье, 03 Октябрь, 2021 18:02 

Зарегистрирован: Пятница, 11 Январь, 2019 19:26
Сообщения: 293
Откуда: Russia
budden писал(а):
И только одна ошибка, на d:=b
Очевидно, что количество ошибок будет зависеть от архитектуры и у меня здесь 5 ошибок.
Очевидно, что, в соответстии с репортом, беззнаковый тип address поглощает знаковые и беззнаковые типы меньшей и равной размерности (без потери точности ), обратное поглощение возможно, естественно, только некоторыми типами в соотвествии с табличкой, указанной в этом же репорте. Так для 64 битного адреса это тип size и unsigned64.
Проблема ещё в том, что на одной и той же архитектуре в разных режимах адрес может быть разной размерности и, в общем случае, вероятно, не получится сделать жесткое ограничерие без привлечения конверсии типов


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Как починить тип SIZE?
СообщениеДобавлено: Воскресенье, 03 Октябрь, 2021 20:22 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
А в чём смысл того, что знаковый SIGNED32 поглощает адрес (на 32-разрядных платформах)?


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 11 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 7


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2024, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB