test: env-stanza -open variant of cross-lib .cmi regression baseline#14776
Merged
Alizter merged 1 commit intoMay 30, 2026
Merged
Conversation
Fifth fixture in the cross-lib `.cmi` regression baseline family (ocaml#14584): pins consumer-compile cmi-dep behaviour when `-open M` is injected by an `(env ...)` stanza rather than the intermediate library's own `(flags ...)` field. The intermediate's library stanza uses `:standard`. Asserts build success under `--sandbox=copy`. The forthcoming per-module narrowing work (ocaml#14492) will validate that this construct continues to work soundly under tighter per-module dep tracking. Signed-off-by: Robin Bate Boerop <me@robinbb.com>
robinbb
added a commit
to robinbb/dune
that referenced
this pull request
May 29, 2026
Closes a soundness gap in the per-module narrowing pipeline missed by the wrapped / ppx-runtime / virtual-impl recoveries: when a dependency library's effective flags inject [-open M], its source can reference [M]'s identifiers without naming [M], and ocamldep emits no token to drive the cross-library walker. Both the library stanza's own [(flags ...)] and any [(env ...)] stanza that contributes default flags to the dep lib's directory can inject [-open] entries. Reported by RyanJamesStewart on ocaml#14517 with this fixture: an unwrapped [middle] depending on unwrapped [prelude] with [(flags (:standard -open Prelude))], exposing [val pick : unit -> color] (where [color] resolves through the open to [Prelude.color]). The consumer pattern-matches the result against bare constructors. The compile genuinely needs [prelude.cmi] to resolve the constructors; the BFS over [ocamldep -modules] cannot reach [prelude] (no syntactic [Prelude] token on either side); the three existing recoveries do not catch it ([prelude] is not wrapped, not a ppx-runtime lib, not a virtual-impl). [Module_compilation.cross_lib_tight_set]: - Add [~mode] (the consumer's compile mode) so we can expand a dep lib's effective flags via [Ocaml_flags.get]. - Extend [read_entry_deps] to compute [read_lib_opens] for the visited lib and union the result into the BFS frontier. Localised in the BFS rather than the initial-frontier computation so the reachability rule reads as: a module is reachable iff the consumer references it, or some reached module's ocamldep names it, or some reached module's owning lib's effective flags open it. - [read_lib_opens] short-circuits to empty for non-local libs; their [src_dir] is not a build path, and env stanzas cannot inject flags into already-compiled artifacts. For local libs we evaluate [Ocaml_flags_db.ocaml_flags] which folds env-stanza flags under the library stanza's spec, so both injection paths are captured even when the library stanza is [:standard]. [Lib_info]: - Add [stanza_flags : Dune_lang.Ocaml_flags.Spec.t] field plus accessor. Local libs carry their stanza's [conf.buildable.flags]; external libs ([findlib], [dune_package]) carry [Spec.standard]. Regression tests on `main` that this patch makes pass under the per-module narrowing stack: - [cross-lib-open-flag-barrier.t] (added by ocaml#14584) pins the stanza-flag injection path. Fails on L4 and L5 head before this patch (Unbound constructor Green under [--sandbox=copy]); passes after. - [cross-lib-env-open-flag-barrier.t] (added by ocaml#14776) pins the env-stanza injection path: same shape, but [-open Prelude] is supplied by an [(env ...)] stanza while the library stanza uses [:standard]. Fails on the pre-ocaml#14517 [:standard]-short-circuit variant of this patch (same Unbound constructor error); passes after. Signed-off-by: Robin Bate Boerop <me@robinbb.com>
There was a problem hiding this comment.
Pull request overview
Adds a single blackbox cram fixture extending the cross-library .cmi regression baseline family (#14584) to cover the (env ...) stanza path for injecting -open Prelude into an intermediate library's effective flags. It complements the existing cross-lib-open-flag-barrier.t (stanza (flags ...) path) and asserts a successful build under --sandbox=copy.
Changes:
- New cram test
cross-lib-env-open-flag-barrier.texercising env-stanza-injected-openacross a prelude → middle → consumer chain.
Collaborator
Author
|
@Alizter I'm factoring this test out of the L5 PR. |
Alizter
approved these changes
May 30, 2026
robinbb
added a commit
that referenced
this pull request
Jun 8, 2026
Closes a soundness gap in the per-module narrowing pipeline missed by the wrapped / ppx-runtime / virtual-impl recoveries: when a dependency library's effective flags inject [-open M], its source can reference [M]'s identifiers without naming [M], and ocamldep emits no token to drive the cross-library walker. Both the library stanza's own [(flags ...)] and any [(env ...)] stanza that contributes default flags to the dep lib's directory can inject [-open] entries. Reported by RyanJamesStewart on #14517 with this fixture: an unwrapped [middle] depending on unwrapped [prelude] with [(flags (:standard -open Prelude))], exposing [val pick : unit -> color] (where [color] resolves through the open to [Prelude.color]). The consumer pattern-matches the result against bare constructors. The compile genuinely needs [prelude.cmi] to resolve the constructors; the BFS over [ocamldep -modules] cannot reach [prelude] (no syntactic [Prelude] token on either side); the three existing recoveries do not catch it ([prelude] is not wrapped, not a ppx-runtime lib, not a virtual-impl). [Module_compilation.cross_lib_tight_set]: - Add [~mode] (the consumer's compile mode) so we can expand a dep lib's effective flags via [Ocaml_flags.get]. - Extend [read_entry_deps] to compute [read_lib_opens] for the visited lib and union the result into the BFS frontier. Localised in the BFS rather than the initial-frontier computation so the reachability rule reads as: a module is reachable iff the consumer references it, or some reached module's ocamldep names it, or some reached module's owning lib's effective flags open it. - [read_lib_opens] short-circuits to empty for non-local libs; their [src_dir] is not a build path, and env stanzas cannot inject flags into already-compiled artifacts. For local libs we evaluate [Ocaml_flags_db.ocaml_flags] which folds env-stanza flags under the library stanza's spec, so both injection paths are captured even when the library stanza is [:standard]. [Lib_info]: - Add [stanza_flags : Dune_lang.Ocaml_flags.Spec.t] field plus accessor. Local libs carry their stanza's [conf.buildable.flags]; external libs ([findlib], [dune_package]) carry [Spec.standard]. Regression tests on `main` that this patch makes pass under the per-module narrowing stack: - [cross-lib-open-flag-barrier.t] (added by #14584) pins the stanza-flag injection path. Fails on L4 and L5 head before this patch (Unbound constructor Green under [--sandbox=copy]); passes after. - [cross-lib-env-open-flag-barrier.t] (added by #14776) pins the env-stanza injection path: same shape, but [-open Prelude] is supplied by an [(env ...)] stanza while the library stanza uses [:standard]. Fails on the pre-#14517 [:standard]-short-circuit variant of this patch (same Unbound constructor error); passes after. Signed-off-by: Robin Bate Boerop <me@robinbb.com>
robinbb
added a commit
that referenced
this pull request
Jun 8, 2026
Closes a soundness gap in the per-module narrowing pipeline missed by the wrapped / ppx-runtime / virtual-impl recoveries: when a dependency library's effective flags inject [-open M], its source can reference [M]'s identifiers without naming [M], and ocamldep emits no token to drive the cross-library walker. Both the library stanza's own [(flags ...)] and any [(env ...)] stanza that contributes default flags to the dep lib's directory can inject [-open] entries. Reported by RyanJamesStewart on #14517 with this fixture: an unwrapped [middle] depending on unwrapped [prelude] with [(flags (:standard -open Prelude))], exposing [val pick : unit -> color] (where [color] resolves through the open to [Prelude.color]). The consumer pattern-matches the result against bare constructors. The compile genuinely needs [prelude.cmi] to resolve the constructors; the BFS over [ocamldep -modules] cannot reach [prelude] (no syntactic [Prelude] token on either side); the three existing recoveries do not catch it ([prelude] is not wrapped, not a ppx-runtime lib, not a virtual-impl). [Module_compilation.cross_lib_tight_set]: - Add [~mode] (the consumer's compile mode) so we can expand a dep lib's effective flags via [Ocaml_flags.get]. - Extend [read_entry_deps] to compute [read_lib_opens] for the visited lib and union the result into the BFS frontier. Localised in the BFS rather than the initial-frontier computation so the reachability rule reads as: a module is reachable iff the consumer references it, or some reached module's ocamldep names it, or some reached module's owning lib's effective flags open it. - [read_lib_opens] short-circuits to empty for non-local libs; their [src_dir] is not a build path, and env stanzas cannot inject flags into already-compiled artifacts. For local libs we evaluate [Ocaml_flags_db.ocaml_flags] which folds env-stanza flags under the library stanza's spec, so both injection paths are captured even when the library stanza is [:standard]. [Lib_info]: - Add [stanza_flags : Dune_lang.Ocaml_flags.Spec.t] field plus accessor. Local libs carry their stanza's [conf.buildable.flags]; external libs ([findlib], [dune_package]) carry [Spec.standard]. Regression tests on `main` that this patch makes pass under the per-module narrowing stack: - [cross-lib-open-flag-barrier.t] (added by #14584) pins the stanza-flag injection path. Fails on L4 and L5 head before this patch (Unbound constructor Green under [--sandbox=copy]); passes after. - [cross-lib-env-open-flag-barrier.t] (added by #14776) pins the env-stanza injection path: same shape, but [-open Prelude] is supplied by an [(env ...)] stanza while the library stanza uses [:standard]. Fails on the pre-#14517 [:standard]-short-circuit variant of this patch (same Unbound constructor error); passes after. Signed-off-by: Robin Bate Boerop <me@robinbb.com>
robinbb
added a commit
that referenced
this pull request
Jun 8, 2026
Closes a soundness gap in the per-module narrowing pipeline missed by the wrapped / ppx-runtime / virtual-impl recoveries: when a dependency library's effective flags inject [-open M], its source can reference [M]'s identifiers without naming [M], and ocamldep emits no token to drive the cross-library walker. Both the library stanza's own [(flags ...)] and any [(env ...)] stanza that contributes default flags to the dep lib's directory can inject [-open] entries. Reported by RyanJamesStewart on #14517 with this fixture: an unwrapped [middle] depending on unwrapped [prelude] with [(flags (:standard -open Prelude))], exposing [val pick : unit -> color] (where [color] resolves through the open to [Prelude.color]). The consumer pattern-matches the result against bare constructors. The compile genuinely needs [prelude.cmi] to resolve the constructors; the BFS over [ocamldep -modules] cannot reach [prelude] (no syntactic [Prelude] token on either side); the three existing recoveries do not catch it ([prelude] is not wrapped, not a ppx-runtime lib, not a virtual-impl). [Module_compilation.cross_lib_tight_set]: - Add [~mode] (the consumer's compile mode) so we can expand a dep lib's effective flags via [Ocaml_flags.get]. - Extend [read_entry_deps] to compute [read_lib_opens] for the visited lib and union the result into the BFS frontier. Localised in the BFS rather than the initial-frontier computation so the reachability rule reads as: a module is reachable iff the consumer references it, or some reached module's ocamldep names it, or some reached module's owning lib's effective flags open it. - [read_lib_opens] short-circuits to empty for non-local libs; their [src_dir] is not a build path, and env stanzas cannot inject flags into already-compiled artifacts. For local libs we evaluate [Ocaml_flags_db.ocaml_flags] which folds env-stanza flags under the library stanza's spec, so both injection paths are captured even when the library stanza is [:standard]. [Lib_info]: - Add [stanza_flags : Dune_lang.Ocaml_flags.Spec.t] field plus accessor. Local libs carry their stanza's [conf.buildable.flags]; external libs ([findlib], [dune_package]) carry [Spec.standard]. Regression tests on `main` that this patch makes pass under the per-module narrowing stack: - [cross-lib-open-flag-barrier.t] (added by #14584) pins the stanza-flag injection path. Fails on L4 and L5 head before this patch (Unbound constructor Green under [--sandbox=copy]); passes after. - [cross-lib-env-open-flag-barrier.t] (added by #14776) pins the env-stanza injection path: same shape, but [-open Prelude] is supplied by an [(env ...)] stanza while the library stanza uses [:standard]. Fails on the pre-#14517 [:standard]-short-circuit variant of this patch (same Unbound constructor error); passes after. Signed-off-by: Robin Bate Boerop <me@robinbb.com>
robinbb
added a commit
that referenced
this pull request
Jun 8, 2026
Closes a soundness gap in the per-module narrowing pipeline missed by the wrapped / ppx-runtime / virtual-impl recoveries: when a dependency library's effective flags inject [-open M], its source can reference [M]'s identifiers without naming [M], and ocamldep emits no token to drive the cross-library walker. Both the library stanza's own [(flags ...)] and any [(env ...)] stanza that contributes default flags to the dep lib's directory can inject [-open] entries. Reported by RyanJamesStewart on #14517 with this fixture: an unwrapped [middle] depending on unwrapped [prelude] with [(flags (:standard -open Prelude))], exposing [val pick : unit -> color] (where [color] resolves through the open to [Prelude.color]). The consumer pattern-matches the result against bare constructors. The compile genuinely needs [prelude.cmi] to resolve the constructors; the BFS over [ocamldep -modules] cannot reach [prelude] (no syntactic [Prelude] token on either side); the three existing recoveries do not catch it ([prelude] is not wrapped, not a ppx-runtime lib, not a virtual-impl). [Module_compilation.cross_lib_tight_set]: - Add [~mode] (the consumer's compile mode) so we can expand a dep lib's effective flags via [Ocaml_flags.get]. - Extend [read_entry_deps] to compute [read_lib_opens] for the visited lib and union the result into the BFS frontier. Localised in the BFS rather than the initial-frontier computation so the reachability rule reads as: a module is reachable iff the consumer references it, or some reached module's ocamldep names it, or some reached module's owning lib's effective flags open it. - [read_lib_opens] short-circuits to empty for non-local libs; their [src_dir] is not a build path, and env stanzas cannot inject flags into already-compiled artifacts. For local libs we evaluate [Ocaml_flags_db.ocaml_flags] which folds env-stanza flags under the library stanza's spec, so both injection paths are captured even when the library stanza is [:standard]. [Lib_info]: - Add [stanza_flags : Dune_lang.Ocaml_flags.Spec.t] field plus accessor. Local libs carry their stanza's [conf.buildable.flags]; external libs ([findlib], [dune_package]) carry [Spec.standard]. Regression tests on `main` that this patch makes pass under the per-module narrowing stack: - [cross-lib-open-flag-barrier.t] (added by #14584) pins the stanza-flag injection path. Fails on L4 and L5 head before this patch (Unbound constructor Green under [--sandbox=copy]); passes after. - [cross-lib-env-open-flag-barrier.t] (added by #14776) pins the env-stanza injection path: same shape, but [-open Prelude] is supplied by an [(env ...)] stanza while the library stanza uses [:standard]. Fails on the pre-#14517 [:standard]-short-circuit variant of this patch (same Unbound constructor error); passes after. Signed-off-by: Robin Bate Boerop <me@robinbb.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fifth fixture in the cross-lib
.cmiregression baseline family started by #14584. Pins consumer-compile.cmidep behaviour when-open Mis injected by an(env ...)stanza rather than the intermediate library's own(flags ...)field. The intermediate's library stanza uses:standard. Asserts build success under--sandbox=copy.Pairs with the stanza-flag variant
cross-lib-open-flag-barrier.talready onmainfrom #14584, covering the second of the two-openinjection paths.The forthcoming per-module narrowing work (#14492) will validate that this construct continues to work soundly under tighter per-module dep tracking.