вторник, 18 июня 2024 г.

Маршрутизация разделов

Маршрутизация — это способ перечислить существующие разделы и их расположение, свойства, версии, и определить доступность одних разделов и их подразделов для других. Последнее позволяет повысить переносимость, а также контроль и безопасность, включая защищённую работу с недоверенными разделами.

sections some {
  
  /* Необязательное перечисление имён как оглавление
  name ( math, path, io, sandbox/io, storage, random.pseudo, editor, 
         random, cypher, untrusted/editor, fileman, compress)
  */

  // расположение доступных разделов
  locate (
    //простейшее указание по одному
    math = file("base/math.def");
    
    //группа разделов по маске через переменную name
    (path, io, sandbox/io, storage, random.pseudo, editor) 
      = file("base/"-name-".def");
    
    //файл может содержать несколько разделов
    (random, cypher) in file("base/cypher.defs") as section(name-".2");
    
    untrusted/editor := file("trash/editor.def");
    fileman := file("files/fileman.def");

    //заглушка для раздела
    compress := stub of file("base/compress.def")
  )
  
  // перечисляет ограниченные по умолчанию разделы 
  restricted ( path )
  
  /* указание доступности для импорта одних разделов другими.
    отсутствие прямого импорта не ограничивает косвенный импорт 
    и косвенные вызовы */
  allow (
    // редактор может получить доступ только к указанному файлу
    (path) for (editor)
    /* хранилище предоставляет ограниченный доступ к системе,
      но само имеет полный доступ для его воплощения */
    (path+all) for (storage)
    
    /* под видом предоставления недоверенному редактору
      «полного» доступа к произвольным файлам даёт путь в изолированную
      песочницу, подменив раздел ввода-вывода */
    (path+all, io = sandbox/io) for (untrusted/editor)
    
    /* те разделы, что не указаны, видны всем (other for all)
      разделы с подразделами не могут входить в эту группу,
      так как подразделы и нужны для разграничения ответственности */
  )
  
  // отсутствие опциональных разделов
  disable (
    (compress) for (fileman)
  )
  
  // строгий запрет на прямое и косвенное использование разделов 
  deny (
    // настоящий ГПСЧ не должен использовать ввод 
    (io) for (random/pseudo)
  )

}

Строгий запрет на использование одних разделов другими подразумевает и довольно сложный запрет на прямой или косвенный вызов процедурных переменных (полиморфных объектов), находящихся в других разделах, так как это может привести к косвенному переносу запрещённого функционала. Возможно использование процедурных переменных, лежащих внутри самого раздела, но запрещено менять их за пределами места начализации переменных в этом разделе. Также возможно явное разрешение на вызов сторонних процедур через формальные параметры подпрограмм раздела. Без возможности менять внутренние переменные это не позволяет провести внедрение запрещённого функционала в сам раздел, а косвенное применение всё равно в конечном итоге производится из вызова, которому это разрешено.

среда, 5 июня 2024 г.

Ошибочное состояние

Правильное понимание ошибочного состояния способствует созданию более ошибкоустойчивого языка, а разработчику позволяет создавать более надёжный код (даже без такого языка).

Ближайшим аналогом из других языков является термин «неопределённое поведение»[0] с поправкой на правильно выставленный приоритет в отношении понимания и обработки.

Ошибочное состояние — это то состояние программы, которое прогнозируемо возникает в результате выхода за пределы применимости правил языка, а также дополнительно заданных правил кода, составленных программистом-пользователем языка через утверждения. Это может произойти только из-за ошибки в коде, причиной чему, в том числе, может быть ошибочное представление программиста о правилах.

