Глава 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.cmi в дополнение к файлу скомпилированного объектного кода x.cmo. Файл x.cmi соответствует интерфейсу, экспортирующему всё, что определено в реализации x.ml.
- Аргументы, заканчивающиеся на .cmo, считаются скомпилированными объектами байткода. Эти файлы связываются вместе с файлами-объектами, полученными путём компиляции аргументов .ml (если таковые имеются), и стандартной библиотекой OCaml, чтобы создать автономную исполняемую программу. Порядок, в котором аргументы .cmo и .ml представлены в командной строке, имеет значение: единицы компиляции инициализируются в том порядке, в котором они указаны. Поэтому, данному файлу x.cmo должны предшествовать все файлы .cmo, которые ссылаются на единицу x.
- Аргументы, заканчивающиеся на .cma, считаются библиотеками байткода объектов. Библиотека байткода объектов упаковывает в один файл набор файлов-объектов (.cmo). Библиотеки создаются с помощью ocamlc -a (см. описание опции -a ниже).
- Аргументы, заканчивающиеся на .c, передаются компилятору C, который генерирует файл-объект .o (под Windows .obj). Этот файл-объект связывается с программой, если установлен флаг -custom (см. описание -custom ниже).
- Аргументы, заканчивающиеся на .o или .a (под Windows .obj или .lib), считаются файлами-объектами и библиотеками C. Они передаются в компоновщик C при компоновке в режиме -custom (см. описание -custom ниже).
- Аргументы, заканчивающиеся на .so (под Windows .dll) считаются динамическими библиотеками C (DLL). Во время компоновки они ищутся для внешних функций C, на которые ссылается код OCaml, и их имена записываются в сгенерированный исполняемый файл байткода. Система выполнения ocamlrun затем загружает их динамически во время запуска программы.
Выходной файл фазы компоновки — файл, содержащий скомпилированный байткод, который может быть выполнен интерпретатором байткода OCaml: командой, названной ocamlrun.
ocamlrun a.out arg1 arg2 … argn
выполняет скомпилированный код, содержащийся в a.out, передавая ему в качестве аргументов строковые значения arg1 до argn. (См. главу 15 для получения дополнительной информации.)
На большинстве систем файл, созданный на этапе компоновки, может быть запущен непосредственно, как в примере:
./a.out arg1 arg2 … argn
Созданный файл имеет установленный бит исполняемости и сам запускает интерпретатор байткода.
Компилятор может выводить информацию о своих внутренних этапах. Он может генерировать файлы .cmt для реализации единицы компиляции и .cmti для сигнатур, если ему передана опция -bin-annot (см. описание -bin-annot ниже). Каждый такой файл содержит типизированное абстрактное синтаксическое дерево (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
- использовать эвристику, чтобы включить цвета только в том случае, если вывод их поддерживает (терминал, совместимый с ANSI);
- always
- включить цвета безусловно;
- never
- отключить вывод цвета.
Переменная окружения OCAML_COLOR учитывается, если -color не указана. Её значения - auto/always/never, как указано выше.
Если -color не указана, OCAML_COLOR не установлена, а переменная окружения NO_COLOR установлена, вывод цвета отключен. В противном случае, значение по умолчанию — 'auto', и текущая эвристика проверяет, существует ли переменная окружения TERM и не пуста ли она или dumb, а также то, что 'isatty(stderr)' выполняется.
- -error-style mode
- Управлять способом вывода сообщений об ошибках и предупреждений. Поддерживаются следующие режимы:
- short
- выводить только ошибку и её расположение;
- contextual
- как short, но также отображать фрагмент исходного кода, соответствующий расположению ошибки.
Переменная окружения OCAML_ERROR_STYLE учитывается, если -error-style не указана. Её значения - short/contextual, как указано выше.
- -compat-32
- Проверить, может ли сгенерированный исполняемый байт-код работать на 32-битных платформах, и сообщить об ошибке, если нет. Это полезно при компиляции байт-кода на 64-битном компьютере.
- -config
- Вывести номер версии ocamlc и подробное описание его конфигурации, затем выйти.
- -config-var var
- Вывести значение определённой переменной конфигурации из вывода -config, затем выйти. Если переменная не существует, код выхода — ненулевой. Эта опция доступна только начиная с OCaml 4.08, поэтому авторы скриптов должны иметь резервный вариант для более старых версий.
- -custom
- Ссылка в режиме «пользовательская среда выполнения». В стандартном режиме ссылки компоновщик генерирует байткод, предназначенный для выполнения со стандартной системой выполнения, ocamlrun. В режиме пользовательской среды выполнения компоновщик генерирует выходной файл, содержащий как систему выполнения, так и байткод для программы. Результирующий файл больше, но его можно выполнить напрямую, даже если команда ocamlrun не установлена. Кроме того, режим «пользовательская среда выполнения» позволяет статически связывать код OCaml с пользовательскими функциями C, как описано в главе 22.
Unix: Никогда не используйте команду strip с исполняемыми файлами, созданными с помощью ocamlc -custom, так как это удалит часть байткода в исполняемом файле.
Unix: Предупреждение о безопасности: никогда не устанавливайте биты «setuid» или «setgid» для исполняемых файлов, созданных с помощью ocamlc -custom, так как это сделает их уязвимыми для атак.
- -depend ocamldep-args
- Вычислять зависимости, как это делала бы команда ocamldep. Остальные аргументы интерпретируются так, как будто они были переданы команде ocamldep.
- -dllib -llibname
- Обеспечить динамическую загрузку разделяемой библиотеки C dlllibname.so ( dlllibname.dll под Windows) системой выполнения ocamlrun при запуске программы.
- -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 directory
- Добавить указанную директорию в список директорий, которые просматриваются при поиске скомпилированных файлов интерфейсов (.cmi), скомпилированных файлов объектного кода .cmo, библиотек (.cma) и библиотек C, указанных с помощью -cclib -lxxx. По умолчанию сначала просматривается текущая директория, затем — стандартная директория библиотек. Директории, добавленные с помощью -I, просматриваются после текущей директории в том порядке, в котором они были заданы в командной строке, но перед стандартной директорией библиотек. См. также параметр -nostdlib.
Если заданная директория начинается с +, она воспринимается относительно стандартной директории библиотек. Например, -I +unix добавляет поддиректорию unix стандартной библиотеки в путь поиска.
- -H directory
- Ведёт себя идентично -I, за исключением (а) программы не могут напрямую ссылаться на модули, добавленные в путь поиска таким способом, и (б) эти директории просматриваются после любых директорий -I. Это позволяет предоставить компилятору скомпилированные файлы интерфейсов и объектного кода для транзитивных зависимостей текущей программы (зависимости её зависимостей), не позволяя им стать прямыми зависимостями.
- -impl filename
- Компилировать файл filename как файл реализации, даже если его расширение не .ml.
- -intf filename
- Компилировать файл filename как файл интерфейса, даже если его расширение не .mli.
- -intf-suffix string
- Распознавать имена файлов, заканчивающиеся на string, как файлы интерфейсов (вместо стандартного .mli).
- -labels
- Метки не игнорируются в типах, метки могут использоваться в приложениях, и помеченные параметры могут быть заданы в любом порядке. Это значение по умолчанию.
- -linkall
- Принудительно связывать все модули, содержащиеся в библиотеках. Если этот флаг не задан, неиспользуемые модули не связываются. При построении библиотеки (параметр -a), установка параметра -linkall заставляет последующие связи программ, включающих эту библиотеку, связывать все модули, содержащиеся в библиотеке. При компиляции модуля (параметр -c), установка параметра -linkall гарантирует, что этот модуль всегда будет связан, если он помещён в библиотеку, а эта библиотека связана.
- -make-runtime
- Построение пользовательской системы выполнения (в файле, указанном параметром -o), включающей файлы и библиотеки C-объектов, заданные в командной строке. Эту пользовательскую систему выполнения можно использовать позже для выполнения исполняемых файлов байткода, созданных с помощью параметра ocamlc -use-runtime runtime-name. Дополнительную информацию см. в разделе 22.1.6.
- -match-context-rows
- Установите количество строк контекста, используемых для оптимизации во время компиляции сопоставления с образцом. Значение по умолчанию 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 output-file
- Указать имя выходного файла. Для исполняемых файлов имя по умолчанию — a.out в Unix и camlprog.exe в Windows. Если используется параметр -a, указать имя создаваемой библиотеки. Если используется параметр -pack, указать имя создаваемого упакованного файла объекта. Если используются параметры -output-obj или -output-complete-obj, указать имя создаваемого файла объекта. Если используется параметр -c, указать имя файла объекта, создаваемого для следующего исходного файла, указанного в командной строке.
- -opaque
- При компиляции реализации родным компилятором по умолчанию создаётся файл .cmx с информацией для межмодульной оптимизации. Также ожидаются файлы .cmx для зависимостей текущего компилируемого исходного файла, и они используются для оптимизации. С OCaml 4.03 компилятор выводит предупреждение, если не может найти файл .cmx для одной из этих зависимостей.
Параметр -opaque, доступный с версии 4.04, отключает информацию о межмодульной оптимизации для текущей компилируемой единицы. При компиляции интерфейса .mli с помощью -opaque отмечается скомпилированный интерфейс .cmi так, что последующие компиляции модулей, которые зависят от него, не будут полагаться на соответствующий файл .cmx и не будут выдавать предупреждений об его отсутствии. При компиляции реализации .ml с помощью -opaque генерируется файл .cmx, который не содержит никакой информации о межмодульной оптимизации.
Использование этого параметра может ухудшить качество генерируемого кода, но сокращает время компиляции как при чистых, так и при инкрементальных сборках. Действительно, при использовании родного компилятора, когда изменяется реализация единицы компиляции, могут потребоваться перекомпиляции всех единиц, которые от неё зависят — потому что информация о межмодульной оптимизации может измениться. Если единица компиляции, чья реализация изменилась, была скомпилирована с помощью -opaque, такая перекомпиляция не требуется. Этот параметр может использоваться, например, для получения более быстрых циклов редактирования-компиляции-тестирования.
- -open Module
- Открывает указанный модуль перед обработкой файлов интерфейса или реализации. Если задано несколько параметров -open, они обрабатываются в порядке следования, так же, как если бы были добавлены операторы open! Module1;; ... open! ModuleN;; в начало каждого файла.
- -output-obj
- Заставляет компоновщик создавать файл C-объекта вместо исполняемого файла байткода. Это полезно для обертывания OCaml-кода в C-библиотеку, вызываемую любой C-программой. См. главу 22, раздел 22.7.5. Имя выходного файла объекта должно быть задано параметром -o. Этот параметр также может быть использован для создания файла C-исходного кода (.c расширение) или скомпилированной разделяемой/динамической библиотеки (.so расширение, .dll в Windows).
- -output-complete-exe
- Создать автономный исполняемый файл, скомпоновав файл C-объекта, содержащий программу байткода, систему времени выполнения OCaml и любой другой статический C-код, предоставленный команде ocamlc. Полученный эффект аналогичен -custom, за исключением того, что байткод встраивается в C-код, поэтому он больше недоступен для инструментов, таких как ocamldebug. С другой стороны, получившийся двоичный файл устойчив к действию strip.
- -output-complete-obj
- То же, что и параметр -output-obj, за исключением того, что создаваемый файл объекта включает в себя систему времени выполнения и библиотеки автоссылки.
- -pack
- Создать файл объекта байткода (.cmo файл) и соответствующий скомпилированный интерфейс (.cmi), объединяющий файлы объектов, заданные в командной строке, делая их подмодулями выходного файла .cmo. Имя выходного файла .cmo должно быть задано параметром -o. Например,
ocamlc -pack -o p.cmo a.cmo b.cmo c.cmo
генерирует скомпилированные файлы p.cmo и p.cmi, описывающие единицу компиляции, содержащую три подмодуля A, B и C, соответствующие содержимому файлов объектов a.cmo, b.cmo и c.cmo. Это содержимое можно использовать как P.A, P.B и P.C в остальной части программы. - -pp command
- Заставляет компилятор вызывать заданную команду как препроцессор для каждого исходного файла. Выход команды перенаправляется в промежуточный файл, который компилируется. При отсутствии ошибок компиляции промежуточный файл удаляется после этого.
- -ppx command
- После разбора, абстрактное синтаксическое дерево передаётся препроцессору command. Модуль Ast_mapper, описанный в главе 30: Ast_mapper , реализует внешний интерфейс препроцессора.
- -principal
- Проверять пути информации во время проверки типов, чтобы убедиться, что все типы получены принципиальным способом. При использовании помеченных аргументов и/или полиморфных методов этот флаг необходим для обеспечения того, что будущие версии компилятора смогут корректно вывести типы, даже если внутренние алгоритмы изменятся. Все программы, принятые в режиме -principal, также принимаются в режиме по умолчанию с эквивалентными типами, но различными двоичными подписями, что может замедлить проверку типов; тем не менее, использовать его один раз перед публикацией исходного кода – хорошая идея.
- -rectypes
- Разрешать произвольные рекурсивные типы во время проверки типов. По умолчанию поддерживаются только рекурсивные типы, где рекурсия проходит через тип объекта. Обратите внимание, что после создания интерфейса с использованием этого флага, вы должны использовать его снова для всех зависимостей.
- -runtime-variant приставка
- Добавить строку приставку к имени используемой программой библиотеки выполнения. В настоящее время поддерживается только одна такая приставка: d, и только если компилятор OCaml был сконфигурирован с опцией -with-debug-runtime. Эта приставка обеспечивает отладочную версию выполнения, которая полезна для отладки проблем с указателями в низкоуровневом коде, таком как C-стыки.
- -safe-string
- Обеспечить разделение между типами string и bytes, тем самым сделав строки неизменяемыми. Это значение по умолчанию и используется с OCaml 5.0.
- -safer-matching
- Не использовать информацию о типе для оптимизации сопоставления с образцом. Это позволяет обнаруживать ошибки сопоставления, даже если сопоставление по шаблону ошибочно предполагалось исчерпывающим. Это влияет только на компиляцию GADTs и полиморфных вариантов.
- -short-paths
- Когда тип виден по нескольким путям модулей, использовать самый короткий при печати имени типа в выведенных интерфейсах и сообщениях об ошибках и предупреждениях. Имена идентификаторов, начинающиеся с подчеркивания _ или содержащие двойные подчеркивания __, имеют штраф в +10 при вычислении их длины.
- -stop-after этап
- Остановить компиляцию после указанного этапа компиляции. В настоящее время поддерживаются следующие этапы: parsing, typing.
- -strict-sequence
- Принудительно устанавливать тип левой части каждой последовательности в единицу.
- -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 имя-выполнения
- Сгенерировать исполняемый файл байткода, который может быть выполнен в пользовательской системе выполнения имя-выполнения, созданной ранее с помощью ocamlc -make-runtime имя-выполнения. Для получения дополнительной информации см. раздел 22.1.6.
- -v
- Вывести номер версии компилятора и расположение каталога стандартной библиотеки, а затем выйти.
- -verbose
- Вывести все внешние команды перед их выполнением, в частности вызовы компилятора C и компоновщика в режиме -custom. Полезно для отладки проблем с библиотеками C.
- -version или -vnum
- Вывести номер версии компилятора в короткой форме (например, 3.11.0), а затем выйти.
- -w список-предупреждений
- Включить, отключить или пометить как критическое предупреждения, указанные в аргументе warning-list. Каждое предупреждение может быть включено или выключено, и каждое предупреждение может быть критическим или некритическим. Если предупреждение отключено, оно не отображается и никак не влияет на компиляцию (даже если оно критическое). Если предупреждение включено, оно нормально отображается компилятором всякий раз, когда исходный код его вызывает. Если оно включено и критическое, компилятор также остановится с ошибкой после его отображения.
Аргумент warning-list представляет собой последовательность спецификаторов предупреждений без разделителей между ними. Спецификатор предупреждения может быть одним из следующих:
- +num
- Включить предупреждение номер num.
- -num
- Отключить предупреждение номер num.
- @num
- Включить и пометить как критическое предупреждение номер num.
- +num1..num2
- Включить предупреждения в заданном диапазоне.
- -num1..num2
- Отключить предупреждения в заданном диапазоне.
- @num1..num2
- Включить и пометить как критическое предупреждения в заданном диапазоне.
- +letter
- Включить набор предупреждений, соответствующих letter. Буква может быть заглавной или строчной.
- -letter
- Отключить набор предупреждений, соответствующих letter. Буква может быть заглавной или строчной.
- @letter
- Включить и пометить как критическое набор предупреждений, соответствующих letter. Буква может быть заглавной или строчной.
- uppercase-letter
- Включить набор предупреждений, соответствующих uppercase-letter.
- lowercase-letter
- Отключить набор предупреждений, соответствующих lowercase-letter.
В качестве альтернативы, warning-list может указать одно предупреждение по его мнемоническому имени (см. ниже), следующим образом:
- +name
- Включить предупреждение name.
- -name
- Отключить предупреждение name.
- @name
- Включить и пометить как критическое предупреждение name.
Номера предупреждений, буквы и имена, которые в настоящее время не определены, игнорируются. Предупреждения следующие (имя после каждого числа указывает мнемонику для этого предупреждения).
- 1 comment-start
- Подозрительная метка начала комментария.
- 2 comment-not-end
- Подозрительная метка конца комментария.
- 3
- Устаревший синоним для предупреждения ’устаревший’.
- 4 fragile-match
- Ломкий поиск совпадений: сопоставление, которое останется полным, даже если к одному из типов вариантов, с которыми производится сопоставление, будут добавлены дополнительные конструкторы.
- 5 ignored-partial-application
- Частично применённая функция: выражение, результат которого имеет тип функции и игнорируется.
- 6 labels-omitted
- Метка опущена в применении функции.
- 7 method-override
- Метод переопределён.
- 8 partial-match
- Частичное соответствие: отсутствующие случаи в поиске совпадений по шаблонам.
- 9 missing-record-field-pattern
- Отсутствующие поля в шаблоне записи.
- 10 non-unit-statement
- Выражение в левой части последовательности, которое не имеет тип unit (и которое не является функцией, см. предупреждение номер 5).
- 11 redundant-case
- Избыточный случай в поиске совпадений по шаблонам (неиспользуемый случай сопоставления).
- 12 redundant-subpat
- Избыточный подшаблон в поиске совпадений по шаблонам.
- 13 instance-variable-override
- Переменная экземпляра переопределена.
- 14 illegal-backslash
- Некорректный обратный слэш в строковой константе.
- 15 implicit-public-methods
- Приватный метод неявно сделан публичным.
- 16 unerasable-optional-argument
- Неудаляемый необязательный аргумент.
- 17 undeclared-virtual-method
- Необъявленный виртуальный метод.
- 18 not-principal
- Неглавный тип.
- 19 non-principal-labels
- Тип без главности.
- 20 ignored-extra-argument
- Неиспользуемый аргумент функции.
- 21 nonreturning-statement
- Невозвращаемое утверждение.
- 22 preprocessor
- Предупреждение препроцессора.
- 23 useless-record-with
- Бесполезная запись with -клаузы.
- 24 bad-module-name
- Неправильное имя модуля: имя исходного файла не является допустимым именем модуля OCaml.
- 25
- Игнорируется: теперь часть предупреждения 8.
- 26 unused-var
- Подозрительная неиспользуемая переменная: неиспользуемая переменная, которая связана с let или as, и не начинается с символа подчёркивания (_).
- 27 unused-var-strict
- Безобидная неиспользуемая переменная: неиспользуемая переменная, которая не связана с let или as, и не начинается с символа подчёркивания (_).
- 28 wildcard-arg-to-constant-constr
- Шаблон со звёздочкой, заданный как аргумент константного конструктора.
- 29 eol-in-string
- Неэкранированный символ конца строки в строковой константе (непереносимый код).
- 30 duplicate-definitions
- Два тега или конструктора с одинаковым именем определены в двух взаимно рекурсивных типах.
- 31 module-linked-twice
- Один и тот же модуль связан дважды в одном исполняемом файле.
- I
- Игнорируется: теперь является жёсткой ошибкой (начиная с 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)
- Неоднозначные переменные в шаблоне «или» под условием. (с версии 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
- Генеративный функтор применяется к пустой структуре (конец структуры), а не к (). (с версии 5.1)
- A
- все предупреждения
- C
- предупреждения 1, 2.
- D
- Псевдоним предупреждения 3.
- E
- Псевдоним предупреждения 4.
- F
- Псевдоним предупреждения 5.
- K
- предупреждения 32, 33, 34, 35, 36, 37, 38, 39.
- L
- Псевдоним предупреждения 6.
- M
- Псевдоним предупреждения 7.
- P
- Псевдоним предупреждения 8.
- R
- Псевдоним предупреждения 9.
- S
- Псевдоним предупреждения 10.
- U
- предупреждения 11, 12.
- V
- Псевдоним предупреждения 13.
- X
- предупреждения 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 30.
- Y
- Псевдоним предупреждения 26.
- Z
- Псевдоним предупреждения 27.
Значение по умолчанию — -w +a-4-6-7-9-27-29-32..42-44-45-48-50-60. Оно отображается с помощью ocamlc -help. Обратите внимание, что предупреждения 5 и 10 не всегда срабатывают, в зависимости от внутреннего устройства проверяющего типа.
- -warn-error warning-list
- Сделать предупреждения, указанные в аргументе warning-list, фатальными. Компилятор завершится с ошибкой, когда одно из этих предупреждений будет выведено. warning-list имеет тот же смысл, что и для параметра -w: знак + (или заглавная буква) отмечает соответствующие предупреждения как фатальные, знак - (или строчная буква) возвращает их к предупреждениям без фатальных последствий, а знак @ одновременно включает и отмечает соответствующие предупреждения как фатальные.
Примечание: в производственном коде не рекомендуется использовать наборы предупреждений (т. е. буквы) в качестве аргументов для -warn-error, поскольку это может нарушить сборку при добавлении новых предупреждений в будущие версии OCaml.
Значение по умолчанию — -warn-error -a (никакое предупреждение не является фатальным).
- -warn-help
- Показать описание всех доступных номеров предупреждений.
- -where
- Вывести расположение стандартной библиотеки, а затем выйти.
- -with-runtime
- Включить системный запуск в сгенерированной программе. Это значение по умолчанию.
- -without-runtime
- Компилятор не включает системный запуск (ни ссылку на него) в сгенерированную программу; его необходимо предоставить отдельно.
- - file
- Обработать file как имя файла, даже если оно начинается с символа тире (-).
- -help or --help
- Отобразить краткое резюме использования и выйти.
управление-cli-контекстом
Контекстное управление параметрами командной строки
Командная строка компилятора может быть изменена «извне» с помощью следующих механизмов. Они являются экспериментальными и могут быть изменены. Их следует использовать только для экспериментальной и опытной работы, а не в выпускаемых пакетах.
- 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 ещё не скомпилирован. Решение: сначала скомпилируйте 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. Как описано в главе 22, такой код должен быть связан с библиотеками 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 для упрощения поэтапного внедрения меток в базу кода. Однако это имеет недостаток, ослабляющий дисциплину меток: если вы используете метки для предотвращения случайной перестановки двух параметров одного типа, метки labels-omitted снова делают эту ошибку возможной.
Предупреждение 6 предупреждает о использовании labels-omitted приложений, чтобы отговорить от их использования. При добавлении меток это предупреждение по умолчанию не было включено, поэтому пользователи часто использовали labels-omitted приложения, часто этого не замечая.
Со временем стало стандартной практикой включать это предупреждение для предотвращения ошибок порядка аргументов. Сейчас предупреждение включено по умолчанию начиная с OCaml 4.13. Labels-omitted приложения больше не рекомендуются, но пользователи, желающие сохранить этот переходный стиль, могут отключить предупреждение явно.
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)
следует переписать на более атомарные тесты. Например, используя шаблоны исключений, описанные в разделе 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.
Хотя это свойство обычно интуитивно понятно, есть по крайней мере один конкретный случай, где может ожидать другая семантика. Рассмотрим шаблон, за которым следует условие «когда»: | 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 посвящено этим запутанным случаям, где указанная левосторонняя семантика не эквивалентна недетерминированной семантике (любая ветвь может быть выбрана) по отношению к конкретному условию. Более точно, оно предупреждает, когда условие использует «неоднозначные» переменные, которые связаны с различными частями проверяемых величин с разных сторон шаблона «или».
© 1995-2024 INRIA.
https://ocaml.org/manual/5.2/comp.html