Spec-Zone.ru › OCaml
☰Инструменты OCaml
  • Пакетная компиляция (ocamlc)
  • Система верхнего уровня или REPL (ocaml)
  • Система времени выполнения (ocamlrun)
  • Компиляция в машинный код (ocamlopt)
  • Генераторы лексеров и парсеров (ocamllex, ocamlyacc)
  • Генератор зависимостей (ocamldep)
  • Генератор документации (ocamldoc)
  • Отладчик (ocamldebug)
  • Отладка машинного кода (gdb, lldb)
  • Профилирование (ocamlprof, perf)
  • Взаимодействие C с OCaml
  • Оптимизация с помощью Flambda
  • Фаззинг с помощью afl-fuzz
  • Трассировка времени выполнения с помощью событий runtime
  • Преобразование программы «Tail Modulo Constructor»
  • Обнаружение гонок данных во время выполнения с помощью ThreadSanitizer

Глава 13 Пакетная компиляция (ocamlc)

В этой главе описывается пакетный компилятор OCaml ocamlc, который компилирует исходные файлы OCaml в объектные файлы байт-кода и связывает эти объектные файлы, создавая автономные исполняемые файлы байт-кода. Затем эти исполняемые файлы запускаются интерпретатором байт-кода ocamlrun.

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.cmo создается файл скомпилированного интерфейса x.cmi. Создаваемый файл x.cmi соответствует интерфейсу, который экспортирует все определения из реализации x.ml.

  • Аргументы, заканчивающиеся на .cmo, считаются скомпилированным объектным байт-кодом. Эти файлы связываются вместе с объектными файлами, полученными при компиляции аргументов .ml (если таковые имеются), и стандартной библиотекой OCaml для создания автономной исполняемой программы. Порядок аргументов .cmo и .ml в командной строке имеет значение: единицы компиляции инициализируются во время выполнения в этом порядке, и обращение к компоненту единицы до ее инициализации приводит к ошибке при компоновке. Следовательно, данный файл x.cmo должен располагаться перед всеми файлами .cmo, ссылающимися на единицу x.
  • Аргументы, заканчивающиеся на .cma, считаются библиотеками объектного байт-кода. Библиотека объектного байт-кода объединяет в одном файле набор файлов объектного байт-кода (файлов .cmo). Библиотеки создаются с помощью ocamlc -a (см. описание параметра -a ниже). Объектные файлы, содержащиеся в библиотеке, связываются как обычные файлы .cmo (см. выше) в порядке, заданном при создании файла .cma. Единственное отличие состоит в том, что если на объектный файл из библиотеки нигде в программе нет ссылок, он не включается в компоновку.
  • Аргументы, заканчивающиеся на .c, передаются компилятору C, который создает объектный файл .o (в Windows — .obj). Этот объектный файл связывается с программой, если задан флаг -custom (см. описание параметра -custom ниже).
  • Предполагается, что аргументы, заканчивающиеся на .o или .a (в Windows — .obj или .lib), являются объектными файлами и библиотеками C. При компоновке в режиме -custom они передаются компоновщику C (см. описание параметра -custom ниже).
  • Предполагается, что аргументы, заканчивающиеся на .so (в Windows — .dll), являются разделяемыми библиотеками C (DLL). Во время компоновки в них ищутся внешние функции C, на которые ссылается код OCaml, а их имена записываются в создаваемый исполняемый файл байт-кода. Затем система времени выполнения ocamlrun динамически загружает их при запуске программы.

В результате этапа компоновки создается файл, содержащий скомпилированный байт-код, который может выполняться интерпретатором байт-кода OCaml — командой с именем ocamlrun. Если a.out — имя файла, созданного на этапе компоновки, команда

        ocamlrun a.out arg1 arg2 … argn

выполняет скомпилированный код, содержащийся в a.out, передавая ему в качестве аргументов символьные строки от arg1 до argn. (Подробнее см. главу 15.)

В большинстве систем файл, созданный на этапе компоновки, можно запускать непосредственно, например:

        ./a.out arg1 arg2 … argn

Созданный файл имеет установленный бит исполняемости и способен самостоятельно запускать интерпретатор байт-кода.

Компилятор может выводить некоторую информацию о своих внутренних этапах. Если ему передан параметр -bin-annot (см. описание параметра -bin-annot ниже), он может создавать файлы .cmt для реализации единицы компиляции и файлы .cmti для сигнатур. Каждый такой файл содержит типизированное абстрактное синтаксическое дерево (AST), создаваемое в процессе проверки типов. Это дерево содержит всю доступную информацию о расположении и конкретном типе каждого терма в исходном файле. Если проверка типов завершилась неудачей, AST является неполным.