Ключевая особенность ошибочного состояния заключается в том, что любое воплощение поведения исполнителя кода при достижении или предсказании этого состояния не влияет на поведение правильного кода, то есть соблюдающего правила языка. При адекватно заданных правилах наличие понятия ошибочного состояния в языке не приводит к появлению дополнительных ошибок в коде, как часто ошибочно считается. Наоборот — ошибочное состояние возникает из-за логических ошибок в коде, и поэтому наличие этого понятия важно для возможности обнаружения и устранения ошибок. И хотя возможно такое определение языка, при котором программа никогда не сможет выйти за пределы применимости его правил, такой язык не приведёт к устранению логических ошибок программы, а наоборот, усложнит их выявление и предотвращение. Нельзя автоматически выявлять ошибку в том поведении, на которое может опираться разработчик, вписывая его в основную логику. В общем случае исполнитель языка не сможет отличить намеренную логику от случайной ошибки.

Примеры возможностей, приводящих к ошибочному состоянию в случае отсутствия явного учёта состояния правильности:

  • Обращение к массиву по индексу, выходящему за его пределы
  • Обращение по пустому указателю
  • Обращение по недопустимому адресу в низкоуровневых операциях с данными
  • Арифметическое переполнение допустимого диапазона чисел — и целых, и дробей
  • Деление на 0
  • Чтение значения переменной с ошибочным значением (не- или де-начализированной)
  • Преобразование типа для недопустимого значения
  • Получение ложного значения в утверждении
  • Возможность достижения потоком выполнения места, помеченного как недостижимое
  • Фактическое зацикливание или превышение ограничения количества вычислений
  • Неявные константные выражения
  • Бесполезные выражения и инструкции, не документирующие и не оказывающие влияния на состояние

Особенности адекватного воплощения поведения при ошибочном состоянии тесно связаны с областью применения кода, а также возможностями воплотителей языка, включая свойства целевой платформы. Это и приводит к тесной связи между ошибочным состоянием и неопределённым поведением, то есть поведением, не заданным в определении языка. Поскольку в общем случае невозможно выбрать единственный вариант, который бы одинаково хорошо подходил всем применениям, попытка избавиться от неопределённости приведёт лишь к невозможности получить наилучшее решение для всех. Также, это не приведёт к повышению ошибкоустойчивости воплощений, поскольку отсутствие возможностей у воплотителей влияет на них гораздо больше любых требований к ним.

Ключевое — предоставление свободы для возможности выбора лучшего поведения в ошибочном состоянии важней одинакового, которое чаще всего является ложной целью. Так же бессмысленна попытка перечислить все возможные способы воплощения поведения, так как их число, и тем более их количественные характеристики не поддаются адекватному подсчёту. Полезно лишь предложить возможные сценарии в качестве приложения.

