Skip to content

include functor support for OxCaml - #1452

Open
Leonidas-from-XIV wants to merge 9 commits into
ocaml:masterfrom
Leonidas-from-XIV:include-functor
Open

include functor support for OxCaml#1452
Leonidas-from-XIV wants to merge 9 commits into
ocaml:masterfrom
Leonidas-from-XIV:include-functor

Conversation

@Leonidas-from-XIV

Copy link
Copy Markdown
Member

This PR is a follow up to #1368 and adds support for include functor where it now displays that the items were included via the functor.

@Leonidas-from-XIV
Leonidas-from-XIV force-pushed the include-functor branch 3 times, most recently from 18499bf to ca9079a Compare July 1, 2026 08:28
@Leonidas-from-XIV
Leonidas-from-XIV marked this pull request as ready for review July 1, 2026 12:22

@art-w art-w left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks a lot, this looks great! I only have minor feedback but nothing blocking :)

Comment thread src/xref2/tools.ml
>>=
match allow_functor with
| true -> assert_functor
| false -> assert_not_functor)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we avoid the ~allow_functor is we match on | Signature s | Functor (_ Signature s) -> s or would that hide some logic errors? (since both assert_.._functor fail with assert false anyway, I assume the error reporting isn't a concern)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes - at the very least we ought to rename the argument. I'd quite like to double check the semantics here though, as we've got at least one instance of this bit going wrong: #960

@Leonidas-from-XIV Leonidas-from-XIV Jul 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Truth be told I am a bit confused about the purpose of assert_not_functor:

  1. Why do we need to make sure the module expression is not a functor?
  2. The function doesn't actually just assert (I thought I could just remove it to allow for functors, but then the types don't match), it also unpacks the signature.

Hence to answer @art-w's question, I don't know - it would change semantics as then it would always allow functors; in which case we could rewrite it into something like

and assert_signature : type err. expansion -> (Component.Signature.t, err) result
    = function
  | Signature sg -> Ok sg
  | Functor (_, mty) -> (
      match mty with Signature sg -> Ok sg | _ -> assert false)
  | _ -> assert false

Comment thread src/document/generator.ml Outdated
(** This is a Module where the type is named and then included. *)
module type Make = (_ : sig type t end) -> sig type included end
type t
include functor Make

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Out of curiosity, can we test the behavior with (** @inline *)?

@jonludlam

Copy link
Copy Markdown
Member

I think there's a chunk of logic missing from this PR. At the moment, all it does AFAICT is get the expansion of the functor and splice it into the expansion of the include. While this works for simple examples, it's not sufficient for more involved cases.

As a simple example, consider this:

module F ( I : sig type t end ) = struct
  type myt = I.t
end

module M = struct
  type t = float
  include functor F
end

Running this through ocamlc -c -i gives what we ought to end up with (roughly):

module F : functor (I : sig type t end) -> sig type myt = I.t end
module M : sig type t = float type myt = float end

If we just simply get the signature of the functor body and splice it in, we end up something more like:

...
module M : sig
  type t = float
  type myt = I.t
end

and obviously that I.t need something done to it. If you run it through odoc as of this branch, it chokes on exactly this:

Exception Failure("Not_found: I/3") handling type_expr: resolved(I/3.t)
backtrace:
Raised at Stdlib.failwith in file "stdlib.ml" (inlined), line 39, characters 17-33
...

I suspect the most straightforward implementation of this would be to do exactly what the docs suggest it does internally. Treat it more like:

module M = struct
  module __DUMMY__ = struct
    type t = float
  end

  include __DUMMY__
  include F(__DUMMY__)
end

We can ensure that the dummy module is hidden, meaning that the include will just inline the contents,
so it should end up rendering in a sensible way. I'm not sure whether it would be best to do this as
a preprocessing step or whether we might be able to rewrite the loaders with an accumulator so we can
do this sort of mangling on loading of the cmt/cmtis.

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