Эти файлы .cmt и .cmti обычно полезны для инструментов анализа кода.

2 Параметры

Команда ocamlc распознает следующие параметры командной строки. Параметры -pack, -a, -c, -output-obj и -output-complete-obj являются взаимоисключающими.

-a
Создать библиотеку (файл .cma) из объектных файлов (.cmo), указанных в командной строке, вместо их связывания в исполняемый файл. Имя библиотеки должно быть задано параметром -o.

Если в командной строке переданы параметры -custom, -cclib или -ccopt, они сохраняются в результирующей библиотеке .cma. При последующем связывании с этой библиотекой параметры -custom, -cclib и -ccopt автоматически добавляются, как если бы они были указаны в командной строке, если только не задан параметр -noautolink.

-absname
Выводить в сообщениях об ошибках абсолютные пути к файлам.
-no-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. При связывании объектных файлов, созданных компилятором C++ (например, g++ или clang++), рекомендуется использовать -cc c++.
-cclib -llibname
Передать параметр -llibname компоновщику C при связывании в режиме «пользовательской среды выполнения» (см. параметр -custom). Это приводит к связыванию указанной библиотеки C с программой.
-ccopt option
Передать указанный параметр компилятору и компоновщику C. Например, при связывании в режиме «пользовательской среды выполнения» параметр -ccopt -Ldir заставляет компоновщик C искать библиотеки C в каталоге dir. (См. параметр -custom.)
-cmi-file filename
Использовать указанный файл интерфейса для проверки типов компилируемого исходного файла ML. Если этот параметр не указан, компилятор ищет файл .mli с тем же базовым именем, что и у компилируемой реализации, в том же каталоге. Если такой файл найден, компилятор ищет соответствующий файл .cmi в подключённых каталогах и сообщает об ошибке, если не находит его.
-color mode
Включить или отключить цвета в сообщениях компилятора (в частности, в предупреждениях и сообщениях об ошибках). Поддерживаются следующие режимы:
auto
использовать эвристики и включать цвета, только если вывод поддерживает их (терминал tty, совместимый с ANSI);
always
всегда включать цвета;
never
отключить цветной вывод.

Если параметр -color не указан, учитывается переменная среды OCAML_COLOR. Её значения — auto/always/never, как описано выше.

Если параметр -color не указан, переменная среды OCAML_COLOR не задана, а переменная среды NO_COLOR задана, цветной вывод отключается. В противном случае используется значение по умолчанию — ’auto’; текущая эвристика проверяет, что переменная среды TERM существует и не пуста и не равна dumb, а также что выполняется условие ’isatty(stderr)’.

-error-style mode
Управлять форматом вывода сообщений об ошибках и предупреждений. Поддерживаются следующие режимы:
short
выводить только ошибку и её местоположение;
contextual
как short, но также показывать фрагмент исходного кода, соответствующий местоположению ошибки.
По умолчанию используется режим contextual.

Если параметр -error-style не указан, учитывается переменная среды OCAML_ERROR_STYLE. Её значения — short/contextual, как описано выше.