Примеры возможного воплощения поведения при ошибочном состоянии:

  • Статическое выявление как ошибки трансляции для простых случаев или при продвинутом анализе. В отличий от опциональных предупреждений, которые могут быть ложно-положительными, выявление ошибки допустимо только для достоверных нарушений правил языка с учётом контекста применения. Поиск нарушений ведётся во все ветках кода без учёта фактической достижимости их выполнения.
  • Аварийное завершение подзадачи в итоге проверок во время исполнения программы. Подходит для отладки и для быстрых проверок хорошо отлаженного кода в выпускной версии.
  • Регистрация ошибки в системе и пометка выходного значения как ошибочного. Другие вычисления с ошибочными значением тоже порождают ошибочное значение, возможно, кроме тех, что приводят к определённому значению вне зависимости от другого значения (e * 0, e & false). Если ошибочное значение добирается до существенной развилки или вывода данных, то происходит аварийное завершение, так как дальнейший учёт ошибочности не представляется возможным. Если ошибочное значение оказывается незадействованным или уничтоженным, то программа может продолжить нормальное выполнение. Этот подход позволяет предотвратить некоторые избыточные падения, оставляя возможность исправления ошибки после получения отчёта о зарегистрированных ошибках.
  • Максимизация случайного поведения. Подходит для отладки того, что сложней диагностировать аварийной остановкой, например, работу с неначализированными переменными. Изменчивое поведение свидетельствует об ошибках и препятствует закреплению случайных характеристик текущей версии исполнителя в ошибочном коде как чего-то ожидаемого[1].
  • Максимизация детерминированности поведения, которое даёт наиболее вероятную правильность. Подходит для выпускной версии, но будет полезен только в паре с предыдущим. Если нужно выбрать что-то одно, лучше выбрать предыдущий.
  • Приостановка выполнения и ожидание ответа пользователя, который может выбрать наиболее подходящее решения на основе анализа конкретной ситуации. Подходит для длительных задач личного характера.
  • Предотвращение нарушений, но без аварийной остановки кода-нарушителя, а возвратом значения ошибки, то есть преобразования ошибки кода в специальное значение, например, ошибку ввода. Подходит для повышения устойчивости кода, менее критичного к последствиям ошибок кода, и в тех подзадачах, где допустимо некоторое значение по умолчанию.
  • Отсутствие специальной реакции, как и необходимости обеспечения детерминированности. Такая возможность — это лишь признание сложности диагностики некоторых ошибок, например, зацикливания, но без требования отказа от их диагностики. Применимо не только к простым трансляторам, но и тем, что работают в связке с системами доказательства правильности, позволяющих, как минимум, доказать отсутствие в коде возможности достижения ошибочного состояния. В таком случае самому транслятору нет необходимости в собственной диагностике ошибок, и потому может быть проще, что важно, если и правильность самого транслятора должна быть доказана.

Сноски

[0] Не исключено, что неудачно выбранное название и привело к тому, что большинство разработчиков понимают эту тему неточно в той или иной степени.
[1] Противодействие закону Хирума. Именно этот закон приводит многих разработчиков к ошибочному выводу о том, что ошибочное состояние/неопределённое поведение является причиной дополнительных ошибок, ведь у них раньше работало, что достигалось ненадёжным подходом правки кода, пока не заработает, вместо устранения всех потенциально выявляемых ошибок.

понедельник, 21 ноября 2022 г.

Ансамбль языков

Хотя проект языка безопасного кодирования сосредоточен непосредственно на нём, для лучшего достижения цели безопасности необходимо согласованное взаимодействие разных языков в общей системе. Не пытаясь объять необъятное, следует всё же учитывать потенциальную возможность взаимодействия и учитывать его в дизайне языка.

Часть языков являются не самостоятельными единицами, а скорее частью разделяемого описания языка (модульность описания). Они могут входить в состав сразу нескольких смежных языков. Удобно это и с точки зрения организации описания основного языка. Другие языки служат другим задачам в рамках более широких потребностей.

Наличие широких потребностей не должно приводить к попытке объединить их все в одном языке. Разные потребности могут приводить к противоречивым требованиям, которые невозможно адекватно воплотить в рамках одного языка. С другой стороны простое игнорирование потребностей приводит лишь к тому, что появляются проекты, которые способны их удовлетворить, но уже не так, как это требуется в рамках защищённой системы, а как придётся.

Языки:

  1. Разъёмно-интерфейсный
    1. Описания типов
  2. Разметка данных
  3. Низкоуровневый системный
    1. Версия для генерации кода
  4. Среднеуровневый системный
  5. Высокоуровневый надёжный
  6. Спецификационный-верифицирующий
  7. Преобразующий
  8. Архитектурный
  9. Быстрокодовый(скриптовый)
    1. Для гибкой записи преимущественно поверхностного кода
    2. Интерфейсный интерактивный

Разъёмно-интерфейсный

Этот язык должен являться общей частью большинства языков, посредством чего они могут взаимодействовать друг с другом. Должен содержать две части. Основа подразумевает отсутствие типа-указателя, а вторую часть как раз составляют указатели. Основа без указателей нужна для организаци взаимодействия систем, не имеющих общей памяти.

Описание типов данных нужно не только для активных интерфейсов, но и пассивных данных.

