Глава 11 Партионная компиляция (ocamlc)
- 11.1 Обзор компилятора
- 11.2 Опции
- 11.3 Модули и файловая система
- 11.4 Распространённые ошибки
- 11.5 Справочник по предупреждениям
В этой главе описывается партионный компилятор OCaml ocamlc, который компилирует исходные файлы OCaml в байт-кодовые объектные файлы и связывает эти объектные файлы для создания автономных исполняемых файлов байт-кода. Эти исполняемые файлы затем запускаются интерпретатором байт-кода ocamlrun.
11.1 Обзор компилятора
Команда ocamlc имеет интерфейс командной строки, похожий на интерфейсы большинства компиляторов C. Она принимает несколько типов аргументов и обрабатывает их последовательно после обработки всех опций:
- Аргументы, оканчивающиеся на .mli, рассматриваются как исходные файлы интерфейсов единиц компиляции. Интерфейсы определяют имена, экспортируемые единицами компиляции: они объявляют имена значений с их типами, определяют публичные типы данных, объявляют абстрактные типы данных и т.д. Из файла x.mli компилятор ocamlc создаёт скомпилированный интерфейс в файле x.cmi.
- Аргументы, оканчивающиеся на .ml, рассматриваются как исходные файлы реализаций единиц компиляции. Реализации предоставляют определения для экспортируемых имен единицы и также содержат выражения, которые должны быть оценены для их побочных эффектов. Из файла x.ml компилятор ocamlc создаёт скомпилированный байт-код объекта в файле x.cmo.
Если существует файл интерфейса x.mli, реализация x.ml проверяется по отношению к соответствующему скомпилированному интерфейсу x.cmi, который предполагается существующим. Если интерфейс x.mli не предоставлен, компиляция x.ml создаёт скомпилированный файл интерфейса x.cmi в дополнение к скомпилированному объектному коду x.cmo. Созданный файл x.cmi соответствует интерфейсу, экспортирующему всё, что определено в реализации x.ml.
- Аргументы, оканчивающиеся на .cmo, рассматриваются как скомпилированный байт-код объекта. Эти файлы связываются вместе, вместе с объектными файлами, полученными при компиляции аргументов .ml (если таковые имеются), и стандартной библиотекой OCaml, для создания автономной исполняемой программы. Порядок представления аргументов .cmo и .ml в командной строке важен: единицы компиляции инициализируются в этом порядке во время выполнения, и использование компонента единицы до его инициализации является ошибкой на этапе линковки. Следовательно, данный файл x.cmo должен предшествовать всем файлам .cmo, которые ссылаются на единицу x.
- Аргументы, оканчивающиеся на .cma, рассматриваются как библиотеки объектного байт-кода. Библиотека объектного байт-кода упаковывает в один файл набор объектных байт-кодовых файлов (.cmo). Библиотеки строятся с помощью ocamlc -a (см. описание опции -a ниже).
- Аргументы, оканчивающиеся на .c, передаются компилятору C, который генерирует объектный файл .o (.obj в Windows). Этот объектный файл связывается с программой, если установлен флаг -custom (см. описание -custom ниже).
- Аргументы, оканчивающиеся на .o или .a (.obj или .lib в Windows) считаются объектные файлы и библиотеки C. Они передаются C-линкеру при линковке в режиме -custom (см. описание -custom ниже).
- Аргументы, оканчивающиеся на .so (.dll в Windows) считаются динамическими библиотеками C (DLL). Во время линковки они проверяются на внешние функции C, на которые ссылается код OCaml, и их имена записываются в сгенерированный исполняемый файл байт-кода. Система выполнения ocamlrun затем загружает их динамически при запуске программы.
Выводом фазы линковки является файл, содержащий скомпилированный байт-код, который может быть выполнен интерпретатором байт-кода OCaml: команда с именем ocamlrun. Если имя файла, созданного на фазе линковки, a.out, команда
ocamlrun a.out arg1 arg2 … argn
выполняет скомпилированный код, содержащийся в a.out, передавая ему аргументы в виде строк символов arg1 до argn. (См. главу 13 для более подробной информации.)
На большинстве систем файл, созданный фазой линковки, может быть запущен напрямую, как в:
./a.out arg1 arg2 … argn
Созданный файл имеет установленный исполняемый бит и сам запускает интерпретатор байт-кода.
Компилятор может выводить некоторую информацию о своих внутренних этапах. Он может выводить файлы .cmt для реализации единицы компиляции и .cmti для сигнатур, если ему передана опция -bin-annot (см. описание -bin-annot ниже). Каждый такой файл содержит типизированное абстрактное синтаксическое дерево (AST), которое создаётся во время процедуры проверки типов. Это дерево содержит всю доступную информацию о местоположении и конкретном типе каждого термина в исходном файле. AST является частичным, если проверка типов не удалась.
Эти файлы .cmt и .cmti обычно полезны для инструментов инспектирования кода.
11.2 Опции
Следующие опции командной строки распознаются ocamlc. Опции -pack, -a, -c, -output-obj и -output-complete-obj являются взаимоисключающими.
- -a
- Создать библиотеку (.cma файл) из объектных файлов (.cmo файлы), указанных в командной строке, вместо их компоновки в исполняемый файл. Имя библиотеки должно быть задано с помощью опции -o.
Если в командной строке передаются опции -custom, -cclib или -ccopt, эти опции сохраняются в результирующей библиотеке .cma. Затем компоновка с этой библиотекой автоматически добавляет обратно опции -custom, -cclib и -ccopt, как если бы они были предоставлены в командной строке, если не указана опция -noautolink.
- -absname
- Принудительно отображать абсолютные пути к именам файлов в сообщениях об ошибках.
- -annot
- Устарело начиная с OCaml 4.11. Пожалуйста, используйте -bin-annot вместо этого.
- -args filename
- Читать дополнительные аргументы командной строки, завершающиеся символом новой строки, из filename.
- -args0 filename
- Читать дополнительные аргументы командной строки, завершающиеся нулевым символом, из filename.
- -bin-annot
- Выгружать подробную информацию о компиляции (типы, привязки, хвостовые вызовы и т.д.) в бинарном формате. Информация для файла src.ml (соотв. src.mli) помещается в файл src.cmt (соотв. src.cmti). В случае ошибки типа выгрузить всю информацию, выведенную анализатором типов до ошибки. Файлы *.cmt и *.cmti, созданные с помощью -bin-annot, содержат больше информации и значительно компактнее, чем файлы, созданные с помощью -annot.
- -c
- Только компиляция. Подавить этап компоновки компиляции. Исходные файлы преобразуются в скомпилированные файлы, но исполняемый файл не создается. Эта опция полезна для отдельной компиляции модулей.
- -cc ccomp
- Использовать ccomp в качестве компоновщика C при компоновке в режиме «пользовательского времени выполнения» (см. опцию -custom) и в качестве компилятора C для компиляции исходных файлов .c.
- -cclib -llibname
- Передать опцию -llibname компоновщику C при компоновке в режиме «пользовательского времени выполнения» (см. опцию -custom). Это приводит к тому, что данная библиотека C компонуется с программой.
- -ccopt option
- Передать данную опцию компилятору и компоновщику C. При компоновке в режиме «пользовательского времени выполнения», например, -ccopt -Ldir заставляет компоновщик C искать библиотеки C в каталоге dir. (См. опцию -custom).
- -color mode
- Включить или отключить цвета в сообщениях компилятора (особенно предупреждения и ошибки). Поддерживаются следующие режимы:
- auto
- использовать эвристику для включения цветов только в том случае, если вывод поддерживает их (терминал ANSI-совместимый tty);
- always
- включить цвета безусловно;
- never
- отключить вывод цвета.
Переменная окружения OCAML_COLOR учитывается, если -color не указана. Её значениями являются auto/always/never, как указано выше.
Если -color не указана, OCAML_COLOR не установлена и установлена переменная окружения NO_COLOR, то вывод цвета отключен. В противном случае, используется значение по умолчанию 'auto', и текущая эвристика проверяет, что переменная окружения TERM существует и не пуста или не равна dumb, и что выполняется 'isatty(stderr)'.
- -error-style mode
- Управлять способом вывода сообщений об ошибках и предупреждений. Поддерживаются следующие режимы:
- short
- выводить только ошибку и её местоположение;
- contextual
- как short, но также отображать фрагмент исходного кода, соответствующий местоположению ошибки.
Переменная окружения OCAML_ERROR_STYLE учитывается, если -error-style не указана. Её значениями являются short/contextual, как указано выше.
- -compat-32
- Проверить, может ли сгенерированный исполняемый байт-код работать на 32-битных платформах, и сигнализировать об ошибке, если нет. Это полезно при компиляции байт-кода на 64-битной машине.
- -config
- Вывести номер версии ocamlc и подробное резюме его конфигурации, затем завершить работу.
- -config-var var
- Вывести значение конкретной переменной конфигурации из вывода -config, затем завершить работу. Если переменная не существует, код выхода отличен от нуля. Эта опция доступна только начиная с OCaml 4.08, поэтому авторам скриптов следует предусмотреть резервный вариант для более старых версий.
- -custom
- Компоновка в режиме «пользовательского времени выполнения». В режиме компоновки по умолчанию компоновщик создает байт-код, предназначенный для выполнения с общей системой времени выполнения, ocamlrun. В режиме пользовательского времени выполнения компоновщик создает выходной файл, содержащий как систему времени выполнения, так и байт-код для программы. Результирующий файл больше, но его можно выполнять напрямую, даже если команда ocamlrun не установлена. Кроме того, режим «пользовательского времени выполнения» позволяет статическую компоновку кода OCaml с пользовательскими функциями C, как описано в главе 20.
Unix: Никогда не используйте команду strip для исполняемых файлов, созданных с помощью ocamlc -custom, это удалит часть байт-кода исполняемого файла.
Unix: Предупреждение о безопасности: никогда не устанавливайте биты «setuid» или «setgid» для исполняемых файлов, созданных с помощью ocamlc -custom, это сделает их уязвимыми для атак.
- -depend ocamldep-args
- Вычислить зависимости, как это сделала бы команда ocamldep. Остальные аргументы интерпретируются так, как если бы они были переданы команде ocamldep.
- -dllib -llibname
- Обеспечьте динамическую загрузку C-совместимой библиотеки dlllibname.so (в Windows — dlllibname.dll) системой выполнения ocamlrun при запуске программы.
- -dllpath dir
- Добавляет директорию dir в путь поиска динамических C-библиотек во время выполнения. При линковке библиотеки поиск ведётся в стандартном пути (соответствующем опции -I). Опция -dllpath просто помещает dir в создаваемый исполняемый файл, где ocamlrun может его найти и использовать, как описано в разделе 13.3.
- -for-pack module-path
- Создаёт объектный файл (.cmo), который позже можно включить как подмодуль (с заданным путём доступа) в единицу компиляции, созданную с помощью -pack. Например, ocamlc -for-pack P -c A.ml сгенерирует a..cmo, который позже можно использовать с ocamlc -pack -o P.cmo a.cmo. Примечание: вы по-прежнему можете упаковать модуль, который был скомпилирован без -for-pack, но в этом случае исключения будут выведены с неверными именами.
- -g
- Добавляет отладочную информацию во время компиляции и линковки. Эта опция необходима для отладки программы с помощью ocamldebug (см. главу 18) и для создания трассировок стека при завершении программы с необработанным исключением (см. раздел 13.2).
- -i
- Принуждает компилятор выводить все определённые имена (с их выведенными типами или определениями) при компиляции реализации (.ml файл). Не создаются скомпилированные файлы (.cmo и .cmi файлы). Это может быть полезно для проверки типов, выведенных компилятором. Также, так как вывод соответствует синтаксису интерфейсов, это может помочь в написании явного интерфейса (.mli файл) для файла: просто перенаправьте стандартный вывод компилятора в .mli файл и отредактируйте его, удалив все объявления неэкспортированных имён.
- -I directory
- Добавляет заданную директорию в список директорий, которые просматриваются при поиске скомпилированных файлов интерфейсов (.cmi), скомпилированных объектных файлов (.cmo), библиотек (.cma) и C-библиотек, указанных с помощью -cclib -lxxx. По умолчанию сначала ищется текущая директория, затем — стандартная директория библиотек. Директории, добавленные с -I, ищутся после текущей директории в порядке их указания в командной строке, но перед стандартной директорией библиотек. Смотрите также опцию -nostdlib.
Если заданная директория начинается с +, она берётся относительно стандартной директории библиотек. Например, -I +unix добавляет поддиректорию unix стандартной библиотеки в путь поиска.
- -impl filename
- Скомпилировать файл filename как файл реализации, даже если его расширение не .ml.
- -intf filename
- Скомпилировать файл filename как файл интерфейса, даже если его расширение не .mli.
- -intf-suffix string
- Распознавать имена файлов, заканчивающиеся на string, как файлы интерфейса (вместо по умолчанию .mli).
- -labels
- Метки не игнорируются в типах, метки могут использоваться в приложениях, и помеченные параметры могут быть указаны в любом порядке. Это значение по умолчанию.
- -linkall
- Принудительно подключать все модули, содержащиеся в библиотеках. Если этот флаг не задан, неиспользуемые модули не подключаются. При построении библиотеки (опция -a), установка опции -linkall заставляет все последующие ссылки программ, использующих эту библиотеку, подключать все модули, содержащиеся в библиотеке. При компиляции модуля (опция -c), установка опции -linkall гарантирует, что этот модуль всегда будет подключён, если он помещён в библиотеку, и эта библиотека подключена.
- -make-runtime
- Создать собственную систему выполнения (в файле, указанном опцией -o), включающую C-объектные файлы и библиотеки, указанные в командной строке. Эта пользовательская система выполнения может быть использована позже для выполнения байткодовых исполняемых файлов, созданных с помощью ocamlc -use-runtime runtime-name опцию. Подробнее см. в разделе 20.1.6.
- -match-context-rows
- Установите количество строк контекста, используемого для оптимизации при компиляции сопоставления с образцом. Значение по умолчанию — 32. Более низкие значения приводят к более быстрому компилению, но к менее оптимизированному коду. Эта расширенная опция предназначена для использования в случае, если программа, интенсивно использующая сопоставление с образцом, приводит к существенному увеличению времени компиляции.
- Укажите имя выходного файла, создаваемого компилятором. По умолчанию выходное имя — a.out в Unix и camlprog.exe в Windows. Если указан параметр -a, укажите имя создаваемой библиотеки. Если указан параметр -pack, укажите имя создаваемого упакованного объектного файла. Если указаны параметры -output-obj или -output-complete-obj, укажите имя создаваемого выходного файла. Если указан параметр -c, укажите имя объектного файла, созданного для следующего исходного файла в командной строке.
- -opaque
- При компиляции реализации родным компилятором по умолчанию создаётся файл .cmx с информацией для межмодульной оптимизации. Он также ожидает наличие файлов .cmx для зависимостей текущего компилируемого источника и использует их для оптимизации. Начиная с OCaml 4.03, компилятор будет выдавать предупреждение, если не сможет найти файл .cmx одной из таких зависимостей.
Параметр -opaque, доступный начиная с 4.04, отключает информацию о межмодульной оптимизации для текущего компилируемого модуля. При компиляции интерфейса .mli использование -opaque помечает скомпилированный интерфейс .cmi таким образом, что последующие компиляции модулей, зависящих от него, не будут полагаться на соответствующий файл .cmx и не будут предупреждать об его отсутствии. При компиляции реализации .ml родным компилятором использование -opaque генерирует .cmx, который не содержит никакой информации о межмодульной оптимизации.
Использование этого параметра может снизить качество генерируемого кода, но сокращает время компиляции, как при чистых, так и при инкрементных сборках. Действительно, при использовании родного компилятора, когда изменяется реализация компиляционной единицы, все единицы, зависящие от неё, могут потребовать перекомпиляции — потому что информация о межмодульной оптимизации может измениться. Если компиляционная единица, реализация которой изменилась, была скомпилирована с помощью -opaque, такая перекомпиляция не нужна. Таким образом, этот параметр можно использовать, например, для ускорения циклов редактирования-компиляции-тестирования.
- -open Module
- Открывает указанный модуль перед обработкой файлов интерфейса или реализации. Если задано несколько параметров -open, они обрабатываются в порядке следования, так как будто инструкции open! Module1;; ... open! ModuleN;; были добавлены в начало каждого файла.
- -output-obj
- Принуждает компоновщик создать объектный файл C вместо исполняемого файла байт-кода. Это полезно для упаковки OCaml-кода в библиотеку C, которую можно вызывать из любой C-программы. См. главу 20, раздел 20.7.5. Имя выходного объектного файла необходимо установить с помощью параметра -o. Этот параметр также может быть использован для создания файла исходного кода C (.c расширение) или скомпилированной общей/динамической библиотеки (.so расширение, .dll в Windows).
- -output-complete-exe
- Создаёт автономный исполняемый файл, связывая объектный файл C, содержащий программу байт-кода, систему времени выполнения OCaml и любой другой статический C-код, переданный ocamlc. Результат подобен -custom, за исключением того, что байт-код встроен в C-код, поэтому он больше недоступен для инструментов, таких как ocamldebug. С другой стороны, полученный бинарник устойчив к strip.
- -output-complete-obj
- То же, что и параметр -output-obj, за исключением того, что создаваемый объектный файл включает библиотеки времени выполнения и автоматической компоновки.
- -pack
- Создаёт файл объектного кода байт-кода (.cmo файл) и связанный с ним скомпилированный интерфейс (.cmi), объединяющий объектные файлы, указанные в командной строке, представляя их в виде подмодулей выходного файла .cmo. Имя выходного файла .cmo необходимо указать с помощью параметра -o. Например,
ocamlc -pack -o p.cmo a.cmo b.cmo c.cmo
генерирует скомпилированные файлы p.cmo и p.cmi, описывающие компиляционную единицу, имеющую три подмодуля A, B и C, соответствующие содержимому объектных файлов a.cmo, b.cmo и c.cmo. Это содержимое можно использовать как P.A, P.B и P.C в остальной части программы. - -pp command
- Заставляет компилятор вызвать данную команду в качестве препроцессора для каждого исходного файла. Вывод команды перенаправляется в промежуточный файл, который компилируется. Если ошибок компиляции нет, промежуточный файл удаляется после этого.
- -ppx command
- После разбора, абстрактное синтаксическое дерево передаётся на обработку препроцессору command. Модуль Ast_mapper, описанный в главе 27: Ast_mapper , реализует внешний интерфейс препроцессора.
- -principal
- Проверяет пути информации при проверке типов, чтобы убедиться, что все типы получены принципиальным способом. При использовании меченных аргументов и/или полиморфных методов этот флаг необходим, чтобы гарантировать, что будущие версии компилятора смогут корректно выводить типы, даже если внутренние алгоритмы изменятся. Все программы, принятые в режиме -principal, также принимаются в режиме по умолчанию с эквивалентными типами, но различными бинарными подписями, и это может замедлить проверку типов; тем не менее, рекомендуется использовать его один раз перед публикацией исходного кода.
- -rectypes
- Разрешает произвольные рекурсивные типы во время проверки типов. По умолчанию поддерживаются только рекурсивные типы, где рекурсия проходит через тип объекта. Обратите внимание, что после создания интерфейса с помощью этого флага, вы должны использовать его снова для всех зависимостей.
- -runtime-variant suffix
- Добавляет строку suffix к имени библиотеки времени выполнения, используемой программой. В настоящее время поддерживается только один такой суффикс: d, и только если компилятор OCaml был сконфигурирован с параметром -with-debug-runtime. Этот суффикс предоставляет отладочную версию времени выполнения, что полезно для отладки проблем с указателями в низкоуровневом коде, например, C-стыках.
- -stop-after pass
- Останавливает компиляцию после указанного этапа компиляции. В настоящее время поддерживаются следующие этапы: parsing, typing.
- -safe-string
- Внушает разделение между типами string и bytes, делая строки неизменяемыми. Это значение по умолчанию.
- -short-paths
- Когда тип виден под несколькими модульными путями, используйте самый короткий при печати имени типа в выведенных интерфейсах и сообщениях об ошибках и предупреждениях. Имена идентификаторов, начинающиеся с символа подчеркивания _ или содержащие двойные подчеркивания __, увеличивают длину на +10 при вычислении.
- -strict-sequence
- Принудительно задайте тип левой части каждой последовательности как unit.
- -strict-formats
- Отклоняйте недопустимые форматы, которые принимались в старых реализациях форматов. Вы должны использовать этот флаг для обнаружения и исправления таких недопустимых форматов, так как они будут отклоняться будущими версиями OCaml.
- -unboxed-types
- Если тип может быть не упакован (т.е. запись с одним аргументом или конкретный тип данных с одним конструктором одного аргумента), он будет не упакован, если не помечен как [@@ocaml.boxed].
- -no-unboxed-types
- Если тип может быть не упакован, он будет упакован, если не помечен как [@@ocaml.unboxed]. Это значение по умолчанию.
- -unsafe
- Отключить проверку границ для доступа к массивам и строкам (конструкции v.(i) и s.[i]). Программы, скомпилированные с флагом -unsafe, поэтому немного быстрее, но небезопасны: может произойти что угодно, если программа обращается к массиву или строке за пределами его границ. Кроме того, отключите проверку на ноль в операциях целочисленного деления и взятия остатка. С флагом -unsafe целочисленное деление (или взятие остатка) на ноль может остановить программу или продолжить с неопределенным результатом вместо возбуждения исключения Division_by_zero.
- -unsafe-string
- Идентифицировать типы string и bytes, тем самым делая строки изменяемыми. Это предназначено для совместимости со старым исходным кодом и не должно использоваться с новым ПО.
- -use-runtime runtime-name
- Сгенерировать исполняемый файл байткода, который может быть запущен на пользовательской системе выполнения runtime-name, созданной ранее с помощью ocamlc -make-runtime runtime-name. Дополнительная информация в разделе 20.1.6.
- -v
- Вывести номер версии компилятора и расположение каталога стандартной библиотеки, затем завершить работу.
- -verbose
- Вывести все внешние команды перед их выполнением, в частности вызовы компилятора C и компоновщика в режиме -custom. Полезно для отладки проблем с библиотеками C.
- -version or -vnum
- Вывести номер версии компилятора в краткой форме (например, 3.11.0), затем завершить работу.
- -w warning-list
- Включить, отключить или отметить как фатальные предупреждения, указанные аргументом warning-list. Каждое предупреждение может быть включено или выключено, и каждое предупреждение может быть фатальным или не фатальным. Если предупреждение отключено, оно не отображается и никак не влияет на компиляцию (даже если оно фатальное). Если предупреждение включено, оно нормально отображается компилятором всякий раз, когда исходный код его вызывает. Если оно включено и фатальное, компилятор также остановится с ошибкой после его отображения.
Аргумент warning-list — это последовательность спецификаторов предупреждений без разделителей между ними. Спецификатор предупреждения — это одно из следующего:
- +num
- Включить предупреждение номер num.
- -num
- Отключить предупреждение номер num.
- @num
- Включить и отметить как фатальное предупреждение номер num.
- +num1..num2
- Включить предупреждения в указанном диапазоне.
- -num1..num2
- Отключить предупреждения в указанном диапазоне.
- @num1..num2
- Включить и отметить как фатальные предупреждения в указанном диапазоне.
- +letter
- Включить набор предупреждений, соответствующих letter. Буква может быть заглавной или строчной.
- -letter
- Отключить набор предупреждений, соответствующих letter. Буква может быть заглавной или строчной.
- @letter
- Включить и отметить как фатальный набор предупреждений, соответствующих letter. Буква может быть заглавной или строчной.
- uppercase-letter
- Включить набор предупреждений, соответствующих uppercase-letter.
- lowercase-letter
- Отключить набор предупреждений, соответствующих lowercase-letter.
Альтернативно, warning-list может указать одно предупреждение по его мнемоническому имени (см. ниже), следующим образом:
- +name
- Включить предупреждение name.
- -name
- Отключить предупреждение name.
- @name
- Включить и отметить как фатальное предупреждение name.
Номера предупреждений, буквы и имена, которые в настоящее время не определены, игнорируются. Предупреждения следующие (имя после каждого номера указывает мнемоническое имя этого предупреждения).
- 1 comment-start
- Подозрительная метка начала комментария.
- 2 comment-not-end
- Подозрительная метка конца комментария.
- 3
- Устаревший синоним предупреждения ’устаревший’.
- 4 fragile-match
- Хрупкое сопоставление с образцом: сопоставление, которое останется полным, даже если к одному из типов вариантов, с которым производится сопоставление, будут добавлены дополнительные конструкторы.
- 5 ignored-partial-application
- Частично применённая функция: выражение, результат которого имеет тип функции и игнорируется.
- 6 labels-omitted
- Метка опущена в применении функции.
- 7 method-override
- Метод переопределён.
- 8 partial-match
- Частичное совпадение: недостающие случаи в сопоставлении с образцом.
- 9 missing-record-field-pattern
- Отсутствующие поля в шаблоне записи.
- 10 non-unit-statement
- Выражение в левой части последовательности, которое не имеет тип unit (и которое не является функцией, см. предупреждение номер 5).
- 11 redundant-case
- Избыточный случай в сопоставлении с образцом (неиспользуемый случай сопоставления).
- 12 redundant-subpat
- Избыточный подшаблон в сопоставлении с образцом.
- 13 instance-variable-override
- Переменная экземпляра переопределена.
- 14 illegal-backslash
- Неправильный обратный слэш в строковой константе.
- 15 implicit-public-methods
- Приватный метод неявно сделан публичным.
- 16 unerasable-optional-argument
- Неудаляемый необязательный аргумент.
- 17 undeclared-virtual-method
- Необъявленный виртуальный метод.
- 18 not-principal
- Неглавный тип.
- 19 non-principal-labels
- Тип без главенства.
- 20 ignored-extra-argument
- Неиспользуемый аргумент функции.
- 21 nonreturning-statement
- Невозвращаемое оператор.
- 22 preprocessor
- Предупреждение препроцессора.
- 23 useless-record-with
- Бесполезная запись with часть.
- 24 bad-module-name
- Неправильное имя модуля: имя файла исходного кода не является допустимым именем модуля OCaml.
- 25
- Игнорируется: теперь часть предупреждения 8.
- 26 unused-var
- Подозрительная неиспользуемая переменная: неиспользуемая переменная, которая связана с let или as и не начинается с символа подчёркивания (_).
- 27 unused-var-strict
- Неопасная неиспользуемая переменная: неиспользуемая переменная, которая не связана с let ни с as, и не начинается с символа подчёркивания (_).
- 28 wildcard-arg-to-constant-constr
- Шаблон подстановки `*` в качестве аргумента константному конструктору.
- 29 eol-in-string
- Неэкранированный конец строки в строковой константе (непереносимый код).
- 30 duplicate-definitions
- Два метки или конструктора с одинаковым именем определены в двух взаимно рекурсивных типах.
- 31 module-linked-twice
- Модуль подключается дважды в одном исполняемом файле.
- 32 unused-value-declaration
- Неиспользуемое объявление значения.
- 33 unused-open
- Неиспользуемый оператор open.
- 34 unused-type-declaration
- Неиспользуемое объявление типа.
- 35 unused-for-index
- Неиспользуемый индекс цикла for.
- 36 unused-ancestor
- Неиспользуемый предковый объект.
- 37 unused-constructor
- Неиспользуемый конструктор.
- 38 unused-extension
- Неиспользуемый расширяющий конструктор.
- 39 unused-rec-flag
- Неиспользуемый флаг rec.
- 40 name-out-of-scope
- Имя конструктора или метки используется вне области видимости.
- 41 ambiguous-name
- Неоднозначное имя конструктора или метки.
- 42 disambiguated-name
- Разрешённое имя конструктора или метки (предупреждение совместимости).
- 43 nonoptional-label
- Необязательная метка применена как необязательная.
- 44 open-shadow-identifier
- Оператор open затеняет уже определённый идентификатор.
- 45 open-shadow-label-constructor
- Оператор open затеняет уже определённую метку или конструктор.
- 46 bad-env-variable
- Ошибка в переменной среды.
- 47 attribute-payload
- Неправильная нагрузка атрибута.
- 48 eliminated-optional-arguments
- Неявное исключение необязательных аргументов.
- 49 no-cmi-file
- Отсутствует файл cmi при поиске псевдонима модуля.
- 50 unexpected-docstring
- Неожиданный комментарий документации.
- 51 wrong-tailcall-expectation
- Вызов функции с неправильным атрибутом @tailcall
- 52 fragile-literal-pattern (см. 11.5.3)
- Хрупкий шаблон константы.
- 53 misplaced-attribute
- Атрибут не может появиться в данном контексте.
- 54 duplicated-attribute
- Атрибут используется более одного раза в выражении.
- 55 inlining-impossible
- Встраивание невозможно.
- 56 unreachable-case
- Недостижимый случай в сопоставлении с образцом (на основе информации о типе).
- 57 ambiguous-var-in-pattern-guard (см. 11.5.4)
- Неоднозначные переменные в шаблонах «или» в условиях.
- 58 no-cmx-file
- Файл cmx отсутствует.
- 59 flambda-assignment-to-non-mutable-value
- Присвоение неизменяемому значению.
- 60 unused-module
- Неиспользуемое объявление модуля.
- 61 unboxable-type-in-prim-decl
- Тип, который можно разложить, в примитивном объявлении.
- 62 constraint-on-gadt
- Ограничение типа на объявлении типа GADT.
- 63 erroneous-printed-signature
- Некорректная напечатанная сигнатура.
- 64 unsafe-array-syntax-without-parsing
- -unsafe используется с препроцессором, возвращающим дерево синтаксического анализа.
- 65 redefining-unit
- Объявление типа, определяющее новый конструктор ’()’.
- 66 unused-open-bang
- Неиспользуемое оператор open!.
- 67 unused-functor-parameter
- Неиспользуемый параметр функтора.
- 68 match-on-mutable-state-prevent-uncurry
- Сопоставление с образцом, зависящее от изменяемого состояния, предотвращает разворачивание оставшихся аргументов.
- 69 unused-field
- Неиспользуемое поле записи.
- 70 missing-mli
- Отсутствует файл интерфейса.
- 71 unused-tmc-attribute
- Неиспользуемый атрибут @tail_mod_cons.
- 72 tmc-breaks-tailcall
- Преобразование @tail_mod_cons превращает хвостовой вызов в нехвостовой.
- A
- все предупреждения
- C
- предупреждения 1, 2.
- D
- Псевдоним предупреждения 3.
- E
- Псевдоним предупреждения 4.
- F
- Псевдоним предупреждения 5.
- K
- предупреждения 32, 33, 34, 35, 36, 37, 38, 39.
- L
- Псевдоним предупреждения 6.
- M
- Псевдоним предупреждения 7.
- P
- Псевдоним предупреждения 8.
- R
- Псевдоним предупреждения 9.
- S
- Псевдоним предупреждения 10.
- U
- предупреждения 11, 12.
- V
- Псевдоним предупреждения 13.
- X
- предупреждения 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 30.
- Y
- Псевдоним предупреждения 26.
- Z
- Псевдоним предупреждения 27.
Значение по умолчанию — -w +a-4-6-7-9-27-29-32..42-44-45-48-50-60. Оно отображается командой ocamlc -help. Обратите внимание, что предупреждения 5 и 10 не всегда срабатывают, в зависимости от внутренних механизмов проверяющего типов.
- -warn-error warning-list
- Отмечает как фатальные предупреждения, указанные в аргументе warning-list. Компилятор завершит работу с ошибкой, когда будет выведено одно из этих предупреждений. Списки предупреждений warning-list имеют такое же значение, как и для параметра -w: знак + (или заглавная буква) отмечает соответствующее предупреждение как фатальное, знак - (или строчная буква) возвращает его к статусу не фатального предупреждения, а знак @ одновременно включает и отмечает соответствующее предупреждение как фатальное.
Примечание: в производственном коде не рекомендуется использовать наборы предупреждений (т.е. буквы) в качестве аргументов для -warn-error, потому что это может нарушить сборку при добавлении новых предупреждений в будущих версиях OCaml.
Значение по умолчанию — -warn-error -a+31 (только предупреждение 31 является фатальным).
- -warn-help
- Отобразить описание всех доступных номеров предупреждений.
- -where
- Вывести расположение стандартной библиотеки, а затем завершить работу.
- -with-runtime
- Включить в сгенерированную программу систему выполнения. Это значение по умолчанию.
- -without-runtime
- Компилятор не включает систему выполнения (ни ссылку на неё) в сгенерированную программу; её необходимо предоставить отдельно.
- - file
- Обрабатывать file как имя файла, даже если оно начинается с символа тире (-).
- -help or --help
- Отобразить краткое руководство по использованию и завершить работу.
contextual-cli-control
Контекстуальное управление параметрами командной строки
Командную строку компилятора можно изменять «извне» с помощью следующих механизмов. Они являются экспериментальными и могут быть изменены. Их следует использовать только для экспериментальной и опытной работы, а не в релизах.
- OCAMLPARAM (переменная окружения)
- Набор аргументов, которые будут вставлены перед или после аргументов из командной строки. Аргументы задаются в виде списка, разделенного запятыми, пар имя=значение. Знак _ используется для указания позиции аргументов командной строки, т.е. a=x,_,b=y означает, что a=x должно быть выполнено перед разбором аргументов, а b=y — после. Наконец, альтернативный разделитель может быть задан как первый символ строки, в наборе :|; ,.
- ocaml_compiler_internal_params (файл в директории stdlib)
- Сопоставление имён файлов со списками аргументов, которые будут добавлены в командную строку (и аргументы OCAMLPARAM).
- OCAML_FLEXLINK (переменная окружения)
- Альтернативный исполняемый файл для использования в Windows для flexlink вместо заданного значения. В основном используется для начальной загрузки.
11.3 Модули и файловая система
Этот короткий раздел призван прояснить взаимосвязь между именами модулей, соответствующих единицам компиляции, и именами файлов, содержащих их скомпилированный интерфейс и скомпилированную реализацию.
Компилятор всегда выводит имя модуля, беря прописную часть имени исходного файла (.ml или .mli файл). То есть он удаляет ведущее имя директории, если таковое имеется, а также суффикс .ml или .mli; затем он меняет первую букву на заглавную, чтобы соответствовать требованию, что имена модулей должны быть заглавными. Например, компиляция файла mylib/misc.ml предоставляет реализацию для модуля под названием Misc. Другие единицы компиляции могут ссылаться на компоненты, определённые в mylib/misc.ml, под именами Misc.имя; они также могут использовать open Misc, а затем использовать имена имя без квалификатора.
Файлы .cmi и .cmo, созданные компилятором, имеют то же базовое имя, что и исходный файл. Следовательно, скомпилированные файлы всегда имеют базовое имя, равное (с учётом заглавной первой буквы) имени модуля, который они описывают (для файлов .cmi) или реализуют (для файлов .cmo).
Когда компилятор сталкивается со ссылкой на свободный идентификатор модуля Mod, он ищет в пути поиска файл с именем Mod.cmi или mod.cmi и загружает скомпилированный интерфейс, содержащийся в этом файле. Вследствие этого, переименование файлов .cmi не рекомендуется: имя файла .cmi всегда должно соответствовать имени единицы компиляции, которую он реализует. Допустимо перемещать их в другую директорию, если их базовое имя сохраняется, и компилятору задаются правильные опции -I. Компилятор выдаст ошибку, если загрузит файл .cmi, который был переименован.
Скомпилированные файлы байткода (.cmo файлы), с другой стороны, могут быть свободно переименованы после создания. Это потому, что компоновщик никогда не пытается найти сам файл .cmo, который реализует модуль с заданным именем: он вместо этого полагается на то, что пользователь предоставит список файлов .cmo вручную.
11.4 Распространённые ошибки
В этом разделе описаны и объяснены наиболее часто встречающиеся сообщения об ошибках.
- Файл filename не найден
- Указанный файл не был найден ни в текущем каталоге, ни в каталогах пути поиска. Файл filename — это либо скомпилированный интерфейсный файл (.cmi), либо скомпилированный байт-код файл (.cmo). Если filename имеет формат mod.cmi, это означает, что вы пытаетесь скомпилировать файл, который ссылается на идентификаторы из модуля mod, но вы ещё не скомпилировали интерфейс для модуля mod. Исправление: сначала скомпилируйте mod.mli или mod.ml, чтобы создать скомпилированный интерфейс mod.cmi.
Если filename имеет формат mod.cmo, это означает, что вы пытаетесь связать файл байт-кода, который ещё не существует. Исправление: сначала скомпилируйте mod.ml.
Если ваша программа расположена в нескольких каталогах, эта ошибка также может возникнуть, потому что вы не указали каталоги для поиска. Исправление: добавьте правильные опции -I в командную строку.
- Поврежденный скомпилированный интерфейс filename
- Компилятор выводит эту ошибку, когда пытается прочитать файл скомпилированного интерфейса (.cmi), имеющий неправильную структуру. Это означает, что при записи файла .cmi что-то пошло не так: диск был заполнен, компилятор был прерван во время создания файла и т. д. Эта ошибка также может возникнуть, если файл .cmi был изменён после его создания компилятором. Исправление: удалите повреждённый файл .cmi и перестройте его.
- Это выражение имеет тип t1, но используется с типом t2
- Это, пожалуй, самая распространённая ошибка типа в программах. Тип t1 — это тип, выведенный для выражения (часть программы, отображаемая в сообщении об ошибке), анализируя само выражение. Тип t2 — это ожидаемый тип в контексте выражения; он выводится путём анализа того, как используется значение этого выражения в остальной части программы. Если два типа t1 и t2 несовместимы, то выдаётся вышеуказанная ошибка.
В некоторых случаях трудно понять, почему два типа t1 и t2 несовместимы. Например, компилятор может сообщить, что «выражение типа foo нельзя использовать с типом foo», и кажется, что оба типа foo совместимы. Это не всегда верно. Два конструктора типов могут иметь одинаковое имя, но фактически представлять разные типы. Это может произойти, если конструктор типа переопределён. Пример:
type foo = A | B let f = function A -> 0 | B -> 1 type foo = C | D f CЭто приводит к сообщению об ошибке «выражение C типа foo не может быть использовано с типом foo».
- Тип этого выражения, t, содержит переменные типов, которые нельзя обобщить
- Переменные типов ('a, 'b, …) в типе t могут быть в одном из двух состояний: обобщённый (что означает, что тип t действителен для всех возможных примеров переменных) и не обобщённый (что означает, что тип t действителен только для одного примера переменных). В привязке let let name = expr, система проверки типов обычно обобщает как можно больше переменных типа в типе expr. Однако это приводит к некорректности (хорошо типизированная программа может аварийно завершить работу) в сочетании с полиморфными изменяемыми структурами данных. Для предотвращения этого обобщение выполняется только в привязках let, если связанное выражение expr принадлежит классу «синтаксических значений», которые включают константы, идентификаторы, функции, кортежи синтаксических значений и т. д. Во всех остальных случаях (например, expr — это применение функции), может быть создана полиморфная изменяемая структура, и поэтому обобщение выключено для всех переменных, встречающихся в контравариантных или невариантных ветвях типа. Например, если тип не значения — 'a list, переменная обобщаема (list — ковариантный конструктор типа), но не в 'a list -> 'a list (левая ветвь -> контравариантна) или 'a ref (ref невариантна).
Необобщённые переменные типа в типе не создают проблем внутри данной структуры или единицы компиляции (содержимое файла .ml или интерактивной сессии), но они не могут быть разрешены внутри сигнатур или скомпилированных интерфейсов (.cmi файл), поскольку они могут быть использованы несогласованно позже. Таким образом, компилятор выдает ошибку, когда структура или единица компиляции определяет значение name, тип которого содержит необобщённые переменные типа. Есть два способа исправить эту ошибку:
- Добавьте ограничение типа или файл .mli, чтобы дать мономорфный тип (без переменных типа) для name. Например, вместо того, чтобы писать
let sort_int_list = List.sort Stdlib.compare (* inferred type 'a list -> 'a list, with 'a not generalized *)напишитеlet sort_int_list = (List.sort Stdlib.compare : int list -> int list);;
- Если вам действительно нужен полиморфный тип для name, преобразуйте его определяющее выражение в функцию, добавив дополнительный параметр. Например, вместо того, чтобы писать
let map_length = List.map Array.length (* inferred type 'a array list -> int list, with 'a not generalized *)напишитеlet map_length lv = List.map Array.length lv
- Добавьте ограничение типа или файл .mli, чтобы дать мономорфный тип (без переменных типа) для name. Например, вместо того, чтобы писать
- Ссылка на неопределённый глобальный модуль mod
- Эта ошибка появляется при попытке связать неполный или неправильно упорядоченный набор файлов. Либо вы забыли указать реализацию для единицы компиляции под названием mod в командной строке (как правило, файл под названием mod.cmo или библиотека, содержащая этот файл). Исправление: добавьте недостающий файл .ml или .cmo в командную строку. Либо вы указали реализацию для модуля под названием mod, но она указана слишком поздно в командной строке: реализация mod должна предшествовать всем файлам байт-кода, которые ссылаются на mod. Исправление: измените порядок файлов .ml и .cmo в командной строке.
Конечно, вы всегда столкнётесь с этой ошибкой, если у вас есть взаимно рекурсивные функции в разных модулях. Например, функция Mod1.f вызывает функцию Mod2.g, а функция Mod2.g вызывает функцию Mod1.f. В этом случае, независимо от того, какие перестановки вы выполните в командной строке, программа будет отклонена на этапе компоновки. Исправления:
- Поместите функции f и g в один модуль.
- Параметризуйте одну функцию через другую. Например, вместо
mod1.ml: let f x = ... Mod2.g ... mod2.ml: let g y = ... Mod1.f ...
определитеmod1.ml: let f g x = ... g ... mod2.ml: let rec g y = ... Mod1.f g ...
и свяжите mod1.cmo перед mod2.cmo. - Используйте ссылку, чтобы хранить одну из двух функций, как в:
mod1.ml: let forward_g = ref((fun x -> failwith "forward_g") : <type>) let f x = ... !forward_g ... mod2.ml: let g y = ... Mod1.f ... let _ = Mod1.forward_g := g
- Внешняя функция f недоступна
- Эта ошибка появляется при попытке связать код, который вызывает внешние функции, написанные на C. Как объяснено в главе 20, такой код должен быть связан с библиотеками C, которые реализуют необходимую функцию C f. Если рассматриваемые библиотеки C не являются динамическими библиотеками (DLL), код необходимо связать в режиме «специального времени выполнения». Исправление: добавьте необходимые библиотеки C в командную строку и, возможно, опцию -custom.
11.5 Справочная информация по предупреждениям
В этом разделе подробно описаны и объяснены некоторые предупреждения:
11.5.1 Предупреждение 6: Метка опущена в применении функции
OCaml поддерживает полные применения с опущенными метками: если у функции известна арность, все аргументы не помечены, и их количество соответствует количеству необязательных параметров, то метки игнорируются, а необязательные параметры сопоставляются в порядке их определения. Необязательные аргументы принимают значения по умолчанию.
let f ~x ~y = x + y let test = f 2 3 > let test = f 2 3 > ^ > Warning 6 [labels-omitted]: labels x, y were omitted in the application of this function.
Эта поддержка labels-omitted приложения была введена при добавлении меток в OCaml, чтобы облегчить постепенное внедрение меток в базу кода. Однако она имеет недостаток, ослабляющий дисциплину меток: если вы используете метки для предотвращения ошибочного изменения порядка двух параметров одного типа, метки labels-omitted снова делают эту ошибку возможной.
Предупреждение 6 предупреждает о применении labels-omitted, чтобы не поощрять их использование. Когда были введены метки, это предупреждение не было включено по умолчанию, поэтому пользователи использовали labels-omitted приложения, часто без замечания.
Со временем стало привычным включать это предупреждение, чтобы избежать ошибок в порядке аргументов. Теперь предупреждение включено по умолчанию с OCaml 4.13. Приложения labels-omitted больше не рекомендуются, но пользователи, желающие сохранить этот переходный стиль, могут отключить предупреждение явно.
11.5.2 Предупреждение 9: отсутствующие поля в шаблоне записи
При сопоставлении шаблонов с записями может быть полезно сопоставить только несколько полей записи. Исключение полей может быть выполнено неявно или явно, завершив шаблон записи с помощью ; _. Однако неявное исключение полей не согласуется с проверками полноты сопоставления шаблонов. Включение предупреждения 9 ставит проверку полноты перед удобством неявного исключения полей и вызовет предупреждение при неявном исключении полей в шаблонах записей. В частности, это предупреждение может помочь выявить исчерпывающие шаблоны записей, которые могут потребовать обновления после добавления новых полей в тип записи.
type 'a point = {x : 'a; y : 'a}
let dx { x } = x (* implicit field elision: trigger warning 9 *)
let dy { y; _ } = y (* explicit field elision: do not trigger warning 9 *)
11.5.3 Предупреждение 52: хрупкий шаблон константы
Некоторые конструкторы, такие как конструкторы исключений Failure и Invalid_argument, принимают в качестве параметра значение string, содержащее текстовое сообщение, предназначенное для пользователя.
Эти текстовые сообщения обычно не стабильны со временем: места вызова, создающие эти конструкторы, могут уточнить сообщение в будущей версии, чтобы сделать его более явным и т.д. Поэтому опасно сопоставлять точное значение сообщения. Например, до OCaml 4.02, Array.iter2 вызывало исключение
Invalid_argument "arrays must have the same length"
С 4.03 оно вызывает более полезное сообщение
Invalid_argument "Array.iter2: arrays must have the same length"
но это означает, что любой код в форме
try ... with Invalid_argument "arrays must have the same length" -> ...
теперь сломан и может страдать от необработанных исключений.
Предупреждение 52 предназначено для предотвращения написания такого хрупкого кода в первую очередь. Оно не возникает при каждом сопоставлении с литеральной строкой, а только в том случае, когда авторы библиотек выразили свое намерение, возможно, изменить значение параметра конструктора в будущем, используя атрибут ocaml.warn_on_literal_pattern (см. раздел руководства об встроенных атрибутах в 10.12.1):
type t = | Foo of string [@ocaml.warn_on_literal_pattern] | Bar of string let no_warning = function | Bar "specific value" -> 0 | _ -> 1 let warning = function | Foo "specific value" -> 0 | _ -> 1 Warning 52 [fragile-literal-pattern]: Code should not depend on the actual values of this constructor's arguments. They are only for information and may change in future versions. (See manual section 11.5)
В частности, все встроенные исключения со строковым аргументом имеют этот атрибут: Invalid_argument, Failure, Sys_error все вызовут это предупреждение, если вы сопоставите конкретный строковый аргумент.
Кроме того, встроенные исключения со структурированным аргументом, включающим строку, также имеют установленный атрибут: Assert_failure и Match_failure вызовут предупреждение для шаблона, использующего литеральную строку для сопоставления первого элемента их кортежного аргумента.
Если ваш код вызывает это предупреждение, вы не должны изменять способ проверки конкретной строки, чтобы избежать предупреждения (например, используя сравнение строк внутри правой части вместо литерального шаблона), так как ваш код останется хрупким. Вместо этого вам следует расширить область шаблона, сопоставив все возможные значения.
let warning = function | Foo _ -> 0 | _ -> 1
Это может потребовать некоторой аккуратности: если scrutinee может вернуть несколько различных случаев одного шаблона или вызвать различные экземпляры одного исключения, вам может потребоваться изменить код, чтобы разделить эти несколько случаев.
Например,
try (int_of_string count_str, bool_of_string choice_str) with | Failure "int_of_string" -> (0, true) | Failure "bool_of_string" -> (-1, false)
должно быть переписано в более атомарные проверки. Например, используя шаблоны исключений, описанные в разделе 9.6, можно написать:
match int_of_string count_str with
| exception (Failure _) -> (0, true)
| count ->
begin match bool_of_string choice_str with
| exception (Failure _) -> (-1, false)
| choice -> (count, choice)
end
Единственный случай, когда это преобразование невозможно, это если данный вызов функции может вызывать различные исключения с тем же конструктором, но различными строковыми значениями. В этом случае вам придется проверить конкретные строковые значения. Это опасный дизайн API, и его следует избегать: лучше определить более точные конструкторы исключений, чем хранить полезную информацию в строках.
11.5.4 Предупреждение 57: неоднозначные переменные шаблона «или» под защитой
Семантика шаблонов «или» в OCaml определена с предвзятостью слева направо: значение v соответствует шаблону p | q, если оно соответствует p или q, но если оно соответствует обоим, среда, захваченная совпадением, является средой, захваченной p, а никогда не захваченной q.
Хотя это свойство обычно интуитивно понятно, есть по крайней мере один конкретный случай, где может быть ожидаема другая семантика. Рассмотрим шаблон, за которым следует условие when: | p when g -> e, например:
| ((Const x, _) | (_, Const x)) when is_neutral x -> branch
Семантика ясна: сопоставить scrutinee с шаблоном, если оно соответствует, проверить условие и если условие выполняется, принять ветвь. В частности, рассмотрим входные данные (Const a, Const b), где a не проходит тест is_neutral a, а b проходит тест is_neutral b. С семантикой слева направо приведенный выше фрагмент кода не принимается его вводом: соответствие (Const a, Const b) с шаблоном «или» выполняется в левой ветви, оно возвращает среду x -> a, а затем условие is_neutral a проверяется и терпит неудачу, ветвь не принимается.
Однако другая семантика может быть более естественной здесь: любая пара, одна сторона которой проходит тест, примет ветвь. С этой семантикой предыдущий фрагмент кода был бы эквивалентен
| (Const x, _) when is_neutral x -> branch
| (_, Const x) when is_neutral x -> branch
Это не семантика, принятая OCaml.
Предупреждение 57 посвящено этим запутанным случаям, когда указанная семантика слева направо не эквивалентна недетерминированной семантике (любая ветвь может быть принята) относительно конкретного условия. Более точно, оно предупреждает, когда условие использует «неоднозначные» переменные, которые связаны с разными частями scrutinee разными сторонами шаблона «или».
© 1995-2022 INRIA.
https://v2.ocaml.org/releases/4.14/htmlman/comp.html