-compat-32
Проверить, сможет ли созданный исполняемый файл байт-кода работать на 32-разрядных платформах, и сообщить об ошибке, если не сможет. Полезно при компиляции байт-кода на 64-разрядном компьютере.
-config
Вывести номер версии ocamlc и подробные сведения о его конфигурации, затем завершить работу.
-config-var var
Вывести значение указанной переменной конфигурации из вывода параметра -config, затем завершить работу. Если переменная не существует, код завершения будет ненулевым. Этот параметр доступен только начиная с OCaml 4.08, поэтому авторам скриптов следует предусмотреть альтернативный вариант для более старых версий.
-custom
Выполнить связывание в режиме «пользовательской среды выполнения». В режиме связывания по умолчанию компоновщик создаёт байт-код, предназначенный для выполнения с общей средой выполнения ocamlrun. В режиме пользовательской среды выполнения компоновщик создаёт выходной файл, содержащий как среду выполнения, так и байт-код программы. Полученный файл имеет больший размер, но его можно запускать напрямую, даже если команда ocamlrun не установлена. Кроме того, режим «пользовательской среды выполнения» позволяет статически связывать код OCaml с определёнными пользователем функциями C, как описано в главе 23.
Unix: Никогда не используйте команду strip для исполняемых файлов, созданных с помощью ocamlc -custom: это удалит из исполняемого файла часть с байт-кодом.
Unix: Предупреждение системы безопасности: никогда не устанавливайте биты «setuid» или «setgid» для исполняемых файлов, созданных с помощью ocamlc -custom: это сделает их уязвимыми для атак.
-depend ocamldep-args
Вычислить зависимости так же, как это делает команда ocamldep. Остальные аргументы интерпретируются так, как если бы они были переданы команде ocamldep.
-dllib -llibname
Настроить динамическую загрузку разделяемой библиотеки C dlllibname.so (в Windows — dlllibname.dll) средой выполнения ocamlrun при запуске программы.
-dllib-suffixed -llibname
Аналогично -dllib, но записываемое имя изменяется с учётом идентификатора среды выполнения и хоста интерпретатора перед загрузкой библиотекой среды выполнения ocamlrun при запуске программы. Этот параметр используется для библиотек-заглушек C, например для DLL, создаваемых командой ocamlmklib -suffixed.
-dllpath dir
Добавить каталог dir в путь поиска разделяемых библиотек C во время выполнения. Во время связывания разделяемые библиотеки ищутся в стандартном пути поиска (соответствующем параметру -I). Параметр -dllpath просто сохраняет dir в созданном исполняемом файле; среда выполнения ocamlrun может найти этот каталог и использовать его, как описано в разделе 15.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 (см. главу 20) и для создания трассировок стека, если программа завершается из-за необработанного исключения (см. раздел 15.2).
-no-g
Не записывать отладочную информацию (по умолчанию).
-i
Заставить компилятор выводить все определённые имена (с выведенными типами или определениями) при компиляции реализации (файла .ml). Скомпилированные файлы (.cmo и .cmi) не создаются. Это может быть полезно для проверки типов, выведенных компилятором. Кроме того, поскольку вывод соответствует синтаксису интерфейсов, он может помочь при написании явного интерфейса (файла .mli) для файла: перенаправьте стандартный вывод компилятора в файл .mli и отредактируйте его, удалив все объявления неэкспортируемых имён.
-i-variance
Заставить компилятор выводить вычисленную вариативность параметров типов для каждого объявления типа. Без этого параметра вариативность выводится только для закрытых типов или типов без манифеста (например, абстрактных типов).
-I directory
Добавить указанный каталог в список каталогов, в которых выполняется поиск скомпилированных файлов интерфейса (.cmi), скомпилированных объектных файлов (.cmo), библиотек (.cma) и библиотек C, указанных с помощью -cclib -lxxx. По умолчанию сначала проверяется текущий каталог, а затем каталог стандартной библиотеки. Каталоги, добавленные с помощью -I, проверяются после текущего каталога в порядке их указания в командной строке, но перед каталогом стандартной библиотеки. См. также параметр -nostdlib.

Если указанный каталог начинается с +, он считается относительно каталога стандартной библиотеки. Например, параметр -I +unix добавляет подкаталог unix стандартной библиотеки в путь поиска.

-H directory
Работает так же, как -I, за исключением того, что (a) программы не могут напрямую ссылаться на модули, добавленные таким образом в путь поиска, и (b) эти каталоги проверяются после всех каталогов -I. Это позволяет предоставить компилятору скомпилированные файлы интерфейсов и объектного кода для транзитивных зависимостей текущей программы (зависимостей её зависимостей), не позволяя им незаметно становиться прямыми зависимостями.
-impl filename
Скомпилировать файл filename как файл реализации, даже если его расширение не равно .ml.
-intf filename
Скомпилировать файл filename как файл интерфейса, даже если его расширение не равно .mli.
-intf-suffix string
Распознавать имена файлов, заканчивающиеся на string, как имена файлов интерфейсов (вместо используемого по умолчанию расширения .mli).
-keywords version+list
Задать ключевые слова согласно спецификации version+list.

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

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

