Skip to content

Commit cb122ec

Browse files
authored
test: regression guard for include-flag soundness through pre-pp entry (#14435)
## Summary Adds a cross-library cram test: a consumer reaches a transitive type only through a *preprocessed* library's `.mli`, where the type is never syntactically named in the consumer's source. Three libraries: - `other_dep` — unwrapped, exposes `type t = { x : int; y : string }`. - `pp_dep` — unwrapped + preprocessed, depends on `other_dep`, exposes `val make_thing : unit -> Other.t`. - `consumer` — depends on `pp_dep`, accesses `(D.make_thing ()).x`. `consumer/c.ml` never mentions `Other`, but the field access forces the type checker to load `other.cmi`. The test asserts `dune build @check` succeeds. Sibling to #14400's `cross-lib-walk-pre-pp-source.t`. That test exercises behaviour at a preprocessed entry directly; this one exercises the *transitive* leg — where the preprocessed entry's `.mli` re-exports a type from a deeper library.
2 parents 9e65007 + b987673 commit cb122ec

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
Regression test for cross-library dependency tracking through a
2+
preprocessed library's interface.
3+
4+
Three libraries, all unwrapped. [other_dep] defines a record
5+
type [Other.t]. [pp_dep] is preprocessed, depends on
6+
[other_dep], and re-exports [Other.t] in its [.mli]. [consumer]
7+
depends only on [pp_dep] and accesses a field of a value of
8+
type [Other.t] without ever naming [Other].
9+
10+
The consumer's compile must find [other.cmi] on its include
11+
path; otherwise the field access fails with "Unbound record
12+
field x".
13+
14+
Dune normally discovers cross-library type leaks by running
15+
ocamldep on each module's source. For preprocessed modules,
16+
ocamldep can fail on the pre-preprocessing source, so dune
17+
skips it — see neighbour test
18+
[cross-lib-walk-pre-pp-source.t]. As a result, dune cannot
19+
observe the implicit dependency from [consumer] to [other_dep]
20+
through [pp_dep]'s interface. The consumer's include flags
21+
must therefore conservatively retain [other_dep]'s [-I]/[-H]
22+
even though dune sees no direct link.
23+
24+
$ cat > dune-project <<EOF
25+
> (lang dune 3.0)
26+
> EOF
27+
28+
[other_dep]:
29+
30+
$ mkdir other_dep
31+
$ cat > other_dep/dune <<EOF
32+
> (library (name other_dep) (wrapped false))
33+
> EOF
34+
$ cat > other_dep/other.ml <<EOF
35+
> type t = { x : int; y : string }
36+
> let make x y = { x; y }
37+
> EOF
38+
$ cat > other_dep/other.mli <<EOF
39+
> type t = { x : int; y : string }
40+
> val make : int -> string -> t
41+
> EOF
42+
43+
[pp_dep]:
44+
45+
$ mkdir pp_dep
46+
$ cat > pp_dep/dune <<EOF
47+
> (library
48+
> (name pp_dep)
49+
> (wrapped false)
50+
> (libraries other_dep)
51+
> (preprocess (action (run cat %{input-file}))))
52+
> EOF
53+
$ cat > pp_dep/d.ml <<EOF
54+
> let make_thing () = Other.make 1 "hi"
55+
> EOF
56+
$ cat > pp_dep/d.mli <<EOF
57+
> val make_thing : unit -> Other.t
58+
> EOF
59+
60+
[consumer] accesses [.x] on a [D.make_thing ()] value, forcing
61+
the type checker to load [other.cmi] even though [Other] is
62+
never named in [c.ml]:
63+
64+
$ mkdir consumer
65+
$ cat > consumer/dune <<EOF
66+
> (library (name consumer) (wrapped false) (libraries pp_dep))
67+
> EOF
68+
$ cat > consumer/c.ml <<EOF
69+
> let v = D.make_thing ()
70+
> let _ = v.x
71+
> EOF
72+
73+
The build must succeed:
74+
75+
$ dune build @check

0 commit comments

Comments
 (0)