Глава 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, но также показывать фрагмент исходного кода, соответствующий местоположению ошибки.
Если параметр -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
- Добавьте ограничение типа или файл .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. Как объясняется в главе 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). Для полных сопоставлений генерируется немного более эффективный код.
В некоторых редких ситуациях такой анализ полноты может быть неверным: проверяющая типы система считает сопоставление полным, хотя определённые значения могут проверяться по ветвям по порядку и не соответствовать ни одной из них. Когда компилятор сопоставлений с образцом обнаруживает возможность такой ситуации, он консервативно компилирует сопоставление как частичное и выдаёт это предупреждение. В некоторых случаях такое консервативное предположение необходимо (сопоставление было частичным), а в других оно оказывается излишне консервативным (более детальный анализ показал бы, что сопоставление на самом деле полное).
Такая ситуация возникает редко: единственный известный случай требует сопоставления с изменяемыми полями. Кроме того, на производительности подавляющего большинства программ это никак не сказалось бы. По этим причинам предупреждение по умолчанию отключено: мы считаем, что при возникновении такой ситуации правильнее всего ничего не делать.
Тем не менее некоторым опытным пользователям может быть интересно выяснить, затрагивает ли эта проблема какие-либо сопоставления с образцом в их кодовой базе, по двум причинам:
- Пользователи могут захотеть точно знать, в каких частях кода может возникнуть исключение Match_failure.
- Опытные пользователи, заботящиеся о производительности, могут захотеть изучить сгенерированный код -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 в таких ситуациях: они генерировали (немного более эффективный, но) некорректный код, который при конкурентном изменении во время сопоставления мог нарушить безопасность типов и памяти.
© 1995-2026 INRIA.
https://ocaml.org/manual/5.5/comp.html