-labels
Метки учитываются в типах, их можно использовать при вызовах, а именованные параметры можно передавать в любом порядке. Это поведение используется по умолчанию.
-launch-method method
Задаёт механизм, с помощью которого обычные исполняемые файлы с байт-кодом находят интерпретатор. Поддерживаются следующие способы:
exe
Небольшая исполняемая программа-запускатель из файла runtime-launch-info добавляется в начало образа байт-кода.
sh
Используется заголовок #!. Если полный путь к требуемому интерпретатору нельзя использовать в строке #!, вместо этого создаётся небольшой shell-скрипт, использующий интерпретатор sh, найденный в PATH.
/path/interpreter
Аналогично sh, но если интерпретатор нельзя использовать в строке #!, вместо поиска sh в PATH используется /path/intepreter.
-linkall
Принудительно включить в связывание все модули, содержащиеся в библиотеках. Если этот параметр не задан, неиспользуемые модули не включаются. При создании библиотеки (параметр -a) параметр -linkall заставляет все последующие связывания программ с этой библиотекой включать все содержащиеся в ней модули. При компиляции модуля (параметр -c) параметр -linkall гарантирует, что этот модуль всегда будет связан, если он помещён в библиотеку и эта библиотека связывается.
-make-runtime
Создать пользовательскую среду выполнения (в файле, указанном параметром -o), включив в неё объектные файлы C и библиотеки, указанные в командной строке. Эту пользовательскую среду выполнения впоследствии можно использовать для запуска исполняемых файлов с байт-кодом, созданных с параметром ocamlc -use-runtime runtime-name. Дополнительные сведения см. в разделе 23.1.6.
-match-context-rows number
Задать количество строк контекста, используемых для оптимизации при компиляции сопоставления с образцом. Значение по умолчанию — 32. Меньшие значения ускоряют компиляцию, но приводят к менее оптимизированному коду. Этот расширенный параметр предназначен для случаев, когда программа с большим количеством сопоставлений с образцом существенно увеличивает время компиляции.
-no-alias-deps
Не записывать зависимости для псевдонимов модулей. Дополнительные сведения см. в разделе 12.8.
-no-app-funct
Отключает аппликативное поведение функторов. При использовании этого параметра каждое применение функтора создаёт новые типы в результате, а повторное применение одного и того же функтора к одному и тому же аргументу даёт две несовместимые структуры.
-noassert
Не компилировать проверки утверждений. Обратите внимание, что специальная форма assert false всегда компилируется, поскольку обрабатывается особым образом при проверке типов. Этот параметр не влияет на связывание уже скомпилированных файлов.
-noautolink
При связывании библиотек .cma игнорировать параметры -custom, -cclib и -ccopt, которые могут содержаться в библиотеках (если эти параметры были заданы при создании библиотек). Это может быть полезно, если библиотека содержит некорректные указания библиотек C или параметров C; в таком случае при связывании укажите -noautolink и передайте правильные библиотеки C и параметры в командной строке.
-nolabels
Игнорировать необязательные метки в типах. Метки нельзя использовать при вызовах, а порядок параметров становится строгим.
-nostdlib
Не включать каталог стандартной библиотеки в список каталогов, где выполняется поиск скомпилированных файлов интерфейса (.cmi), скомпилированных объектных файлов (.cmo), библиотек (.cma) и библиотек C, указанных с помощью -cclib -lxxx. См. также параметр -I.
-o filename
Задать имя итогового выходного файла: исполняемого файла, скомпилированного объекта (параметр -c), библиотеки (параметр -a), упакованного объекта (параметр -pack) либо исходного или объектного файла C (параметры -output-obj и -output-complete-obj).a.out, а в Windows — camlprog.exe.

Этот параметр также можно использовать при компиляции файла интерфейса или реализации без связывания (параметр -c); в этом случае он задаёт имя файла cmi или .cmo, а также имя единицы компиляции: им становится часть filename до первой точки.

-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. См. главу 23, раздел 23.7.6. Имя выходного объектного файла необходимо указать с помощью параметра -o. Этот параметр также можно использовать для создания исходного файла C (расширение .c) или скомпилированной разделяемой/динамической библиотеки (расширение .so, в Windows — .dll).
-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
Заставляет компилятор вызывать указанную command в качестве препроцессора для каждого исходного файла. Вывод command перенаправляется во временный файл, который компилируется. Если ошибок компиляции нет, временный файл впоследствии удаляется.
-ppx command
После разбора передаёт абстрактное синтаксическое дерево препроцессору command. Модуль Ast_mapper, описанный в главе 31: Ast_mapper , реализует внешний интерфейс препроцессора.
-principal
Проверяет путь получения информации во время проверки типов, чтобы убедиться, что все типы выводятся способом, обеспечивающим главность. При использовании именованных аргументов и/или полиморфных методов этот флаг необходим, чтобы гарантировать корректный вывод типов будущими версиями компилятора, даже если внутренние алгоритмы изменятся. Все программы, принимаемые в режиме -principal, также принимаются в режиме по умолчанию с эквивалентными типами, но другими двоичными сигнатурами; при этом проверка типов может замедлиться. Тем не менее перед публикацией исходного кода рекомендуется один раз использовать этот флаг.
-rectypes
Разрешает произвольные рекурсивные типы при проверке типов. По умолчанию поддерживаются только рекурсивные типы, в которых рекурсия проходит через тип объекта. Обратите внимание: создав интерфейс с этим флагом, вы должны использовать его и для всех зависимостей.
-runtime-search method
Определяет, может ли заголовок обычного исполняемого файла с байткодом искать интерпретатор или требует, чтобы тот находился в фиксированном месте. Поддерживаются следующие методы:
disable
Используется фиксированный абсолютный путь к интерпретатору; исполняемый файл не запустится, если интерпретатор не найден по этому пути.
fallback
Используется фиксированный абсолютный путь к интерпретатору, но если исполняемый файл не сможет найти интерпретатор по этому пути, он сначала выполнит поиск в каталоге, содержащем исполняемый файл, а затем в PATH.
enable
Исполняемый файл сначала выполняет поиск интерпретатора в каталоге, содержащем исполняемый файл, а затем в PATH. Абсолютный путь к интерпретатору не сохраняется.
-runtime-variant suffix
Добавляет строку suffix к имени библиотеки среды выполнения, используемой программой. В настоящее время поддерживается только один такой суффикс: d, и только если компилятор OCaml был настроен с параметром -with-debug-runtime. Этот суффикс указывает на отладочную версию среды выполнения, полезную для поиска проблем с указателями в низкоуровневом коде, например в заглушках C.
-safe-string
Обеспечивает разделение типов string и bytes, делая строки доступными только для чтения. Это поведение используется по умолчанию и является обязательным начиная с OCaml 5.0.
-safer-matching
Не использует информацию о типах для оптимизации сопоставления с образцом. Это позволяет обнаруживать ошибки сопоставления, даже если предполагалось, что сопоставление с образцом является исчерпывающим. Это влияет только на компиляцию GADT и полиморфных вариантов.
-set-runtime-default name=value
При компоновке исполняемого файла переопределяет значение настройки среды выполнения по умолчанию. В настоящее время поддерживаются следующие настройки:
standard_library_default
Задаёт расположение по умолчанию, используемое исполняемым файлом для поиска стандартной библиотеки. По умолчанию это абсолютный путь к стандартной библиотеке, с которой был настроен сам компилятор.
-short-paths
Если тип доступен по нескольким путям модулей, использует самый короткий путь при выводе имени типа в выведенных интерфейсах, сообщениях об ошибках и предупреждениях. При вычислении длины имена идентификаторов, начинающиеся с подчёркивания _ или содержащие двойное подчёркивание __, получают штраф +10.
-stop-after pass
Останавливает компиляцию после указанного этапа компиляции. В настоящее время поддерживаются следующие этапы: parsing, typing.
-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, делая строки доступными для записи. Этот параметр предназначен для совместимости со старым исходным кодом и не должен использоваться в новом программном обеспечении. Начиная с OCaml 5.0 этот параметр всегда вызывает ошибку.
-use-runtime runtime-name
Создаёт исполняемый файл с байткодом, который можно запускать в пользовательской среде выполнения runtime-name, предварительно созданной с помощью ocamlc -make-runtime runtime-name. Дополнительную информацию см. в разделе 23.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
Устаревший синоним предупреждения «deprecated».
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
Недопустимая escape-последовательность с обратной косой чертой в строковой константе.
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
Шаблон wildcard указан как аргумент конструктора-константы.
29 eol-in-string
Непреобразованный конец строки в строковой константе (непереносимый код).
30 duplicate-definitions
Метки или конструкторы с одинаковыми именами определены в двух взаимно рекурсивных типах.
31 module-linked-twice
Модуль дважды скомпонован в одном исполняемом файле. Игнорируется: теперь это безусловная ошибка (начиная с версии 5.1).
32 unused-value-declaration
Неиспользуемое объявление значения. (начиная с версии 4.00)
33 unused-open
Неиспользуемая инструкция open. (начиная с версии 4.00)
34 unused-type-declaration
Неиспользуемое объявление типа. (начиная с версии 4.00)
35 unused-for-index
Неиспользуемый индекс цикла for. (начиная с версии 4.00)
36 unused-ancestor
Неиспользуемая переменная предка. (начиная с версии 4.00)
37 unused-constructor
Неиспользуемый конструктор. (начиная с версии 4.00)
38 unused-extension
Неиспользуемый конструктор расширения. (начиная с версии 4.00)
39 unused-rec-flag
Неиспользуемый флаг rec. (начиная с версии 4.00)
40 name-out-of-scope
Имя конструктора или метки использовано вне области видимости. (начиная с версии 4.01)
41 ambiguous-name
Неоднозначное имя конструктора или метки. (начиная с версии 4.01)
42 disambiguated-name
Имя конструктора или метки разрешено как неоднозначное (предупреждение совместимости). (начиная с версии 4.01)
43 nonoptional-label
Необязательная метка применена как обязательная. (начиная с версии 4.01)
44 open-shadow-identifier
Инструкция open затеняет уже определённый идентификатор. (начиная с версии 4.01)
45 open-shadow-label-constructor
Инструкция open затеняет уже определённую метку или конструктор. (начиная с версии 4.01)
46 bad-env-variable
Ошибка в переменной окружения. (начиная с версии 4.01)
47 attribute-payload
Недопустимая полезная нагрузка атрибута. (начиная с версии 4.02)
48 eliminated-optional-arguments
Неявное устранение необязательных аргументов. (начиная с версии 4.02)
49 no-cmi-file
Отсутствует файл cmi при поиске псевдонима модуля. (начиная с версии 4.02)
50 unexpected-docstring
Неожиданный комментарий документации. (начиная с версии 4.03)
51 wrong-tailcall-expectation
Вызов функции снабжён некорректным атрибутом @tailcall. (начиная с версии 4.03)
52 fragile-literal-pattern (см. 13.5.3)
Ненадёжный образец-константа. (начиная с версии 4.03)
53 misplaced-attribute
Атрибут не может находиться в этом контексте. (начиная с версии 4.03)
54 duplicated-attribute
Атрибут используется в выражении более одного раза. (начиная с версии 4.03)
55 inlining-impossible
Встраивание невозможно. (начиная с версии 4.03)
56 unreachable-case
Недостижимый случай в сопоставлении с образцом (на основании информации о типах). (начиная с версии 4.03)
57 ambiguous-var-in-pattern-guard (см. 13.5.4)
Неоднозначные переменные or-образца в условии. (начиная с версии 4.03)
58 no-cmx-file
Отсутствует файл cmx. (начиная с версии 4.03)
59 flambda-assignment-to-non-mutable-value
Присваивание неизменяемому значению. (начиная с версии 4.03)
60 unused-module
Неиспользуемое объявление модуля. (начиная с версии 4.04)
61 unboxable-type-in-prim-decl
Тип, который нельзя распаковать, в объявлении примитива. (начиная с версии 4.04)
62 constraint-on-gadt
Ограничение типа в объявлении типа GADT. (начиная с версии 4.06)
63 erroneous-printed-signature
Ошибочно напечатанная сигнатура. (начиная с версии 4.08)
64 unsafe-array-syntax-without-parsing
Параметр -unsafe используется с препроцессором, возвращающим синтаксическое дерево. (начиная с версии 4.08)
65 redefining-unit
Объявление типа задаёт новый конструктор ’()’. (начиная с версии 4.08)
66 unused-open-bang
Неиспользуемая инструкция open!. (начиная с версии 4.08)
67 unused-functor-parameter
Неиспользуемый параметр функтора. (начиная с версии 4.10)
68 match-on-mutable-state-prevent-uncurry
Сопоставление с образцом, зависящее от изменяемого состояния, препятствует разворачиванию оставшихся аргументов. (начиная с версии 4.12)
69 unused-field
Неиспользуемое поле записи. (начиная с версии 4.13)
70 missing-mli
Отсутствует файл интерфейса. (начиная с версии 4.13)
71 unused-tmc-attribute
Неиспользуемый атрибут @tail_mod_cons. (начиная с версии 4.14)
72 tmc-breaks-tailcall
Хвостовой вызов становится обычным вызовом в результате преобразования @tail_mod_cons. (начиная с версии 4.14)
73 generative-application-expects-unit
Генеративный функтор применён к пустой структуре (struct end), а не к (). (начиная с версии 5.1)
74 degraded-to-partial-match
Сопоставление с образцом компилируется как неполное, хотя кажется полным. (начиная с версии 5.3)
75 unnecessarily-partial-tuple-pattern
Образец кортежа заканчивается на .., хотя полностью соответствует ожидаемому типу. (начиная с версии 5.4)
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-7-9-27-29-30-32..42-44-45-48-50-60-66..70-74. Оно отображается командой ocamlc -help. Обратите внимание: предупреждения 5 и 10 возникают не всегда — это зависит от внутренних механизмов проверки типов.

