Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ocaml for openSUSE:Factory checked in at 2026-09-28 10:36:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ocaml (Old) and /work/SRC/openSUSE:Factory/.ocaml.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ocaml" Mon Sep 28 10:36:43 2026 rev:97 rq:1380249 version:5.5.1 Changes: -------- --- /work/SRC/openSUSE:Factory/ocaml/ocaml.changes 2026-08-12 16:11:07.326966982 +0200 +++ /work/SRC/openSUSE:Factory/.ocaml.new.383539/ocaml.changes 2026-09-28 10:37:23.243172534 +0200 @@ -1,0 +2,6 @@ +Thu Sep 17 17:17:17 UTC 2026 - [email protected] + +- update to version 5.5.1 + See included Changes for benefits and breaking changes + +------------------------------------------------------------------- Old: ---- ocaml-5.5.0.tar.xz New: ---- ocaml-5.5.1.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ocaml.spec ++++++ --- /var/tmp/diff_new_pack.ak2JhH/_old 2026-09-28 10:37:24.166211133 +0200 +++ /var/tmp/diff_new_pack.ak2JhH/_new 2026-09-28 10:37:24.167211174 +0200 @@ -45,7 +45,7 @@ %bcond_without suse_ocaml_use_rpm_license_macro Name: ocaml -Version: 5.5.0 +Version: 5.5.1 Release: 0 Summary: OCaml Compiler and Programming Environment %if %{with suse_ocaml_use_rpm_license_macro} ++++++ _service ++++++ --- /var/tmp/diff_new_pack.ak2JhH/_old 2026-09-28 10:37:24.200212554 +0200 +++ /var/tmp/diff_new_pack.ak2JhH/_new 2026-09-28 10:37:24.204212722 +0200 @@ -166,7 +166,7 @@ <param name="exclude">utils/HACKING.adoc</param> <param name="exclude">utils/dune</param> <param name="filename">ocaml</param> - <param name="revision">f5238509da6029a44ba0eb648f5dff7d9c89f519</param> + <param name="revision">46e1c324fb9e9f21f52c9cc3399020f7ef8b9b91</param> <param name="scm">git</param> <param name="submodules">disable</param> <param name="url">https://github.com/ocaml/ocaml.git</param> ++++++ ocaml-5.5.0.tar.xz -> ocaml-5.5.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/Changes new/ocaml-5.5.1/Changes --- old/ocaml-5.5.0/Changes 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/Changes 2026-09-04 13:16:34.000000000 +0200 @@ -1,3 +1,78 @@ +OCaml 5.5.1 (4 September 2026) +------------------------------ + +### Bug fixes: + +- #14891, #14982: fix scope error leading to an erroneous typechecking for + non-dependent application of module-dependent function in presence of + dependent first-class module types: + ```ocaml + module type T = sig module type S end + let f (module M:T) (m: (module M.S)) = m + module type P = sig type 'a t end + let error = + f (module struct module type S = P end) (module List) + ```` + (Florian Angeletti, report by Hazem ElMasry, review by Gabriel Scherer) + +- #14872: harden loading of bytecode executable files against corrupted + or malicious files having 2^29 TOC entries or more. + (Xavier Leroy, review by Nicolás Ojeda Bär) + +- #14883, #14884: fix Windows cross-compilation with older mingw32-gcc versions + (Brian Ward, review by Antonin Décimo and Stefan Muenzel) + +- #14881, #14882: fix printing of external types that are subject to module + constraints. + (Stefan Muenzel, review by Florian Angeletti) + +- #14871, #14914: Ignore OCAMLTOP_INCLUDE_PATH during the build. + (David Allsopp, report by Andreas Rossberg, review by Florian Angeletti) + +- #14901, #14923: Fix the generated installation script to cope with macOS's + geriatric version of bash when executing in opam's sandbox. + (David Allsopp, report by Julian Fondren and Sacha-Élie Ayoun, investigation + and initial fix by Kate Deplaix, review by Florian Angeletti) + +- #14933: Respect `sysconf(_SC_SIGSTKSZ)` when choosing the size for the + alternate signal stack, avoiding fatal errors when linked against musl libc on + some Intel CPUs. + (Nat Mote, review by Florian Angeletti and Miod Vallat) + +- #14940, fix a memory leak in the OCaml runtime by bounding the size + of the internal cache of stacks. + (Vesa Karvonen, Florian Angeletti, review by Gabriel Scherer) + +- #14966: add the missing EV_MINOR_EPHE_CLEAN constructor to + Runtime_events.runtime_phase, introduced in #13643. The runtime has emitted + this phase since 5.4, when a minor collection has to clean locked ephemerons, + but the OCaml type had no constructor for it, so consumers were handed an + out-of-range value and crashed when matching on it. + (Tim McGilchrist, review by Florian Angeletti) + +- #14969: Fix the units of the runtime events counter EV_C_MINOR_ALLOCATED_WORDS + to report as the number of words of minor heap consumed, including headers. + (Tim McGilchrist, review by Nicolás Ojeda Bär) + +- #14989: Improve build reproducibility by letting only otherlibs/{str,unix} + build their own .cmi and .cmx. The generic %.cmi/%.cmx rules of the root + Makefile were racing with them under make -j and recorded a different source + path, which changed the interface digest (and, through it, most other compiled + artefacts) as well as the debug info packed into str.a and unix.a. + (Bernhard M. Wiedemann, review by David Allsopp and Stefan Muenzel) + +- #15019: `Marshal.from_{string,bytes}`: guard against overflow in + the computation of the total data length. + (Xavier Leroy, report by Akshay Singh, review by Nicolás Ojeda Bär + and Antonin Décimo) + +- #15029: Fix a regression on Windows where an OCaml thread that never + yielded voluntarily would keep the runtime lock forever, so that the other + threads of its domain never ran. Preemptive switching between systhreads had + no effect; only explicit calls to `Thread.yield` or blocking sections would + let other threads run. + (Nicolás Ojeda Bär, report by Daniel Larraz, review by Antonin Décimo) + OCaml 5.5.0 (19 June 2026) ------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/Makefile new/ocaml-5.5.1/Makefile --- old/ocaml-5.5.0/Makefile 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/Makefile 2026-09-04 13:16:34.000000000 +0200 @@ -2196,6 +2196,12 @@ otherlibs/unix/unix.cma: otherlibraries otherlibs/str/str.cma: otherlibraries +# Root-directory targets depend on these .cmi/.cmx via .depend. The empty +# recipes keep the generic %.cmi/%.cmx rules from racing with the otherlibs +# sub-make, which records a different source path and digest in the artefacts. +otherlibs/unix/unix.cmi otherlibs/str/str.cmi: otherlibraries ; +otherlibs/unix/unix.cmx otherlibs/str/str.cmx: otherlibrariesopt ; + debugger/%: VPATH += otherlibs/unix otherlibs/dynlink ocamldebug_COMPILER_SOURCES = $(addprefix toplevel/, \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/Makefile.common new/ocaml-5.5.1/Makefile.common --- old/ocaml-5.5.0/Makefile.common 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/Makefile.common 2026-09-04 13:16:34.000000000 +0200 @@ -867,3 +867,9 @@ export MSYS := $(strip \ $(filter-out winsymlinks winsymlinks:%, $(MSYS)) winsymlinks:nativestrict) endif + +# Invocations of the compilers during the build use -nostdlib which ensures that +# existing installations and the OCAMLLIB/CAMLLIB environment variables don't +# affect the build. There isn't an equivalent flag for the toplevel's +# OCAMLTOP_INCLUDE_PATH, so it's scrubbed from the environment instead. +unexport OCAMLTOP_INCLUDE_PATH diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/VERSION new/ocaml-5.5.1/VERSION --- old/ocaml-5.5.0/VERSION 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/VERSION 2026-09-04 13:16:34.000000000 +0200 @@ -1,4 +1,4 @@ -5.5.0 +5.5.1 # Starting with OCaml 4.14, although the version string that appears above is # still correct and this file can thus still be used to figure it out, Binary files old/ocaml-5.5.0/boot/ocamlc and new/ocaml-5.5.1/boot/ocamlc differ Binary files old/ocaml-5.5.0/boot/ocamllex and new/ocaml-5.5.1/boot/ocamllex differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/build-aux/ocaml_version.m4 new/ocaml-5.5.1/build-aux/ocaml_version.m4 --- old/ocaml-5.5.0/build-aux/ocaml_version.m4 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/build-aux/ocaml_version.m4 2026-09-04 13:16:34.000000000 +0200 @@ -35,7 +35,7 @@ m4_define([OCAML__VERSION_MAJOR], [5]) m4_define([OCAML__VERSION_MINOR], [5]) m4_define([OCAML__RELEASE_NUMBER], [21]) -m4_define([OCAML__VERSION_PATCHLEVEL], [0]) +m4_define([OCAML__VERSION_PATCHLEVEL], [1]) # Note that the OCAML__VERSION_EXTRA string defined below is always empty # for officially-released versions of OCaml. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/configure new/ocaml-5.5.1/configure --- old/ocaml-5.5.0/configure 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/configure 2026-09-04 13:16:34.000000000 +0200 @@ -56,7 +56,7 @@ fi fi # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for OCaml 5.5.0. +# Generated by GNU Autoconf 2.71 for OCaml 5.5.1. # # Report bugs to <[email protected]>. # @@ -677,8 +677,8 @@ # Identity of this package. PACKAGE_NAME='OCaml' PACKAGE_TARNAME='ocaml' -PACKAGE_VERSION='5.5.0' -PACKAGE_STRING='OCaml 5.5.0' +PACKAGE_VERSION='5.5.1' +PACKAGE_STRING='OCaml 5.5.1' PACKAGE_BUGREPORT='[email protected]' PACKAGE_URL='http://www.ocaml.org' @@ -1653,7 +1653,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures OCaml 5.5.0 to adapt to many kinds of systems. +\`configure' configures OCaml 5.5.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1720,7 +1720,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of OCaml 5.5.0:";; + short | recursive ) echo "Configuration of OCaml 5.5.1:";; esac cat <<\_ACEOF @@ -1923,7 +1923,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -OCaml configure 5.5.0 +OCaml configure 5.5.1 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2580,7 +2580,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by OCaml $as_me 5.5.0, which was +It was created by OCaml $as_me 5.5.1, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3343,8 +3343,8 @@ -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: Configuring OCaml version 5.5.0" >&5 -printf "%s\n" "$as_me: Configuring OCaml version 5.5.0" >&6;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: Configuring OCaml version 5.5.1" >&5 +printf "%s\n" "$as_me: Configuring OCaml version 5.5.1" >&6;} # It's important for the setting up of defaults and the checking of the # --with-relative-libdir option to know whether the user specified --libdir. @@ -3444,7 +3444,7 @@ -VERSION=5.5.0 +VERSION=5.5.1 OCAML_DEVELOPMENT_VERSION=false @@ -3454,7 +3454,7 @@ OCAML_VERSION_MINOR=5 -OCAML_VERSION_PATCHLEVEL=0 +OCAML_VERSION_PATCHLEVEL=1 OCAML_VERSION_EXTRA= @@ -3695,15 +3695,15 @@ printf "%s\n" "#define OCAML_VERSION_MINOR 5" >>confdefs.h -printf "%s\n" "#define OCAML_VERSION_PATCHLEVEL 0" >>confdefs.h +printf "%s\n" "#define OCAML_VERSION_PATCHLEVEL 1" >>confdefs.h printf "%s\n" "#define OCAML_VERSION_ADDITIONAL \"\"" >>confdefs.h printf "%s\n" "#define OCAML_VERSION_EXTRA \"\"" >>confdefs.h -printf "%s\n" "#define OCAML_VERSION 50500" >>confdefs.h +printf "%s\n" "#define OCAML_VERSION 50501" >>confdefs.h -printf "%s\n" "#define OCAML_VERSION_STRING \"5.5.0\"" >>confdefs.h +printf "%s\n" "#define OCAML_VERSION_STRING \"5.5.1\"" >>confdefs.h printf "%s\n" "#define OCAML_RELEASE_NUMBER 21" >>confdefs.h @@ -4587,13 +4587,13 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if the installed OCaml compiler can build the cross compiler" >&5 printf %s "checking if the installed OCaml compiler can build the cross compiler... " >&6; } already_installed_version="$(ocamlc -vnum)" - if test x"5.5.0" = x"$already_installed_version" + if test x"5.5.1" = x"$already_installed_version" then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes (5.5.0)" >&5 -printf "%s\n" "yes (5.5.0)" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes (5.5.1)" >&5 +printf "%s\n" "yes (5.5.1)" >&6; } else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no (5.5.0 vs $already_installed_version)" >&5 -printf "%s\n" "no (5.5.0 vs $already_installed_version)" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no (5.5.1 vs $already_installed_version)" >&5 +printf "%s\n" "no (5.5.1 vs $already_installed_version)" >&6; } as_fn_error $? "exiting" "$LINENO" 5 fi cross_compiler=true @@ -25143,7 +25143,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by OCaml $as_me 5.5.0, which was +This file was extended by OCaml $as_me 5.5.1, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -25216,7 +25216,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -OCaml config.status 5.5.0 +OCaml config.status 5.5.1 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/ocaml-variants.opam new/ocaml-5.5.1/ocaml-variants.opam --- old/ocaml-5.5.0/ocaml-variants.opam 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/ocaml-variants.opam 2026-09-04 13:16:34.000000000 +0200 @@ -1,7 +1,7 @@ opam-version: "2.0" -version: "5.5.0" +version: "5.5.1" license: "LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception" -synopsis: "Official release of OCaml 5.5.0" +synopsis: "Official release of OCaml 5.5.1" maintainer: "[email protected]" authors: [ "Xavier Leroy" @@ -16,7 +16,7 @@ bug-reports: "https://github.com/ocaml/ocaml/issues" depends: [ # This is OCaml 5.5.0 - "ocaml" {= "5.5.0" & post} + "ocaml" {= "5.5.1" & post} # General base- packages "base-unix" {post} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/otherlibs/runtime_events/runtime_events.ml new/ocaml-5.5.1/otherlibs/runtime_events/runtime_events.ml --- old/ocaml-5.5.0/otherlibs/runtime_events/runtime_events.ml 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/otherlibs/runtime_events/runtime_events.ml 2026-09-04 13:16:34.000000000 +0200 @@ -95,6 +95,7 @@ | EV_COMPACT_FORWARD | EV_COMPACT_RELEASE | EV_EMPTY_MINOR +| EV_MINOR_EPHE_CLEAN type lifecycle = EV_RING_START @@ -206,6 +207,7 @@ | EV_COMPACT_FORWARD -> "compaction_forward" | EV_COMPACT_RELEASE -> "compaction_release" | EV_EMPTY_MINOR -> "empty_minor" + | EV_MINOR_EPHE_CLEAN -> "minor_ephe_clean" let lifecycle_name lifecycle = match lifecycle with diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/otherlibs/runtime_events/runtime_events.mli new/ocaml-5.5.1/otherlibs/runtime_events/runtime_events.mli --- old/ocaml-5.5.0/otherlibs/runtime_events/runtime_events.mli 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/otherlibs/runtime_events/runtime_events.mli 2026-09-04 13:16:34.000000000 +0200 @@ -469,6 +469,13 @@ This includes time spent trying to become stop-the-world leader. @since 5.4 *) +| EV_MINOR_EPHE_CLEAN +(** +Event spanning the cleaning of ephemerons whose keys were locked during a minor +GC. Keys that were promoted are updated to their new location and ephemerons +whose keys were collected are emptied. +@since 5.4 +*) (** Lifecycle events for Runtime_events and domains. *) type lifecycle = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/otherlibs/unix/system.c new/ocaml-5.5.1/otherlibs/unix/system.c --- old/ocaml-5.5.0/otherlibs/unix/system.c 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/otherlibs/unix/system.c 2026-09-04 13:16:34.000000000 +0200 @@ -23,6 +23,7 @@ #include "caml/unixsupport.h" #include <process.h> #include <stdio.h> +#include <errno.h> CAMLprim value caml_unix_system(value command) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/caml/fiber.h new/ocaml-5.5.1/runtime/caml/fiber.h --- old/ocaml-5.5.0/runtime/caml/fiber.h 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/caml/fiber.h 2026-09-04 13:16:34.000000000 +0200 @@ -262,6 +262,7 @@ void caml_change_max_stack_size (uintnat new_max_wsize); void caml_maybe_expand_stack(void); CAMLextern void caml_free_stack(struct stack_info* stk); +CAMLextern void caml_free_stack_cache(struct stack_info** cache); /* gc_regs_buckets is allocated on-demand by [maybe_expand_stack]. */ CAMLextern void caml_free_gc_regs_buckets(value *gc_regs_buckets); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/caml/memory.h new/ocaml-5.5.1/runtime/caml/memory.h --- old/ocaml-5.5.0/runtime/caml/memory.h 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/caml/memory.h 2026-09-04 13:16:34.000000000 +0200 @@ -96,22 +96,14 @@ CAMLmalloc(caml_stat_free, 1, 1) CAMLextern caml_stat_block caml_stat_alloc_noexc(asize_t); -/* [caml_stat_alloc_aligned(size, modulo, block*)] allocates a memory block of - the requested [size] (in bytes), the starting address of which is aligned to - the provided [modulo] value. The function returns the aligned address, as - well as the unaligned [block] (as an output parameter). It throws an OCaml - exception in case the request fails, and so requires the runtime lock. -*/ -CAMLaligned_alloc(caml_stat_free, 1, 1, 2) CAMLreturns_nonnull() -CAMLextern void* caml_stat_alloc_aligned(asize_t, int modulo, caml_stat_block*); - -/* [caml_stat_alloc_aligned_noexc] is a variant of [caml_stat_alloc_aligned] - that returns NULL in case the request fails, and doesn't require the runtime - lock to be held. +/* [caml_stat_calloc(num, size)] allocates a block of memory for an array + of [num] elements, each of them [size] bytes long, and initializes all its + bits to zero, effectively allocating a zero-initialized memory block of + [num * size] bytes. It throws an OCaml exception in case + the request fails, and so requires the runtime lock to be held. */ -CAMLaligned_alloc(caml_stat_free, 1, 1, 2) -CAMLextern void* caml_stat_alloc_aligned_noexc(asize_t, int modulo, - caml_stat_block*); +CAMLcalloc(caml_stat_free, 1, 1, 2) +CAMLextern caml_stat_block caml_stat_calloc(asize_t, asize_t); /* [caml_stat_calloc_noexc(num, size)] allocates a block of memory for an array of [num] elements, each of them [size] bytes long, and initializes all its diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/domain.c new/ocaml-5.5.1/runtime/domain.c --- old/ocaml-5.5.0/runtime/domain.c 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/domain.c 2026-09-04 13:16:34.000000000 +0200 @@ -190,7 +190,6 @@ struct interruptor interruptor; /* backup thread */ - caml_plat_thread backup_thread; atomic_uintnat backup_thread_msg; caml_plat_mutex domain_lock; caml_plat_cond domain_cond; @@ -203,6 +202,7 @@ typedef struct dom_internal dom_internal; static CAMLthread_local dom_internal* domain_self; +static CAMLthread_local bool is_backup_thread; static struct { /* enter barrier for STW sections, participating domains arrive into @@ -1168,6 +1168,7 @@ domain_self = di; caml_state = di->state; + is_backup_thread = true; msg = atomic_load_acquire (&di->backup_thread_msg); while (msg != BT_TERMINATE) { @@ -1222,6 +1223,7 @@ static value install_backup_thread_exn (dom_internal* di) { int err; + caml_plat_thread backup_thread; #ifndef _WIN32 sigset_t mask, old_mask; #endif @@ -1244,7 +1246,7 @@ #endif atomic_store_release(&di->backup_thread_msg, BT_ENTERING_OCAML); - err = caml_plat_thread_create(&di->backup_thread, 0, backup_thread_func, + err = caml_plat_thread_create(&backup_thread, 0, backup_thread_func, (void*)di); #ifndef _WIN32 @@ -1253,7 +1255,7 @@ if (err != 0) return caml_check_error_exn(err, "failed to create domain backup thread"); - caml_plat_thread_detach(di->backup_thread); + caml_plat_thread_detach(backup_thread); return Val_unit; } @@ -2177,8 +2179,7 @@ CAMLexport int caml_bt_is_self(void) { - return caml_plat_thread_equal(domain_self->backup_thread, - caml_plat_thread_self()); + return is_backup_thread; } CAMLexport intnat caml_domain_is_multicore (void) @@ -2408,6 +2409,8 @@ if(domain_state->current_stack != NULL) { caml_free_stack(domain_state->current_stack); } + caml_free_stack_cache(domain_state->stack_cache); + domain_state->stack_cache = NULL; caml_free_backtrace_buffer(domain_state->backtrace_buffer); caml_free_gc_regs_buckets(domain_state->gc_regs_buckets); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/fiber.c new/ocaml-5.5.1/runtime/fiber.c --- old/ocaml-5.5.0/runtime/fiber.c 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/fiber.c 2026-09-04 13:16:34.000000000 +0200 @@ -181,6 +181,8 @@ cache[cache_bucket] != NULL) { stack = cache[cache_bucket]; cache[cache_bucket] = + /* In the cache, reuse the exception_ptr field as an internal + linked-list next pointer. */ (struct stack_info*)stack->exception_ptr; CAMLassert(stack->cache_bucket == stack_cache_bucket(wosize)); hand = stack->handler; @@ -560,6 +562,43 @@ return stk; } +atomic_uintnat caml_cache_stacks_per_class = +#if defined(USE_MMAP_MAP_STACK) + 1 +#else + 128 +#endif + ; + +void caml_free_stack_memory(struct stack_info* stack) { + atomic_fetch_sub(&live_stack_counter, + (value*)(stack->handler+1) - (value*)stack); +#ifdef DEBUG + memset(stack, 0x42, (char*)stack->handler - (char*)stack); +#endif +#ifdef USE_MMAP_MAP_STACK + munmap(stack, stack->size); +#else + caml_stat_free(stack); +#endif +} + +void caml_free_stack_cache (struct stack_info** stack_cache) +{ + if (stack_cache == NULL) + return; + + struct stack_info* stored_stack; + for (int i = 0; i < NUM_STACK_SIZE_CLASSES; i++) { + while (stack_cache[i] != NULL) { + stored_stack = stack_cache[i]; + stack_cache[i] = (struct stack_info*) stored_stack->exception_ptr; + caml_free_stack_memory(stored_stack); + }; + } + caml_stat_free(stack_cache); +} + void caml_free_stack (struct stack_info* stack) { CAMLnoalloc; @@ -568,24 +607,24 @@ CAMLassert(stack->magic == 42); CAMLassert(cache != NULL); if (stack->cache_bucket != -1) { - stack->exception_ptr = - (void*)(cache[stack->cache_bucket]); - cache[stack->cache_bucket] = stack; + struct stack_info* top = (struct stack_info*)cache[stack->cache_bucket]; + /* When stored inside the cache, the fiber id field is reused to count + the number of fibers in the bucket */ + int64_t count = top ? top->id : 0; + if (count < caml_cache_stacks_per_class) { + /* Reuse exception_ptr to point to the next fiber in the bucket. */ + stack->exception_ptr = (void *)top; + stack->id = count + 1; + cache[stack->cache_bucket] = stack; #ifdef DEBUG memset(Stack_base(stack), 0x42, (Stack_high(stack)-Stack_base(stack))*sizeof(value)); #endif + } else { + caml_free_stack_memory(stack); + } } else { - atomic_fetch_sub(&live_stack_counter, - (value*)(stack->handler+1) - (value*)stack); -#ifdef DEBUG - memset(stack, 0x42, (char*)stack->handler - (char*)stack); -#endif -#ifdef USE_MMAP_MAP_STACK - munmap(stack, stack->size); -#else - caml_stat_free(stack); -#endif + caml_free_stack_memory(stack); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/gc_ctrl.c new/ocaml-5.5.1/runtime/gc_ctrl.c --- old/ocaml-5.5.0/runtime/gc_ctrl.c 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/gc_ctrl.c 2026-09-04 13:16:34.000000000 +0200 @@ -51,6 +51,7 @@ extern _Atomic uintnat caml_custom_minor_max_bsz; /* see custom.c */ extern uintnat caml_minor_heap_max_wsz; /* see domain.c */ extern atomic_uintnat caml_mark_stack_prune_factor; /* see major_gc.c */ +extern atomic_uintnat caml_cache_stacks_per_class; /* see fiber.c */ CAMLprim value caml_gc_quick_stat(value v) { @@ -514,6 +515,7 @@ #define TWEAK(v) { #v, &caml_##v, 0 } TWEAK(mark_stack_prune_factor), TWEAK(small_heap_limit), + TWEAK(cache_stacks_per_class), #undef TWEAK }; enum {N_GC_TWEAKS = sizeof(gc_tweaks)/sizeof(gc_tweaks[0])}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/intern.c new/ocaml-5.5.1/runtime/intern.c --- old/ocaml-5.5.0/runtime/intern.c 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/intern.c 2026-09-04 13:16:34.000000000 +0200 @@ -843,6 +843,7 @@ int header_len; uintnat data_len; uintnat uncompressed_data_len; + uintnat total_len; /* header_len + data_len */ uintnat num_objects; uintnat whsize; int compressed; @@ -902,6 +903,10 @@ default: intern_failwith2(fun_name, "bad object"); } + if (caml_uadd_overflow(h->header_len, h->data_len, &h->total_len)) { + intern_failwith2 + (fun_name, "object too large to be read back on this platform"); + } } /* Decompress the input if needed. @@ -1035,7 +1040,8 @@ /* Initialize global state */ intern_init(s, &Byte_u(str, ofs), caml_string_length(str) - ofs, NULL); caml_parse_header(s, "input_val_from_string", &h); - if (ofs + h.header_len + h.data_len > caml_string_length(str)) + if (ofs < 0 || ofs + h.total_len < h.total_len || + ofs + h.total_len > caml_string_length(str)) caml_failwith("input_val_from_string: bad length"); /* Allocate result */ intern_alloc_storage(s, h.whsize, h.num_objects); @@ -1077,7 +1083,7 @@ intern_init(s, src, len, input); caml_parse_header(s, fun_name, &h); - if (h.header_len + h.data_len > len) + if (h.total_len > len) intern_failwith2(fun_name, "bad length"); s->intern_src_end = s->intern_src + h.data_len; return input_val_from_block(s, &h); @@ -1117,7 +1123,7 @@ { uint32_t magic; int header_len; - uintnat data_len; + uintnat data_len, total_len; struct caml_intern_state *s = init_intern_state (); s->intern_src = &Byte_u(buff, Long_val(ofs)); @@ -1147,7 +1153,12 @@ default: caml_failwith("Marshal.data_size: bad object"); } - return Val_long((header_len - 16) + data_len); + if (caml_uadd_overflow(header_len, data_len, &total_len) + || total_len > Max_long + 20) { + caml_failwith("Marshal.data_size: " + "object too large to be read back on this platform"); + } + return Val_long(total_len - 16); } /* Resolution of code pointers */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/memory.c new/ocaml-5.5.1/runtime/memory.c --- old/ocaml-5.5.0/runtime/memory.c 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/memory.c 2026-09-04 13:16:34.000000000 +0200 @@ -705,6 +705,16 @@ } } +/* [sz] is a number of bytes */ +CAMLexport caml_stat_block caml_stat_calloc(asize_t num, asize_t sz) +{ + void *result = caml_stat_calloc_noexc(num, sz); + /* calloc() may return NULL if size is 0 or number of elements is 0 */ + if ((result == NULL) && (sz != 0) && (num != 0)) + caml_raise_out_of_memory(); + return result; +} + CAMLexport caml_stat_string caml_stat_strdup_noexc(const char *s) { size_t slen = strlen(s); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/minor_gc.c new/ocaml-5.5.1/runtime/minor_gc.c --- old/ocaml-5.5.0/runtime/minor_gc.c 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/minor_gc.c 2026-09-04 13:16:34.000000000 +0200 @@ -737,7 +737,7 @@ CAML_EV_COUNTER(EV_C_MINOR_ALLOCATED, minor_allocated_bytes); CAML_EV_COUNTER(EV_C_MINOR_ALLOCATED_WORDS, - Whsize_wosize(minor_allocated_bytes)); + Wsize_bsize(minor_allocated_bytes)); CAML_EV_END(EV_MINOR); if (minor_allocated_bytes == 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/signals.c new/ocaml-5.5.1/runtime/signals.c --- old/ocaml-5.5.0/runtime/signals.c 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/signals.c 2026-09-04 13:16:34.000000000 +0200 @@ -21,6 +21,9 @@ #include <errno.h> #include <stdbool.h> #include "caml/config.h" +#ifndef _WIN32 +#include <unistd.h> +#endif #ifdef USE_MMAP_MAP_STACK #include <sys/mman.h> #endif @@ -544,12 +547,40 @@ return Val_int(caml_rev_convert_signal_number(Int_val(signo))); } +#ifdef POSIX_SIGNALS +/* On Linux, the minimum signal stack size is architecture-dependent, whereas + * SIGSTKSZ can be a build-time constant and may be smaller than the run-time + * minimum (e.g. on musl with certain Intel CPUs). Thus, we call sysconf for the + * run-time value ourselves. */ +Caml_inline size_t caml_sigstksz(void) +{ + /* Compute the recommended signal stack size only once. Atomic due to benign + * concurrent reads during domain init. */ + static _Atomic size_t cache = 0; + size_t size = atomic_load_relaxed(&cache); + if (size == 0) { + long sz; +#ifdef _SC_SIGSTKSZ + /* glibc/musl only */ + sz = sysconf(_SC_SIGSTKSZ); + if (sz <= 0) + sz = SIGSTKSZ; +#else + sz = SIGSTKSZ; +#endif + size = (size_t) sz; + atomic_store_relaxed(&cache, size); + } + return size; +} +#endif /* POSIX_SIGNALS */ + void * caml_init_signal_stack(void) { #ifdef POSIX_SIGNALS stack_t stk; stk.ss_flags = 0; - stk.ss_size = SIGSTKSZ; + stk.ss_size = caml_sigstksz(); /* The memory used for the alternate signal stack must not free'd before calling sigaltstack with SS_DISABLE. malloc/mmap is therefore used rather than caml_stat_alloc_noexc so that if a shutdown path erroneously fails @@ -562,7 +593,7 @@ if (stk.ss_sp == MAP_FAILED) return NULL; if (sigaltstack(&stk, NULL) < 0) { - munmap(stk.ss_sp, SIGSTKSZ); + munmap(stk.ss_sp, stk.ss_size); return NULL; } #else @@ -586,7 +617,7 @@ stack_t stk, disable; disable.ss_flags = SS_DISABLE; disable.ss_sp = NULL; /* not required but avoids a valgrind false alarm */ - disable.ss_size = SIGSTKSZ; /* macOS wants a valid size here */ + disable.ss_size = caml_sigstksz(); /* macOS wants a valid size here */ if (sigaltstack(&disable, &stk) < 0) { caml_fatal_error("Failed to reset signal stack (err %d)", errno); } @@ -598,7 +629,7 @@ /* Memory was allocated with malloc/mmap directly (see caml_init_signal_stack) */ #ifdef USE_MMAP_MAP_STACK - munmap(signal_stack, SIGSTKSZ); + munmap(signal_stack, caml_sigstksz()); #else free(signal_stack); #endif /* USE_MMAP_MAP_STACK */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/runtime/startup_byt.c new/ocaml-5.5.1/runtime/startup_byt.c --- old/ocaml-5.5.0/runtime/startup_byt.c 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/runtime/startup_byt.c 2026-09-04 13:16:34.000000000 +0200 @@ -159,15 +159,14 @@ void caml_read_section_descriptors(int fd, struct exec_trailer *trail) { - int toc_size; - - toc_size = trail->num_sections * 8; - trail->section = caml_stat_alloc(toc_size); - lseek(fd, - (long) (TRAILER_SIZE + toc_size), SEEK_END); + size_t toc_size; + trail->section = caml_stat_calloc(trail->num_sections, 8); + toc_size = (size_t) trail->num_sections * 8; + lseek(fd, - (file_offset) (TRAILER_SIZE + toc_size), SEEK_END); if (read(fd, (char *) trail->section, toc_size) != toc_size) caml_fatal_error("cannot read section table"); /* Fixup endianness of lengths */ - for (int i = 0; i < trail->num_sections; i++) + for (uint32_t i = 0; i < trail->num_sections; i++) fixup_endianness_trailer(&(trail->section[i].len)); } @@ -178,13 +177,13 @@ int32_t caml_seek_optional_section(int fd, struct exec_trailer *trail, const char *name) { - long ofs; - - ofs = TRAILER_SIZE + trail->num_sections * 8; - for (int i = trail->num_sections - 1; i >= 0; i--) { + size_t ofs = TRAILER_SIZE + (size_t) trail->num_sections * 8; + uint32_t i = trail->num_sections; + while (i > 0) { + i -= 1; ofs += trail->section[i].len; if (strncmp(trail->section[i].name, name, 4) == 0) { - lseek(fd, -ofs, SEEK_END); + lseek(fd, - (file_offset) ofs, SEEK_END); return trail->section[i].len; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/testsuite/tests/lib-marshal/fuzzy.ml new/ocaml-5.5.1/testsuite/tests/lib-marshal/fuzzy.ml --- old/ocaml-5.5.0/testsuite/tests/lib-marshal/fuzzy.ml 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/testsuite/tests/lib-marshal/fuzzy.ml 2026-09-04 13:16:34.000000000 +0200 @@ -79,6 +79,17 @@ end; Gc.full_major() +(* Same, but use Marshal.from_string instead *) + +let test_string ic = + In_channel.set_binary_mode ic true; + let s = In_channel.input_all ic in + begin try + ignore (Marshal.from_string s 0) + with Failure _ | Invalid_argument _ | Out_of_memory -> () + end; + Gc.full_major() + (* Internal fuzzing. Rather naive. *) let random_offset b = @@ -123,7 +134,7 @@ end; Gc.full_major() done; - Bytes.set_uint8 b i (Bytes.get_uint8 b i) + Bytes.set_uint8 b i (String.get_uint8 d i) done let () = @@ -135,7 +146,9 @@ "-x", Arg.Unit fuzz1, "<num iter> Perform internal fuzzing test (exhaustive 1-byte)"; "-r", Arg.Unit (fun () -> test stdin), - " Read marshaled data from standard input" + " Read marshaled data from standard input"; + "-s", Arg.Unit (fun () -> test_string stdin), + " Read marshaled data from standard input via a string" ] (fun s -> raise (Arg.Bad ("don't know what to do with " ^ s))) "Usage: fuzzy [option].\nOptions are:" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/testsuite/tests/lib-systhreads/testpreempt.ml new/ocaml-5.5.1/testsuite/tests/lib-systhreads/testpreempt.ml --- old/ocaml-5.5.0/testsuite/tests/lib-systhreads/testpreempt.ml 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/testsuite/tests/lib-systhreads/testpreempt.ml 2026-09-04 13:16:34.000000000 +0200 @@ -1,12 +1,6 @@ (* TEST include systhreads; hassysthreads; - (* - On Windows, we use Sleep(0) for triggering preemption of threads. - However, this does not seem very reliable, so that this test fails - on some Windows configurations. See GPR #1533. - *) - not-target-windows; { bytecode; }{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/testsuite/tests/parallel/fiber_migration.ml new/ocaml-5.5.1/testsuite/tests/parallel/fiber_migration.ml --- old/ocaml-5.5.0/testsuite/tests/parallel/fiber_migration.ml 1970-01-01 01:00:00.000000000 +0100 +++ new/ocaml-5.5.1/testsuite/tests/parallel/fiber_migration.ml 2026-09-04 13:16:34.000000000 +0200 @@ -0,0 +1,72 @@ +(* TEST + multicore; + { + bytecode; + }{ + native; + } +*) + +(** Test fiber migration. We test that sending fibers from one domain + to another do not increase the size of the fiber cache of the + receiving domain in an unbounded way *) + +(** Migration effect *) +type _ Effect.t += Migrate : unit Effect.t + +(** Transfer of continuation to a shared stack *) +let shared_fiber_stack: (unit->unit) list Atomic.t = Atomic.make [] +let rec push item = + (* Update to Atomic.update once the function is less fresh *) + let before = Atomic.get shared_fiber_stack in + let after = item :: before in + if not (Atomic.compare_and_set shared_fiber_stack before after) then + push item +let get_fibers () = Atomic.get shared_fiber_stack +let take_fibers () = List.rev (Atomic.exchange shared_fiber_stack []) + +(** Produce fibers by performing the Migrate effect, then store + them in the shared stack *) +let producer_loop () = + let max_live_fiber = 10 in + for _ = 1 to max_live_fiber do + match Effect.perform Migrate with + | () -> () + | effect Migrate, k -> push (Effect.Deep.continue k) + done; + (* Wait for the consumer to cache all fibers in order to bound the + number of live fibers *) + while get_fibers () <> [] do Domain.cpu_relax () done + +(** Receiver loop: pick fibers in the shared stack, resume them and + store them in the cache of the receiver domain. *) +let rec receiver_loop = function + | [] -> + (* wait for the producer to send more fibers *) + while get_fibers () = [] do + Domain.cpu_relax () + done; + receiver_loop (take_fibers ()) + | fiber :: fibers -> + (* we resume the fiber in the shared stack, and store it in the + current domain cache *) + match fiber () with + | () -> receiver_loop fibers + | exception Exit -> () + +let main () = + let receiving_domain = Domain.spawn (fun () -> receiver_loop []) in + let finally () = + push (fun () -> raise Exit); + Domain.join receiving_domain + in + Fun.protect ~finally begin fun () -> + for _ = 1 to 50 do producer_loop () done; + (* Check that the size of fiber caches did not grow unboundedly during the loop *) + let cache_size = (Gc.quick_stat()).live_stacks_words in + if (cache_size < 25000) then + Printf.printf "OK\n%!" + else Printf.printf "Bad %d\n%!" cache_size + end + +let () = main () diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/testsuite/tests/parallel/fiber_migration.reference new/ocaml-5.5.1/testsuite/tests/parallel/fiber_migration.reference --- old/ocaml-5.5.0/testsuite/tests/parallel/fiber_migration.reference 1970-01-01 01:00:00.000000000 +0100 +++ new/ocaml-5.5.1/testsuite/tests/parallel/fiber_migration.reference 2026-09-04 13:16:34.000000000 +0200 @@ -0,0 +1 @@ +OK diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/testsuite/tests/typing-misc/type_external.ml new/ocaml-5.5.1/testsuite/tests/typing-misc/type_external.ml --- old/ocaml-5.5.0/testsuite/tests/typing-misc/type_external.ml 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/testsuite/tests/typing-misc/type_external.ml 2026-09-04 13:16:34.000000000 +0200 @@ -283,3 +283,76 @@ [%%expect{| type t = external "\\\\\\" |}] + +(* manifest *) +module type P = sig + type t = external "p" +end +module Q(P : P) : P with type t = P.t = struct + type t = P.t +end;; +[%%expect{| +module type P = sig type t = external "p" end +Lines 4-6, characters 40-3: +4 | ........................................struct +5 | type t = P.t +6 | end.. +Error: Signature mismatch: + Modules do not match: + sig type t = P.t end + is not included in + sig type t = external "p" end + Type declarations do not match: + type t = P.t + is not included in + type t = external "p" + The first is abstract, but the second is external "p". +|}] + +module type P1 = sig + type !'b t = external "p" +end +module Q(P1 : P1) : P1 with type 'a t = 'a P1.t = struct + type 'a t = 'a P1.t +end;; +[%%expect{| +module type P1 = sig type !'b t = external "p" end +Lines 4-6, characters 50-3: +4 | ..................................................struct +5 | type 'a t = 'a P1.t +6 | end.. +Error: Signature mismatch: + Modules do not match: + sig type !'a t = 'a P1.t end + is not included in + sig type !'a t = external "p" end + Type declarations do not match: + type !'a t = 'a P1.t + is not included in + type !'a t = external "p" + The first is abstract, but the second is external "p". +|}] + +module type P1 = sig + type +'b t = external "p" +end +module Q(P1 : P1) : P1 with type 'a t = 'a P1.t = struct + type 'a t = 'a P1.t +end;; +[%%expect{| +module type P1 = sig type +!'b t = external "p" end +Lines 4-6, characters 50-3: +4 | ..................................................struct +5 | type 'a t = 'a P1.t +6 | end.. +Error: Signature mismatch: + Modules do not match: + sig type +!'a t = 'a P1.t end + is not included in + sig type +!'a t = external "p" end + Type declarations do not match: + type +!'a t = 'a P1.t + is not included in + type +!'a t = external "p" + The first is abstract, but the second is external "p". +|}] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/testsuite/tests/typing-modular-explicits/general.ml new/ocaml-5.5.1/testsuite/tests/typing-modular-explicits/general.ml --- old/ocaml-5.5.0/testsuite/tests/typing-modular-explicits/general.ml 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/testsuite/tests/typing-modular-explicits/general.ml 2026-09-04 13:16:34.000000000 +0200 @@ -1495,3 +1495,30 @@ ^^ This extra argument is not expected. |}] + +module type S = sig type t type e val v : e -> t end +let helper (type a) (module M : S with type t = a) _ : a = assert false +let outer (type a) (type b) (module M : S with type e = a and type t = b) e = + helper (module M) (M.v e) +[%%expect {| +module type S = sig type t type e val v : e -> t end +val helper : (module S with type t = 'a) -> 'b -> 'a = <fun> +val outer : (module M : S with type e = 'a and type t = 'b) -> M.e -> M.t = + <fun> +|}] + + +(* Bug #14891: nondep package type *) + +module type N = sig module type T end +module type S = sig type 'a t end +let f (module M:N) (x:(module M.T)) = x +let app = f (module struct module type T = S end) +let ok = f (module struct module type T = S end) (module List:S) +[%%expect {| +module type N = sig module type T end +module type S = sig type 'a t end +val f : (module M : N) -> (module M.T) -> (module M.T) = <fun> +val app : (module S) -> (module S) = <fun> +val ok : (module S) = <module> +|}] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/typing/ctype.ml new/ocaml-5.5.1/typing/ctype.ml --- old/ocaml-5.5.0/typing/ctype.ml 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/typing/ctype.ml 2026-09-04 13:16:34.000000000 +0200 @@ -2321,7 +2321,7 @@ | Tpackage {pack_path = p; pack_constraints} -> begin match Path.check_for_unbound_unscoped_idents bound_id p with | Some i -> - occur_normalize_modtype_path env bound_uv bound_id i p + occur_normalize_modtype_path env bound_uv bound_id i p ty (fun pack_path -> Tpackage {pack_path; pack_constraints}) | None -> List.iter (fun (_, t) -> occur_rec env bound_uv bound_id t) @@ -2334,6 +2334,7 @@ match id_escape with | Some i -> occur_normalize_modtype_path env bound_uv bound_id i pack.pack_path + ty (fun pack_path -> Tfunctor (l, id, {pack with pack_path}, ty)) | None -> List.iter (fun (_, t) -> occur_rec env bound_uv bound_id t) @@ -2358,7 +2359,7 @@ occur_desc env bound_uv bound_id ty end else iter_type_expr (occur_rec env bound_uv bound_id) ty - and occur_normalize_modtype_path env bound_uv bound_id us p f = + and occur_normalize_modtype_path env bound_uv bound_id us p ty f = match Env.try_normalize_modtype_path env p with | None -> raise_escape_exn (Module (Ident.of_unscoped us)) | Some p' -> @@ -2459,7 +2460,7 @@ Similar to [nondep_type], however we remove the dependency in place, while [nondep] does a copy of the type. *) -let identifier_escape env idl ty = +let identifier_escape env idl initial_ty = with_type_mark begin fun mark -> let rec occur ?(ignore_mark=false) idl ty = if try_mark_node mark ty || ignore_mark then begin @@ -2473,7 +2474,7 @@ begin match Path.find_free_opt idl pack.pack_path with | None -> iter_type_expr (occur idl) ty | Some i -> - occur_normalize_modtype_path env idl pack.pack_path i + occur_normalize_modtype_path env idl pack.pack_path i ty (fun pack_path -> Tpackage {pack with pack_path}) end | Tobject (_, ({contents = Some (p, _)} as nm)) -> @@ -2489,7 +2490,7 @@ | Tfunctor (l, id, pack, t) -> begin match Path.find_free_opt idl pack.pack_path with | Some i -> - occur_normalize_modtype_path env idl pack.pack_path i + occur_normalize_modtype_path env idl pack.pack_path i ty (fun pack_path -> Tfunctor (l, id, {pack with pack_path}, ty)) | None -> List.iter (fun (_, t) -> occur idl t) pack.pack_constraints; @@ -2514,14 +2515,14 @@ occur ~ignore_mark:true idl ty end else iter_type_expr (occur idl) ty - and occur_normalize_modtype_path env idl p id f = + and occur_normalize_modtype_path env idl p id ty f = match Env.try_normalize_modtype_path env p with | None -> raise_escape_exn (Module id) | Some p' -> set_type_desc ty (f p'); occur ~ignore_mark:true idl ty in - occur (List.map Ident.of_unscoped idl) ty + occur (List.map Ident.of_unscoped idl) initial_ty end let identifier_escape_for tr_exn env idl t = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocaml-5.5.0/typing/out_type.ml new/ocaml-5.5.1/typing/out_type.ml --- old/ocaml-5.5.0/typing/out_type.ml 2026-06-19 13:32:53.000000000 +0200 +++ new/ocaml-5.5.1/typing/out_type.ml 2026-09-04 13:16:34.000000000 +0200 @@ -1453,7 +1453,7 @@ | _ -> {ot_non_gen=false; ot_name="?"; ot_variance} in let type_defined decl = - let abstr = + let non_inferable_variance = match decl.type_kind with Type_abstract _ -> decl.type_manifest = None || decl.type_private = Private @@ -1465,16 +1465,18 @@ | Type_open -> decl.type_manifest = None | Type_external _ -> - assert (decl.type_manifest = None); true + true in let vari = List.map2 (fun ty v -> let is_var = is_Tvar ty in - let with_variance = !Clflags.print_variance || abstr || not is_var in + let with_variance = + !Clflags.print_variance || non_inferable_variance || not is_var + in let with_injectivity = !Clflags.print_variance || - (abstr || not is_var) && + (non_inferable_variance || not is_var) && type_kind_is_abstract decl && match decl.type_manifest with | None -> true