Разметка данных

Простой инструмент для описаний данных, что полезно иметь отдельно от основного языка программирования. Должен быть связан с языком типов. Текстовая разметка должна быть лишь вспомогательной к двоичному формату для простой визуализации человеку.

Низкоуровневый системный

Этот язык нужен для представления кода на уровне, близком к машинному, в то же время сохраняя важные защиты высокоуровневых языков. Язык позволяет распространять код между недоверенными системами. Может служить промежуточным представлением обычных трансляторов.

Версия языка, предназначенная для использования в качестве целевого в кодогенерации. Нужна с целью защиты от некоторых ошибок и уязвимостей, возникающих в этой задаче. В первую очередь в такой версии должны отсутствовать константы-строки в произвольных выражениях для избежания подстановок произвольного кода при вставке неэкранированных данных.

Среднеуровневый системный

Это и есть основной язык, рассматриваемый здесь. Среднеуровневость относительно простого языка позволяет получить достаточно удобный инструмент для создания сложных систем, в то же время сохраняя хороший уровень контроля за происходящим в коде, и обеспечивая бо́льшую прозрачность отображения на машину, чем более хитроумные возможности высокоуровневых языков. Пригоден для систем настоящего времени.

Высокоуровневый надёжный

Язык для создания прикладного кода с упором на упрощение автоматизации. Может быть более сложным и иметь больше возможностей, ускоряющих разработку. Менее критичен в отношении ресурсоёмкости исполнения и предсказумости поведения, но не должен чересчур жертвовать проверками правильности в угоду расслабленному кодированию.

Спецификационный-верифицирующий

Опциональная часть других языков. Такой язык нужен для точного задания семантики и для проверки отдельных свойств кода. Лишён потребности в эффективном исполненини итогового кода, благодаря чему позволяет проще задавать правильный код, ведь многие особенности языков, приводящие к дополнительным ошибкам, обусловлены необходимостью в оптимизации.

Преобразующий[0]

Этот язык призван отражать изменения, которые происходят в коде, для возможности автоматизированной подстройки кода к изменяющимся зависимостям. Это похоже на механизм заплаток(патчей), но не на уровне плоского текстового формата, а на уровне языка — добавление и удаление функций и типов, переименование, изменение порядка и так далее.

Быстрокодовый(скриптовый)

Язык для поверхностного кодирования, для ситуативных задач. Для них требуется лёгкость ввода, и не требуется ни высокая проработанность, ни высокая производительность. В пределе такие программы могут терять актуальность даже после единственного исполнения. Уровень критичности к ошибкам сравнительно низок. Тем не менее при аккуратном проектировании возможна куда более вменяемая логика работы, чем для многих современных скриптовых языков, например, bash.

Сам быстрокодовый язык может служить интерактивным интерфейсом для программы. Даже когда это не так, наиболее удачной архитектурой пользовательской программы может считаться та, в которой пользовательский интерфейс является надстройкой над таким языком. Это позволяет легко переходить от непосредственных пользовательских действий к автоматизации и наоборот.


Сноски:
[0] Преобразования — это часть концепции поддержки наследия без раздувания сложности

пятница, 10 июня 2022 г.

Другие языки

Здесь хочется отметить некоторые сторонние языки программирования как из-за их схожести в постановке задач, так и из-за различия.

EctasyLang позиционируется как первый язык, спроектированный для облачных решений, но как по идеолоигии, так и по ряду конкретных решений напоминает формулируемый здесь язык защитного программирования.

четверг, 24 декабря 2020 г.

Исполнители языка

Для лучшего достижения целей надёжного программирования транслятору языка необходимо обладать заданными свойствами, которые сложно совместить в единой программе, поэтому целесообразно заложить несколько различных трансляторов, совместно позволяющих добиться поставленных целей. Также можно добавить, что стандарты надёжного программирования для уменьшения вероятности внесённых инструментами ошибок могут требовать сборки программы тремя независимыми трансляторами.