-warn-error warning-list
Пометить как фатальные предупреждения, указанные аргументом warning-list. При появлении одного из этих предупреждений компилятор завершит работу с ошибкой. Аргумент warning-list имеет то же значение, что и для параметра -w: знак + (или прописная буква) помечает соответствующие предупреждения как фатальные, знак - (или строчная буква) снова делает их нефатальными, а знак @ одновременно включает соответствующие предупреждения и помечает их как фатальные.

Примечание: не рекомендуется использовать наборы предупреждений (то есть буквы) в качестве аргументов -warn-error в производственном коде, поскольку это может нарушить сборку, если в будущих версиях OCaml появятся новые предупреждения.

По умолчанию задано значение -warn-error -a (ни одно предупреждение не является фатальным).

-warn-help
Показать описание всех доступных номеров предупреждений.
-where
Вывести расположение стандартной библиотеки и завершить работу.
-with-runtime
Параметры -with-runtime и -without-runtime обеспечивают точный контроль над способом компоновки среды выполнения. По умолчанию используется -with-runtime. Вкратце, он указывает включить систему времени выполнения или ссылку на её путь по умолчанию в создаваемую программу, исполняемый файл или объектный файл. Подробное поведение зависит от используемого компилятора и параметров:

Для ocamlc в стандартном режиме компоновки (без использования -custom или -output-*) параметр -with-runtime создаёт файл, который можно выполнить, а -without-runtime создаёт чистый образ байт-кода, который необходимо явно передать среде выполнения (то есть ./foo вместо ocamlrun ./foo).

