Skip to content

Commit

Permalink
Fix #2725 by calling load_context/1 in the unspecified branch of stri…
Browse files Browse the repository at this point in the history
…p_module/3

This fixes #2725, by making it so that `strip_module(Pred, M, P), call(M:P)`
doesn't throw an `instanciation_error` when `Pred` isn't in the form `module:predicate`.

Now, `strip_module(hello, M, P)` will call `load_context(M)`, which unifies `M`
with the topmost module (or `user`).

Two new test cases are added: issue2725.pl, which tests the minimal case id(X) --> X.
and the strip_module(P, M, _), call(M:P) scenario, and module_resolution,
which tests the behavior of strip_module in a few scenarios.
  • Loading branch information
adri326 committed Jan 7, 2025
1 parent d57f871 commit 1ad88fb
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 22 deletions.
15 changes: 3 additions & 12 deletions src/lib/builtins.pl
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,7 @@
% Asserts (inserts) a new clause (rule or fact) into the current module.
% The clause will be inserted at the beginning of the module.
asserta(Clause0) :-
loader:strip_subst_module(Clause0, user, Module, Clause),
loader:strip_module(Clause0, Module, Clause),
asserta_(Module, Clause).

asserta_(Module, (Head :- Body)) :-
Expand All @@ -1191,7 +1191,7 @@
% Asserts (inserts) a new clause (rule or fact) into the current module.
% The clase will be inserted at the end of the module.
assertz(Clause0) :-
loader:strip_subst_module(Clause0, user, Module, Clause),
loader:strip_module(Clause0, Module, Clause),
assertz_(Module, Clause).

assertz_(Module, (Head :- Body)) :-
Expand All @@ -1211,15 +1211,9 @@
loader:strip_module(Clause0, Module, Clause),
( Clause \= (_ :- _) ->
loader:strip_module(Clause, Module, Head),
( var(Module) -> Module = user
; true
),
Body = true,
retract_module_clause(Head, Body, Module)
; Clause = (Head :- Body) ->
( var(Module) -> Module = user
; true
),
retract_module_clause(Head, Body, Module)
).

Expand Down Expand Up @@ -1374,10 +1368,7 @@
'$get_db_refs'(_, _, _, PIs),
lists:member(Pred, PIs)
; loader:strip_module(Pred, Module, UnqualifiedPred),
( var(Module),
\+ functor(Pred, (:), 2)
; atom(Module)
),
atom(Module),
UnqualifiedPred = Name/Arity ->
( ( nonvar(Name), \+ atom(Name)
; nonvar(Arity), \+ integer(Arity)
Expand Down
5 changes: 3 additions & 2 deletions src/loader.pl
Original file line number Diff line number Diff line change
Expand Up @@ -695,10 +695,11 @@
( MQ = specified(M) ->
true
; MQ = unspecified,
true
% TODO: Switch to load_context(M) once PR #2756 gets merged.
load_context(M1)
M = M1
).


:- non_counted_backtracking strip_subst_module/4.

strip_subst_module(Goal, M1, M2, G) :-
Expand Down
23 changes: 15 additions & 8 deletions src/machine/system_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ use super::libraries;
use super::preprocessor::to_op_decl;
use super::preprocessor::to_op_decl_spec;

/// Represents the presence (or absence) of a `module:` prefix to predicates.
///
/// On the prolog side, `strip_module(X, Y, Z)` takes care of splitting the `X = module:predicate`
/// pair into `Y = module` and `Z = predicate`. If no module prefix is present, then `Y` is left unset.
///
/// On the rust side, [`MachineState::strip_module`] splits a given [`HeapCellValue`] into
/// a pair of [`ModuleQuantification`] and `HeapCellValue`.
#[derive(Debug)]
pub(crate) enum ModuleQuantification {
Specified(HeapCellValue),
Expand Down Expand Up @@ -1141,9 +1148,7 @@ impl MachineState {
if name == atom!(":") && arity == 2 {
let module_loc = self.heap[s+1];

module_quantification = ModuleQuantification::Specified(
module_loc,
);
module_quantification = ModuleQuantification::Specified(module_loc);

qualified_goal = self.heap[s+2];
} else {
Expand Down Expand Up @@ -1328,11 +1333,13 @@ impl Machine {
}
)
}
None => Ok(if let Some(load_context) = self.load_contexts.last() {
load_context.module
} else {
atom!("user")
}),
None => {
if let Some(load_context) = self.load_contexts.last() {
Ok(load_context.module)
} else {
Ok(atom!("user"))
}
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/tests/module_resolution.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
:- module(module_resolution, [get_module/2]).

get_module(P, M) :- strip_module(P, M, _).

:- initialization((strip_module(hello, M, _), write(M), write('\n'))).
:- initialization((loader:strip_module(hello, M, _), write(M), write('\n'))).
:- initialization((get_module(hello, M), write(M), write('\n'))).
:- initialization((module_resolution:get_module(hello, M), write(M), write('\n'))).
35 changes: 35 additions & 0 deletions tests-pl/issue2725.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
:- module(issue2725, []).
:- use_module(library(dcgs)).

% Tests that the id/3 dcg can be called.
% library(dcgs) currently expands it to id(X, Y, Z) :- phrase(X, Y, Z).
id(X) --> X.
call_id :-
id("Hello", X, []),
X = "Hello".
:- initialization(call_id).

test_default_strip_module :-
strip_module(hello, M, P),
nonvar(M),
M = issue2725,
nonvar(P),
P = hello,
strip_module(hello, issue2725, _),
strip_module(hello, M, P).
:- initialization(test_default_strip_module).

% Tests that strip_module followed by call works with or without the module: prefix.
strip_module_call(Pred) :-
loader:strip_module(Pred, M, Pred0),
call(M:Pred0).

my_true.

test_strip_module_call :-
strip_module_call(my_true),
strip_module_call(issue2725:my_true).
:- initialization(test_strip_module_call).

% :- initialization(loader:prolog_load_context(module, M), write(M), write('\n')).
% :- initialization(loader:load_context(user)).
Empty file.
6 changes: 6 additions & 0 deletions tests/scryer/cli/src_tests/module_resolution.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module_resolution
module_resolution
module_resolution
module_resolution
user
user
10 changes: 10 additions & 0 deletions tests/scryer/cli/src_tests/module_resolution.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
args = [
"-f",
"--no-add-history",
"src/tests/module_resolution.pl",
"-f",
"-g", "use_module(library(module_resolution))",
"-g", "get_module(some_predicate, M), write(M), write('\\n')",
"-g", "module_resolution:get_module(some_predicate, M), write(M), write('\\n')",
"-g", "halt"
]
6 changes: 6 additions & 0 deletions tests/scryer/issues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,9 @@ fn issue2588_load_html() {
fn call_qualification() {
load_module_test("tests-pl/issue2361-call-qualified.pl", "");
}

#[test]
#[cfg_attr(miri, ignore = "it takes too long to run")]
fn issue2725_dcg_without_module() {
load_module_test("tests-pl/issue2725.pl", "");
}

0 comments on commit 1ad88fb

Please sign in to comment.