Можно выделить 4-е основные свойства, задающие назначение инструмента:

  1. Определяющий
  2. Юркий
  3. Доказанный
  4. Многоцелевой

Определяющий. Для избежания раночтений надёжному языку необходимо его чёткое определение. Пожалуй, самым чётким возможным определением, хотя бы и вспомогательным, является формальное описание. А полное формальное описание языка, хотя бы и гипотетически, позволяет получить из него пригодный для выполнения на машине исполнитель языка. Его целью является максимально понятное описание минимально размера, а не эффективность и удобство использования, поэтому кроме исключительных случаев такой транслятор не может использоваться разработчиком напрямую. Зато он может служить образцом для более сложных трансляторов и, в том числе, использоваться как часть тестовой системы или как формальная спецификация для транслятора с доказанной правильностью. Особенности: отсутствие подробностей в диагностике статических ошибок, падение итоговой программы при достижении динамически диагностирумых ошибок.

Юркий. Небольшой, простой и быстрый транслятор с минимальными внешними зависимостями бывает крайне полезен и позволяет расширить область применимости языка в качестве встраиваемого решения. Также может служить для самораскрутки проекта. Желательно предоставление исполнения кода непосредственно из исходных кодов.

Доказанный. Полное формальное доказательство правильности позволяет исключить из прагматичного транслятора ошибки относительно спецификации языка. Наличие такого транслятора можно считать обязательным для будущих проектов, требующих максимальной надёжности. Так как доказательство является сложной задачей, то для её облегчения или даже, вообще, возможности код транслятора нужно с самого начала писать под доказательство, выбирая для его воплощения такие решения, которые легче для доказательства, но могут отрицательно влиять на пользовательские качества. Поэтому такой транслятор не может совпадать с другими прагматическими трансляторами.

Многоцелевой. Это основной транслятор и он должен решать множество самых разных задач разработчика, во многом, связанных между собой:

  1. Поддержка нескольких исходных языков-спутников.
  2. Трансляция в разнообразные машинные языки и промежуточные представления.
  3. Языковые и машиноспецифичные оптимизации кода.
  4. Статический анализ для выявления ошибок и, желательно, проверка доказательства правильности.
  5. Преобразования исходного кода для перехода на новые решения.
  6. Отслеживание связей в коде.
  7. Подсчёт метрик кода.

Решение многих задач приводит к объёмности, сложности и неповоротливости такого транслятора. Из-за этого он не может быть ни быстрым, ни доказанным полностью, но может встраивать в себя остальные разновидности трансляторов, таким образом не лишая себя их сильных сторон.

воскресенье, 20 декабря 2020 г.

Свойства. Сопровождаемый

В типичном сценарии программный код пишется итерационно — сначала создаётся первичная версия рабочего кода, затем она постепенно дорабатывается для улучшения поддержки заданных свойств. Хорошо спроектированный язык может способствовать этой важнейшей модели разработки, тем самым позволяя уменьшить издержки и позволяя добиться большей надёжности при сопоставимых затратах на разработку на менее удачных языках.

Это означает, что язык по возможности не должен заставлять переписывать дорабатываемые части кода, а способствовать их минимальному изменению. Особенно когда это касается доработки прямолинейного кода до верифицированного и надёжно защищённого от ошибок входных данных. Также, язык должен обладать свойствами, которые позволяют легче преобразовывать код под изменяющиеся условия — подробней.

суббота, 23 сентября 2017 г.

Ловушки для создателя языка программирования

Практически каждый программист является создателем языков программирования. Каждый раз когда разработчик задумывается о том, почему в используемом языке чего-то не хватает или, наоборот, что-то мешается, он создаёт в своей голове образ изменённого языка, лучше соответствующего его желаниям. Такие образы могут содержать как дельные рационализаторские предложения, так и быть смесью неупорядоченных, возможно, противоречивых возможностей, заимствованных из других языков или желаний. За воплощение таких языков большинство разработчиков никогда не возьмётся, но, тем менее, такие упражнения вполне можно назвать созданием языка. Поэтому размышления о возможных ошибках при проектировании языка в контексте достижения правильности кода могут быть интересными многим, также позволяя лучше понять те или иные вопросы дизайна языка.

вторник, 20 июня 2017 г.

Целочисленные типы

В языке для написания критичного к ошибкам кода нужен особый подход к целочисленным типам, позволяющий уменьшить вероятность переполнения, а потому способствующий правильности программ.

Целочисленные типы должны быть разделены на две группы:
  1. Ограниченное число предопределённых типов, подходящих для большинства задач, и к которым применимы встроенные арифметические операции: +, -, *, /, %.
  2. Типы из разделов, для работы с которыми необходимо использовать функции из тех же разделов с соответствующими названиями: add, sub, mul, div, mod и т.д.

Разделение сделает более удобным, а потому и более желанным использование типов, для которых риск совершения ошибки ниже, оставив возможность использовать более редкие особые случаи, которые будут применяться только в тех задачах, где они нужны.

Предопределённые типы

В первую группу должны войти:
НазваниеОтрезок допустимых значений
int -(231 - 1) .. (231 - 1)
longint -(263 - 2) .. (263 - 2)

int и longint — знаковые, симметричные относительно 0 типы. Равенство по модулю максимального и минимального значений упрощает учёт правильных вычислений, в частности, гарантирует, что смена знака и значение по модулю всегда выполнимы без переполнения. Это важней желания вместить на одно допустимое значение больше, как это позволяет наиболее распространённый способ кодирования отрицательных чисел — дополнительный код. В большинстве случаев минимальное отрицательное (- 231, - 263) даже не нужно решения задачи, но непропорционально своему значению требует избыточного внимания в случае необходимости достижения полной правильности, из-за создания особого случая. Эти значения зарезервированы для использования в качестве неопределённого значения.

Главным целочисленным типом является int, так как он даёт хороший компромисс по предоставляемому отрезку чисел, достаточного для большинства задач, и возможностью эффективной обработки. В частности, количество элементов массива не может превышать максимального значения int. longint нужен как из-за того, что в современных условиях нередки ситуации, где отрезка 32-битных значений всё же не хватает, например, для кодирования времени в наносекундах или обработки больших файлов, так и потому что расширенный отрезок чисел естественным образом возникает при вычислениях в полном множестве int.

byte занимает особое место — он нужен в первую очередь как строительный элемент, и сам по себе не является целочисленным типом, но легко может быть приведён к нему, равно как и в обратном направлении. byte соответствует отрезку значений 0 .. 28 - 1.

Остальные целочисленные типы других диапазонов вынесены из ядра языка, так как потребность в них ниже основных, что, однако, не препятствует нередкому злоупотреблению ними и может приводить к лишней потребности сопряжения разных ёмкостей значений, что служит дополнительным источником ошибок.

Беззнаковые типы по размеру совпадающие с int и longint не включены в предопределение по схожей причине. Кроме того, близость 0 — нижней границы повышает вероятность переполнения даже в случае вычисления малых значений.

***

В арифметических выражениях, в которых смешиваются операнды разных целочисленных типов, операнды меньшего типа рассматриваются как эквивалентные операнды наибольшего. Вычисления выражений для операндов int и longint происходят в диапазоне longint, кроме тех операций с int, которые гарантировано не приводят к увеличению необходимого диапазона, например, сложения целых, приведённых от byte, или просто при делении.

Контроль переполнения происходит как в выражениях, так и при сохранении значения. Если диапазон приёмника меньше диапазона типа выражения, требуется явное преобразование типа. Если такой же или больше, то не требуется, но контроль переполнения выполняется в любом случае. Такое правило позволяет уменьшить вероятность переполнений и не требует избыточных явных приведений типа.