Во всех остальных случаях использования ocamlc и ocamlopt параметры -with-runtime и -without-runtime определяют, передаёт ли компилятор флаги для компоновки с установленной средой выполнения (-with-runtime) или пользователь должен передать их сам (-without-runtime).

Подробнее о параметрах -custom и -output-* см. в их документации и в разделе 23.7.6 руководства.

-without-runtime
Компилятор не включает систему времени выполнения (и ссылку на неё) в создаваемую программу, исполняемый файл или объектный файл; её необходимо предоставить отдельно. Подробности см. в описании параметра -with-runtime.
- file
Обработать file как имя файла, даже если оно начинается с символа дефиса (-).
-help или --help
Показать краткую сводку по использованию и завершить работу.
контекстное-управление-командной-строкой

Контекстное управление параметрами командной строки

Командную строку компилятора можно изменять «извне» с помощью следующих механизмов. Они являются экспериментальными и могут измениться. Их следует использовать только для экспериментов и разработки, но не в выпущенных пакетах.

OCAMLPARAM (переменная окружения)
Набор аргументов, которые будут вставлены перед аргументами командной строки или после них. Аргументы задаются списком пар name=value, разделённых запятыми. Для указания положения аргументов командной строки используется _; например, a=x,_,b=y означает, что a=x будет обработан до разбора аргументов, а b=y — после. Кроме того, в качестве первого символа строки можно задать альтернативный разделитель из набора :|; ,.
ocaml_compiler_internal_params (файл в каталоге stdlib)
Таблица соответствия имён файлов спискам аргументов, которые будут добавлены к аргументам командной строки и OCAMLPARAM.

3 Модули и файловая система

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

Компилятор всегда выводит имя модуля, беря имя файла без расширения и делая его первую букву прописной (файл .ml или .mli). То есть он удаляет начальное имя каталога, если оно есть, а также суффикс .ml или .mli; затем делает первую букву прописной, чтобы соблюсти требование о том, что имена модулей должны начинаться с заглавной буквы. Например, компиляция файла mylib/misc.ml предоставляет реализацию модуля с именем Misc. Другие единицы компиляции могут обращаться к компонентам, определённым в mylib/misc.ml, используя имена Misc.name; также они могут выполнить open Misc, а затем использовать неквалифицированные имена name.

Файлы .cmi и .cmo, создаваемые компилятором, имеют то же имя без расширения, что и исходный файл. Следовательно, имя скомпилированных файлов без расширения всегда совпадает (за исключением регистра первой буквы) с именем модуля, который они описывают (для файлов .cmi) или реализуют (для файлов .cmo).

Когда компилятор встречает ссылку на свободный идентификатор модуля Mod, он ищет в пути поиска файл с именем Mod.cmi или mod.cmi и загружает скомпилированный интерфейс, содержащийся в этом файле. Поэтому переименовывать файлы .cmi не рекомендуется: имя файла .cmi всегда должно соответствовать имени реализуемой им единицы компиляции. Их можно переместить в другой каталог, если сохранить имя файла без расширения и указать компилятору правильные параметры -I. Компилятор сообщит об ошибке, если загрузит переименованный файл .cmi.

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

4 Распространённые ошибки

В этом разделе описаны и объяснены наиболее часто встречающиеся сообщения об ошибках.

Не удаётся найти файл filename
Указанный файл не найден ни в текущем каталоге, ни в каталогах пути поиска. filename — это либо файл скомпилированного интерфейса (файл .cmi), либо скомпилированный файл байт-кода (файл .cmo). Если filename имеет формат mod.cmi, это означает, что вы пытаетесь скомпилировать файл, в котором используются идентификаторы из модуля 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
    
Ссылка на неопределённый глобальный модуль 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. Как объясняется в главе 23, такой код нужно компоновать с библиотеками C, в которых реализована требуемая функция C f. Если соответствующие библиотеки C не являются разделяемыми библиотеками (DLL), код нужно компоновать в режиме «пользовательской среды выполнения». Решение: добавьте необходимые библиотеки C в командную строку, а также, возможно, параметр -custom.

5 Справочник по предупреждениям

В этом разделе подробно описаны и объяснены некоторые предупреждения:

