Finding a module implicit dependencies

What’s the most reliable way of finding a module implicit dependencies?

In the yesterday release of Logtalk 3.30.0 I’m using the following internal predicate in the SWI-Prolog adapter file:

% '$lgt_find_module_predicate'(+atom, -atom, @callable)
% succeeds when Module:Predicate is visible in module Current

'$lgt_find_visible_module_predicate'(_Current, Module, Predicate) :-
	predicate_property(Predicate, implementation_module(Module)).

This predicate is used when compiling Prolog modules as Logtalk objects and the linter takes advantage of it to advise on missing use_module/2 directives. Extensive testing suggests that the reported missing directives are accurate but I wonder if the above definition should take the source module into account or if there are any gotchas that I’m missing.

What does that mean? I don’t really recall the history behind implementation_module. It seems to say if we need this defined it will come from the indicated module'. Typically I useimported_from`, but this actually loads the dependency as a side-effect.

Simple example:

:- module(foo, [bar/2]).

bar(X, L) :-
    member(X, L).

The foo module have an implicit dependency on the lists module, which provides the member/2 predicate.

I chosen the implementation_module/1 property over the imported_from/1 property due to the latter side-effect (not that the side-effect itself is expected to be problematic as the main purpose of compiling modules as objects is to help in porting code).

It turns out that the implementation_module/1 property exists for any predicate, including non-existing ones. E.g.

?- predicate_property(just_invented(_,_), implementation_module(Module)).

Module = user.

This sounds like a bug and it makes the property useless in my use case. Thus, I’m switching to the imported_from/1 property.

From the docs “True when Module is the module in which Head is or will be defined” It is a rather tricky property that is needed for various tabling operations to get the right (qualified) call variant. It behaves as is intentionally for undefined predicates. You can of course first check that the predicate is defined.

I did noticed that statement on the documentation before posting. But I also assumed that “will be defined” implied that a declaration for the predicate (e.g. multifile, dynamic, or tabled) must exist beforehand.