Например, следующие операции, которые во многих других языках могут содержать ошибки переполнения при определённых значениях переменных, в защитном языке оказываются всегда правильны:

Защитный языкJava
var (a,b,c,m int; d longint; f bool)

a = max(int) / 2; 
b = a + 2; 
c = 300;
m = min(int);

f = a + b < c; // false
d = a * b; // 1152921504606846975
c = b * 2 / 3; // 715827883
m = -m;        // 2147483647
int a, b, c, m; long d; boolean f;

a = Integer.MAX_VALUE / 2;
b = a + 2;
c = 300;
m = Integer.MIN_VALUE;

f = a + b < c; // true
d = a * b;     // -1
c = b * 2 / 3; // -715827882
m = -m;        //-2147483648

Типы предопределённых разделов

Вторую группу целочисленных типов должны представить типы из состава специальных разделов, отличающихся знаковостью и разрядностью гарантированных диапазонов. Разделы должны предоставлять почти одинаковые наборы функций для работы с собственными целочисленными типами. Имена разделов можно вывести из синтаксического уравнения – имя = [u]int(8|16|32|64), где u обозначает беззнаковость, а число, естественно — разрядность типа. Диапазоны допустимых значений определяются по формулам - [0 .. 2n-1] для беззнаковых и [-2n-1 .. 2n-1-1] для чисел со знаком, так как они должны быть закодированы через двоичное дополнение.

Объявления разделов:
type (t) // сам целочисленный тип

const (min, max) // минимальное и максимальное значение

// Группа функций, которые трактуют переполнение как ошибку.
// Если при их выполнении не было явно получено состояние правильности,
// то в случае переполнения они завершают выполнение программы.
func add(addend1,  addend2 t)    (sum t);
func sub(minuend,  subtrahend t) (difference t);
func mul(factor1,  factor2 t)    (product t);
func div(divident, divisor t)    (ratio t);
func mod(divident, divisor t)    (remainder t);

// Группа процедур, для которых переполнение сопровождается воображаемым
// отбросом старших разрядов.
proc mod_add(addend1, addend2,    > sum t)        (overflow bool);
proc mod_sub(minuend, subtrahend, > difference t) (overflow bool);
proc mod_mul(factor1, factor2,    > product t)    (overflow bool);

// Функции для преобразования к основным типам и обратно. Поскольку при
// этом может возникнуть переполнение, то функции также подразумевают
// явное взятие правильности, либо завершение работы.
func to_byte   (value? t) (result byte);
func to_int    (value? t) (result int);
func to_longint(value? t) (result longint);

func from_byte   (value byte)    (result t);
func from_int    (value int)     (result t);
func from_longint(value longint) (result t);

// Функции интерпретации стандартных типов как соответствующих их
// диапазону типов из разделов противоположной знаковости и наоборот.

// в разделе uint64:
func as_int64  (value t) (result int64.t);
func as_longint(value t) (result longint);
func as_t(value longint) (result t);

// в разделе uint32:
func as_int32  (value t) (result int32.t);
func as_int    (value t) (result int);
func as_longint(value t) (result longint);
func as_t    (value int) (result t);

// в разделе int8:
func as_byte   (value t) (result byte);
func as_int    (value t) (result int);
func as_longint(value t) (result longint);
func as_t   (value byte) (result t);

Битовые операции для встроенных целочисленных типов не предусмотрены как неуместные. Вместо них вводятся:

  1. Встроенная функция для возведения в степень func pow(v, n int) (res longint) или отдельная операция v**n. Выбор будет сделан позднее.
  2. Типы-множества, которые можно приводить к целочисленным и обратно и о которых подробней будет рассказано в отдельной заметке.

Дополнительные материалы:

  1. Danger – unsigned types used here!
  2. INT14-C. Avoid performing bitwise and arithmetic operations on the same data.