5.1 Предупреждение 6: Метка пропущена при применении функции

OCaml поддерживает полное применение без меток labels-omitted: если арность функции известна, все аргументы не помечены, а их количество совпадает с количеством необязательных параметров, то метки игнорируются, а необязательные параметры сопоставляются в порядке их определения. Необязательные аргументы получают значения по умолчанию.

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, чтобы упростить постепенное внедрение меток в кодовую базу. Однако у неё есть недостаток: она ослабляет дисциплину использования меток. Если вы используете метки, чтобы не допустить случайной перестановки вызывающим кодом двух параметров одного типа, применение без меток снова делает такую ошибку возможной.

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

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

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 *)

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 (см. раздел руководства о встроенных атрибутах в 12.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 13.5.3)

В частности, этот атрибут установлен для всех встроенных исключений со строковым аргументом: Invalid_argument, Failure и Sys_error вызывают это предупреждение, если сопоставлять их с конкретным строковым аргументом.

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

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

let warning = function
  | Foo _ -> 0
  | _ -> 1

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

Например,

try (int_of_string count_str, bool_of_string choice_str) with
  | Failure "int_of_string" -> (0, true)
  | Failure "bool_of_string" -> (-1, false)

следует переписать в виде более простых проверок. Например, используя шаблоны exception, описанные в разделе 11.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, и его следует избегать: лучше определить более точные конструкторы исключений, чем хранить полезную информацию в строках.

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

Семантика ясна: сопоставить проверяемое выражение с шаблоном; если оно ему соответствует, проверить защитное условие; если условие выполнено, выбрать эту ветвь. В частности, рассмотрим входное значение (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 предназначено для таких запутанных случаев, когда заданная семантика слева направо не эквивалентна недетерминированной семантике (может быть выбрана любая ветвь) относительно конкретного защитного условия. Точнее, предупреждение возникает, когда защитное условие использует «неоднозначные» переменные, которые связываются с разными частями проверяемых выражений в разных ветвях шаблона-альтернативы.

5.5 Предупреждение 74: сопоставление с образцом упрощено до частичного.

Проверяющая типы система OCaml анализирует полноту, чтобы различать сопоставления, являющиеся «полными» (все возможные входные значения обрабатываются одной из ветвей), и «частичными» (некоторые значения могут не соответствовать ни одной ветви и вызвать исключение Match_failure). Для полных сопоставлений генерируется немного более эффективный код.

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

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

Тем не менее некоторым опытным пользователям может быть интересно выяснить, затрагивает ли эта проблема какие-либо сопоставления с образцом в их кодовой базе, по двум причинам:

  1. Пользователи могут захотеть точно знать, в каких частях кода может возникнуть исключение Match_failure.
  2. Опытные пользователи, заботящиеся о производительности, могут захотеть изучить сгенерированный код -dlambda, если затронутое сопоставление находится в критически важной для производительности части программы.

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

Повторные сопоставления в изменяемых позициях

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

Рассмотрим, например:

let f : bool * bool ref -> unit = function
| (true, {contents = true}) -> ()
| (_, r) when (r.contents <- true; false) -> assert false
| (false, _) -> ()
| (_, {contents = false}) -> ()

let () = f (true, ref false)
(* Exception: Match_failure(...) *)

Проверяющая типы система OCaml выполняет анализ полноты, не учитывая возможность конкурентного изменения проверяемого выражения, и считает функцию f полной. Из-за побочных эффектов на изменяемых полях функция f является частичной.

Обратите внимание, что следующая версия также может быть затронута:

let g : bool * bool ref -> unit = function
| (true, {contents = true}) -> ()
| (false, _) -> ()
| (_, {contents = false}) -> ()

В этом примере защитное условие when не изменяет проверяемое выражение, но гонка данных, вызванная другим доменом, может изменить второе поле входного значения в самый неподходящий момент, что приведёт к тому же поведению с Match_failure. Иными словами, при наличии побочных эффектов на изменяемых полях функция g может быть частичной.

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

let h : bool ref -> unit = function
| ({contents = true}) -> ()
| r when (r.contents <- true; false) -> assert false
| ({contents = false}) -> ()

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

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

Примечание: это предупреждение появилось в OCaml 5.3; более ранние версии компилятора OCaml не выдавали его и не гарантировали генерацию случая Match_failure в таких ситуациях: они генерировали (немного более эффективный, но) некорректный код, который при конкурентном изменении во время сопоставления мог нарушить безопасность типов и памяти.

« Расширения языкаСистема верхнего уровня, или REPL (ocaml) »
Авторское право © 2026 Institut National de Recherche en Informatique et en Automatique

© 1995-2026 INRIA.
https://ocaml.org/manual/5.5/comp.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API