34
   35:- module(metaprops,
   36          [(declaration)/1,
   37           (declaration)/2,
   38           (global)/1,
   39           (global)/2,
   40           (type)/1,
   41           (type)/2,
   42           checkprop_goal/1,
   43           compat/1,
   44           compat/2,
   45           get_checkprop/1,
   46           ncompat/1,
   47           with_cv_module/2,
   48           cv_module/1,
   49           instan/1,
   50           instan/2,
   51           last_prop_failure/1
   52          ]).   53
   54:- use_module(library(apply)).   55:- use_module(library(occurs)).   56:- use_module(library(ordsets)).   57:- use_module(library(assertions)).   58:- use_module(library(resolve_calln)).   59:- use_module(library(context_values)).   60:- use_module(library(extend_args)).   61:- use_module(library(qualify_meta_goal)).   62:- use_module(library(safe_prolog_cut_to)).   63:- use_module(library(gcb)).   64:- use_module(library(list_sequence)).   65:- use_module(library(substitute)).   66:- use_module(library(clambda)).   67:- use_module(library(terms_share)).   68:- init_expansors.   69
   70:- true prop (type)/1 + (declaration(check), global(prop)) # "Defines a type.".
   71
   72type(Goal) :- call(Goal).
   73
   74:- true prop (global)/1 + (global(prop), declaration)
   75# "A property that is global, i.e., can appear after the + in the assertion.
   76and as meta predicates, meta_predicate F(0)".
   77
   78global(Goal) :- call(Goal).
   79
   80:- type assrt_type/1, assrt_status/1.
   81
   82:- global global(Prop, Goal) : (assrt_type(Prop), callable(Goal))
   83# "Like global/1, but allows to specify the default assertion type".
   84
   85global(_, Goal) :- call(Goal).
   86
   87:- true prop (declaration)/1 + (global(prop), declaration)
   88# "A property that is a declaration, i.e., an operator is added as op(1125, fx, F). Implies global/1".
   89
   90declaration(Goal) :- call(Goal).
   91
   92:- true prop declaration(Status, Goal) : (assrt_status(Status), callable(Goal)) + global(prop)
   93# "Like declaration/1, but allows to specify the default assertion status".
   94
   95declaration(_, Goal) :- call(Goal).
   96
   97:- true prop cv_module/1.
   98
   99cv_module(CM) :-
  100    current_context_value(current_module, CM),
  101    !.
  102cv_module(user).
  103
  104:- true prop type(T, A)
  105# "~w is internally of type ~w, a predicate of arity 1 defined as a type/1."-[A, T].
  106
  107:- meta_predicate
  108        type(1, ?),
  109        type(1, ?, -).  110
  111type(M:T, A) :-
  112    type(M:T, A, H),
  113    M:H.
  114
  115type(M:T, A, H) :-
  116    extend_args(T, [Arg], H),
  117    ( cv_module(C),
  118      predicate_property(M:H, meta_predicate(Spec)),
  119      functor(H, _, N),
  120      arg(N, Spec, Meta),
  121      '$expand':meta_arg(Meta)
  122    ->Arg = C:A
  123    ; Arg = A
  124    ).
  125
  126:- multifile
  127    unfold_calls:unfold_call_hook/4.  128
  129unfold_calls:unfold_call_hook(type(T, A), metaprops, M, M:call(T, A)).
  130
  131:- thread_local
  132        '$last_prop_failure'/2.  133
  134generalize_term(STerm, Term, _) :-
  135    \+ terms_share(STerm, Term).
  136
  137neg(nonvar(A), var(A)).
  138neg(A==B,  A \== B).
  139neg(A =B,  A  \= B).
  140neg(A=:=B, A =\= B).
  141neg(A,     \+A).
  142
  143current_prop_failure((SG :- Body)) :-
  144    '$last_prop_failure'(Term, SubU),
  145    sort(SubU, Sub),
  146    greatest_common_binding(Term, Sub, ST, SSub, [[]], Unifier, []),
  147    substitute(generalize_term(SSub), ST, SG),
  148    maplist(\ A^NA^once(neg(A, NA)), SSub, NSub),
  149    foldl(simplify_unifier(SG-SSub), Unifier, LitL, NSub),
  150    LitL \= [],
  151    list_sequence(LitL, Body).
  152
  153simplify_unifier(Term, A=B) -->
  154    ( {occurrences_of_var(A, Term, 0 )}
  155    ->{A=B}
  156    ; [A=B]
  157    ).
  158
  159last_prop_failure(L) :-
  160    findall(E, once(current_prop_failure(E)), L),
  161    retractall('$last_prop_failure'(_, _)).
  162
  163asserta_prop_failure(T, S) :-
  164    once(( retract('$last_prop_failure'(T, L))
  165         ; L = []
  166         )),
  167    asserta('$last_prop_failure'(T, [S|L])).
  168
  169cleanup_prop_failure(T, S) :-
  170    retractall('$last_prop_failure'(_, _)),
  171    asserta('$last_prop_failure'(T, S)).
  172
  173:- meta_predicate with_cv_module(0, +).  174with_cv_module(Goal, CM) :-
  175    with_context_value(Goal, current_module, CM).
  176
  177valid_compat(compat).
  178valid_compat(ncompat).
  179
  180:- meta_predicate checkprop_goal(0 ).  181checkprop_goal(Goal) :-
  182    ( current_context_value(checkprop, CheckProp)
  183    ->valid_compat(CheckProp)
  184    ; Goal
  185    ).
  186
  187/*
  188:- meta_predicate compat1(1, ?).
  189compat1(M:Pred1, Arg) :-
  190    term_variables(Pred1, PVars),
  191    term_variables(Arg,   AVars),
  192    ord_subtract(AVars, PVars, Shared),
  193    \+ \+ compat(M:call(Pred1, Arg), Shared).
  194*/
  195
  196:- true prop compat(Prop)
  197# "Uses ~w as a compatibility property."-[Prop].
  198
  199get_checkprop(Value) :- current_context_value(checkprop, Value).
  200
  201:- meta_predicate compat(0).  202
  203compat(M:Goal) :-
  204    ( functor(Goal, _, A),
  205      arg(A, Goal, Last)
  206    ->VarL = [Last]
  207    ; VarL = []
  208    ),
  209    \+ \+ compat(M:Goal, VarL).
  210
  211:- true prop ncompat/1
  212   # "Naive version of compat, faster but could fall into infinite loops".
  213
  214:- meta_predicate ncompat(0).  215
  216ncompat(MGoal) :-
  217    \+ \+ with_context_value(MGoal, checkprop, ncompat).
  218
  219:- meta_predicate compat(0, +).  220
  221compat(MGoal, VarL) :-
  222    (   current_context_value(checkprop, ncompat)
  223    ->  MGoal
  224    ;   (   current_context_value(checkprop, compat)
  225        ->  icompat(MGoal, VarL)
  226        ;   with_context_value(icompat(MGoal, VarL), checkprop, compat)
  227        )
  228    ).
  229
  230icompat(M:Goal, VarL) :-
  231    copy_term_nat(Goal-VarL, Term-VarTL),   232    sort(VarTL, VS),
  233    cleanup_prop_failure(Term, []),
  234    prolog_current_choice(CP),
  235    compat(Term, data(VS, Term, CP), M).
  236
  239compat(Var, _, _) :- var(Var), !.
  240compat(M:Goal, D, _) :-
  241    !,
  242    compat(Goal, D, M).
  243compat(G, _, M) :-
  244    ground(G),
  245    !,
  246    M:G.   247compat(nonvar(_), _, _) :- !.
  248compat(A, D, M) :-
  249    do_resolve_calln(A, B),
  250    !,
  251    compat(B, D, M).
  252compat((A, B), D, M) :-
  253    !,
  254    compat(A, D, M),
  255    compat(B, D, M).
  256compat(type(T, A), D, M) :-
  257    !,
  258    strip_module(M:T, C, H),
  259    type(C:H, A, G),
  260    compat(G, D, C).
  261compat(compat(A), D1, M) :-
  262    !,
  263    strip_module(M:A, _, B),
  264    ( functor(B, _, N),
  265      arg(N, B, Last)
  266    ->D1 = data(VS, Term, CP),
  267      D  = data([Last|VS], Term, CP)
  268    ; D = D1
  269    ),
  270    \+ \+ compat(A, D, M).
  271    
  272compat((A->B; C), D, M) :-
  273    !,
  274    (   call(M:A)
  275    ->  compat(B, D, M)
  276    ;   compat(C, D, M)
  277    ),
  278    !.
  279compat((A*->B; C), D, M) :-
  280    !,
  281    (   call(M:A)
  282    *-> compat(B, D, M)
  283    ;   compat(C, D, M)
  284    ),
  285    !.
  286compat(\+ G, _, M) :-
  287    !,
  288    \+ M:G.
  289compat((A->B), D, M) :-
  290    !,
  291    ( call(M:A)
  292    ->compat(B, D, M)
  293    ).
  294compat((A;B), D, M) :-
  295    !,
  296    ( compat(A, D, M)
  297    ; compat(B, D, M)
  298    ).
  299compat(!, data(_, _, CP), _) :-
  300    !,
  301    cut_from(CP).
  302compat(checkprop_goal(_), _, _) :- !.
  303compat(with_cv_module(A, C), D, M) :-
  304    !,
  305    with_cv_module(compat(A, D, M), C).
  306compat(mod_qual(T, A), D, M) :-
  307    nonvar(A),
  308    !,
  309    strip_module(M:A, C, V),
  310    with_cv_module(compat(type(T, V), D, M), C).
  311compat(Term, D, M) :-
  312    D = data(_, T, _),
  313    asserta_prop_failure(T, Term),
  314    compat_1(Term, D, M),
  315    cleanup_prop_failure(T, []).
  316
  317compat_1(@(A, C), D, M) :-
  318    !,
  319    compat_1(A, @(M:A, C), D, C, M).
  320compat_1(A, D, M) :-
  321    compat_1(A, M:A, D, M, M).
  322
  323compat_1(A, G, D, C, M) :-
  324    D = data(V, T, _),
  325    compat_1(V, T, A, G, C, M).
  326
  327compat_1(V, T, A, G, C, M) :-
  328    term_variables(A, AU), sort(AU, AVars),
  329    term_variables(V, TU), sort(TU, TVars),
  330    ord_intersect(AVars, TVars, Shared),
  331    ( functor(A, _, N),
  332      arg(N, A, Last),
  333      var(Last),
  334      ord_intersect([Last], Shared, [_])
  335    ->true
  336    ; Shared = []
  337    ->once(G)
  338    ; is_type(A, M)
  339    ->functor(A, _, N),
  340      arg(N, A, Last),
  341      term_variables(Last-V, V2), sort(V2, V3),
  342      catch(compat_body(A, C, M, V3, T), _, G)
  343    ; \+ ( \+ compat_safe(A, M),
  344           \+ ground(A),
  345           \+ aux_pred(A),
  346           \+ is_prop(A, M),
  347           print_message(warning, format("While checking compat, direct execution of predicate could cause infinite loops: ~q", [G-T])),
  348           fail
  349         ),
  350      once(G)
  351    ).
  352
  353aux_pred(P) :-
  354    functor(P, F, _),
  355    atom_concat('__aux_', _, F).
  356
  357compat_safe(_ =.. _, _).
  358compat_safe(_ = _, _).
  359compat_safe(_ is _, _).
  360compat_safe(call_cm(_, _, _), _).
  361compat_safe(context_module(_), _).
  362compat_safe(strip_module(_, _, _), _).
  363compat_safe(curr_arithmetic_function(_), _).
  364compat_safe(term_variables(_, _), _).
  365compat_safe(current_predicate(_), _).
  366compat_safe(functor(_, _, _), _).
  367compat_safe(freeze(_, _), _).
  368compat_safe(goal_2(_, _), _).
  369compat_safe(prop_asr(_, _, _, _), _).
  370compat_safe(static_strip_module(_, _, _, _), _).
  371compat_safe(freeze_cohesive_module_rt(_, _, _, _, _, _), _).
  372compat_safe(length(A, B), _) :- once((is_list(A) ; ground(B))).
  373
  374is_prop(Head, M) :-
  375    prop_asr(Head, M, Stat, prop, _, _, _, _),
  376    memberchk(Stat, [check, true]).
  377
  378is_type(Head, M) :-
  379    once(( prop_asr(Head, M, Stat, prop, _, _, _, Asr),
  380           memberchk(Stat, [check, true]),
  381           once(prop_asr(glob, type(_), _, Asr))
  382         )).
  383
  384compat_body(M, H, C, V, T, CP) :-
  385    functor(H, F, A),
  386    functor(P, F, A),
  387    (     388        clause(M:H, Body, Ref),
  389        clause(_:G,    _, Ref),
  390        \+ P=@=G
  391    *-> true
  392    ;   clause(M:H, Body, Ref)
  393    ),
  394    clause_property(Ref, module(S)),   395    ( predicate_property(M:H, transparent),
  396      \+ ( predicate_property(M:H, meta_predicate(Meta)),
  397           arg(_, Meta, Spec),
  398           '$expand':meta_arg(Spec)
  399         )
  400    ->CM = C
  401    ; CM = S
  402    ),
  403    compat(Body, data(V, T, CP), CM).
  404
  405compat_body(G1, C, M, V, T) :-
  406    qualify_meta_goal(G1, M, C, G),
  407    prolog_current_choice(CP),
  408    compat_body(M, G, C, V, T, CP).
  409
  410cut_from(CP) :- catch(safe_prolog_cut_to(CP), _, true).
  411
  412freeze_fail(CP, Term, V, N) :-
  413    freeze(V, ( prolog_cut_to(CP),
  414                cleanup_prop_failure(Term, [nonvar(N), N==V]),
  415                fail
  416              )).
  417
  418:- global instan(Prop)
  419# "Uses Prop as an instantiation property. Verifies that execution of
  420   ~w does not produce bindings for the argument variables."-[Prop].
  421
  422:- meta_predicate instan(0).  423
  424instan(Goal) :-
  425    term_variables(Goal, VS),
  426    instan(Goal, VS).
  427
  428:- meta_predicate instan(0, +).  429
  430instan(Goal, VS) :-
  431    prolog_current_choice(CP),
  432    \+ \+ ( copy_term_nat(Goal-VS, Term-VN),
  433            maplist(freeze_fail(CP, Term), VS, VN),
  434            with_context_value(Goal, checkprop, instan)
  435          )