Skip to content

test: env-stanza -open variant of cross-lib .cmi regression baseline#14776

Merged
Alizter merged 1 commit into
ocaml:mainfrom
robinbb:robinbb-test-cross-lib-env-open-flag-barrier
May 30, 2026
Merged

test: env-stanza -open variant of cross-lib .cmi regression baseline#14776
Alizter merged 1 commit into
ocaml:mainfrom
robinbb:robinbb-test-cross-lib-env-open-flag-barrier

Conversation

@robinbb

@robinbb robinbb commented May 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fifth fixture in the cross-lib .cmi regression baseline family started by #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.

Pairs with the stanza-flag variant cross-lib-open-flag-barrier.t already on main from #14584, covering the second of the two -open injection paths.

The forthcoming per-module narrowing work (#14492) will validate that this construct continues to work soundly under tighter per-module dep tracking.

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 robinbb self-assigned this May 29, 2026
@robinbb robinbb requested a review from Copilot May 29, 2026 06:42
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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.t exercising env-stanza-injected -open across a prelude → middle → consumer chain.

@robinbb robinbb requested a review from Alizter May 29, 2026 16:22
@robinbb

robinbb commented May 29, 2026

Copy link
Copy Markdown
Collaborator Author

@Alizter I'm factoring this test out of the L5 PR.

@robinbb robinbb marked this pull request as ready for review May 29, 2026 16:46
@Alizter Alizter merged commit 6d7951f into ocaml:main May 30, 2026
31 checks passed
@robinbb robinbb deleted the robinbb-test-cross-lib-env-open-flag-barrier branch June 1, 2026 03:23
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants