1/*****************************************************************************
    2 * This file is part of the Prolog Development Tool (PDT)
    3 * 
    4 * Author: Eva Stoewe, G´┐Żnter Kniesel, Jan Wielemaker, Andreas Becker
    5 * WWW: http://sewiki.iai.uni-bonn.de/research/pdt/start
    6 * Mail: pdt@lists.iai.uni-bonn.de
    7 * Copyright (C): 2013, CS Dept. III, University of Bonn
    8 * 
    9 * All rights reserved. This program is  made available under the terms
   10 * of the Eclipse Public License v1.0 which accompanies this distribution,
   11 * and is available at http://www.eclipse.org/legal/epl-v10.html
   12 * 
   13 ****************************************************************************/
   14
   15:- module(pdt_prolog_metainference,
   16	  [ infer_meta/2,		% :Head, -MetaSpec
   17	    infer_meta/3,		% :Head, -MetaSpec, -NewOrUpdated
   18	    inferred_meta/2		% :Head, ?MetaSpec
   19	  ]).   20
   21:- use_module(library(lists)).   22:- use_module(library(apply)).   23:- use_module(pdt_prolog_library(utils4modules_visibility)).   24:- use_module(pdt_prolog_library(lists)).   25:- use_module(pdt_meta_specification).   26
   27reset :-
   28	retractall(inferred_meta_pred(_, _, _)).
   29
   30run :-
   31	run(1).
   32
   33run(Run) :-
   34	statistics(cputime, CPU0),
   35	format('Starting meta inference iteration ~w~n', [Run]),
   36	findall(Module:MetaSpec, (
   37		declared_in_module(Module, Name, Arity, Module),
   38		functor(Head, Name, Arity),
   39		\+ predicate_property(Module:Head, built_in),
   40		\+ predicate_property(Module:Head, foreign),
   41		\+ predicate_property(Module:Head, number_of_clauses(0)),
   42		do_infer_meta(Module:Head, MetaSpec),
   43		(	inferred_meta_pred(Head, Module, ExistingMetaSpec)
   44		->	(	MetaSpec == ExistingMetaSpec
   45			->	fail
   46			;	retract(inferred_meta_pred(Head, Module, ExistingMetaSpec)),
   47				assertz(inferred_meta_pred(Head, Module, MetaSpec))
   48			)
   49		;	assertz(inferred_meta_pred(Head, Module, MetaSpec))
   50		)
   51	), NewOrModifiedMetaSpecs),
   52	forall(member(Module:MetaSpec, NewOrModifiedMetaSpecs), format('% :- meta_predicate ~w:~w.~n', [Module, MetaSpec])),
   53	statistics(cputime, CPU1),
   54	CPU is CPU1 - CPU0,
   55	length(NewOrModifiedMetaSpecs, NewOrModified),
   56	format('Finished meta inference iteration ~w: ~w meta predicates found or modified in ~f sec.~n', [Run, NewOrModified, CPU]),
   57	(	NewOrModified > 0
   58	->	NextRun is Run + 1,
   59		run(NextRun)
   60	;	true
   61	).
   62
   63:- meta_predicate
   64	inferred_meta(:, ?),
   65	infer_meta(:, -),
   66	infer_meta(:, -, -).   67
   68:- dynamic
   69	inferred_meta_pred/3.			% Head, Module, Meta

Infer meta-predicate properties

This module infers meta-predicate properties by inspecting the clauses of predicates that call other predicates. This is extremely useful for program analysis and refactoring because many programs `in the wild' have incomplete or incorrect meta-predicate information.

See also
- This library is used by prolog_walk_code/1 to improve the accuracy of this analysis.
To be done
- Re-introduce some alias-analysis
-
Not all missing meta-declarations are interesting. Notably, meta-predicates that are private and only pass meta-arguments on behalve of a public meta-predicates do not need a declaration. */
 inferred_meta(:Head, ?MetaSpec) is nondet
True when MetaSpec is an inferred meta-predicate specification for Head.
   92inferred_meta(M:Head, MetaSpec) :-
   93	inferred_meta_pred(Head, M, MetaSpec).
   94inferred_meta(M:Head, MetaSpec) :-
   95	predicate_property(M:Head, imported_from(From)),
   96	inferred_meta_pred(Head, From, MetaSpec).
 infer_meta(:Head, -MetaSpec) is semidet
True when MetaSpec is a meta-predicate specifier for the predicate Head. Derived meta-predicates are collected and made available through inferred_meta_predicate/2.
  105infer_meta(M:Head, MetaSpec) :-
  106	infer_meta(M:Head, MetaSpec, _).
  107
  108infer_meta(M:Head, MetaSpec, NewOrUpdated) :-
  109	predicate_property(M:Head, imported_from(From)), !,
  110	do_infer_meta(From:Head, MetaSpec),
  111	update_inferred_metaspec(Head, From, MetaSpec, NewOrUpdated).
  112infer_meta(M:Head, MetaSpec, NewOrUpdated) :-
  113	do_infer_meta(M:Head, MetaSpec),
  114	update_inferred_metaspec(Head, M, MetaSpec, NewOrUpdated).
  115
  116update_inferred_metaspec(Head, M, NewMetaSpec, NewOrUpdated) :-
  117	inferred_meta_pred(Head, M, OldMetaSpec),
  118	!,
  119	(	OldMetaSpec == NewMetaSpec
  120	->	NewOrUpdated = false
  121	;	retract(inferred_meta_pred(Head, M, OldMetaSpec)),
  122		assertz(inferred_meta_pred(Head, M, NewMetaSpec)),
  123		NewOrUpdated = true
  124	).
  125update_inferred_metaspec(Head, M, NewMetaSpec, true) :-
  126	assertz(inferred_meta_pred(Head, M, NewMetaSpec)).
  127
  128:- meta_predicate
  129	do_infer_meta(:, -).  130
  131do_infer_meta(Module:AHead, MetaSpec):-
  132	functor(AHead, Functor, Arity),
  133	functor(Head, Functor, Arity),	% Generalise the head
  134	findall(MetaTerm,
  135		meta_pred_args_in_clause(Module, Head, MetaTerm),
  136		MetaTerms),
  137	MetaTerms \== [],
  138	combine_meta_args(MetaTerms, MetaTerm),
  139	meta_terms_to_arg_specs_in_head(MetaTerm, MetaSpec).
 meta_pred_args_in_clause(+Module, +Head, -MetaTermHead) is nondet
  144meta_pred_args_in_clause(Module, Head, MetaTermHead) :-
  145	catch(clause(Module:Head, Body), _, fail),
  146	annotate_meta_vars_in_body(Body, Module),
  147	meta_term_head(Head, MetaTermHead).
 annotate_meta_vars_in_body(+Term, +Module) is det
Annotate variables in Term if they appear as meta-arguments.
To be done
- Aliasing. Previous code detected aliasing for
- We can make this nondet, exploring multiple aliasing paths in disjunctions.
  162:- discontiguous(annotate_meta_vars_in_body/2).  163annotate_meta_vars_in_body(A, _) :-
  164	atomic(A), !.
  165annotate_meta_vars_in_body(Var, _) :-
  166	var(Var), !,
  167	annotate(0, Var).
  168annotate_meta_vars_in_body(Module:Term, _) :- !,
  169	(   atom(Module)
  170	->  annotate_meta_vars_in_body(Term, Module)
  171	;   var(Module)
  172	->  annotate(m, Module)
  173	;   true			% may continue if Term is a system
  174	).				% predicate?
  175annotate_meta_vars_in_body((TermA, TermB), Module) :- !,
  176	annotate_meta_vars_in_body(TermB, Module),
  177	annotate_meta_vars_in_body(TermA, Module).
  178annotate_meta_vars_in_body((TermA; TermB), Module) :- !,
  179	(	annotate_meta_vars_in_body(TermB, Module)
  180	;	annotate_meta_vars_in_body(TermA, Module)
  181	).
  182annotate_meta_vars_in_body((TermA->TermB), Module) :- !,
  183	annotate_meta_vars_in_body(TermB, Module),
  184	annotate_meta_vars_in_body(TermA, Module).
  185annotate_meta_vars_in_body((TermA*->TermB), Module) :- !,
  186	annotate_meta_vars_in_body(TermB, Module),
  187	annotate_meta_vars_in_body(TermA, Module).
  188annotate_meta_vars_in_body(A=B, _) :-
  189	!,
  190	unifiable(A, B, Unifiers),
  191	new_aliases(Unifiers).
 new_aliases(+Aliases) is det
Add the alias information to the variable annotations. If two variables are aliased their annotations are merged.
  199new_aliases([]).
  200new_aliases([A = B|Aliases]) :-
  201	(	var(B)
  202	->	merge_variable_annotations(A, B),
  203		new_alias(A, B),
  204		new_alias(B, A)
  205	;	new_alias(A, B)
  206	),
  207	new_aliases(Aliases).
 merge_variable_annotations(+V1, +V2) is det
Merge the annotations of the variables V1 and V2 (without aliases).
  213merge_variable_annotations(V1, V2) :-
  214	(	get_attr(V1, pmi, Meta1)
  215	->	(	get_attr(V2, pmi, Meta2)
  216		->	% V1 and V2 are both annotated
  217			get_aliased(Meta1, Aliased1),	             % remember their aliases
  218			get_aliased(Meta2, Aliased2),                
  219			merge_annotations(Meta1, Meta2, MetaMerged), % merge annotations (without aliases) into MetaMerged
  220			duplicate_term(MetaMerged, MetaMergedCopy),  % make a "real" copy of MetaMerged (necessary because of the use of setarg/3)
  221			set_aliased(MetaMerged, Aliased1),           % fill the remembered aliases into the merged term
  222			put_attr(V1, pmi, MetaMerged),
  223			set_aliased(MetaMergedCopy, Aliased2),
  224			put_attr(V2, pmi, MetaMergedCopy)
  225		;	% only V1 is annotated
  226			duplicate_term(Meta1, MetaCopy),             % V2 gets a copy of the annotatinos 
  227			set_aliased(MetaCopy, []),                   % of V1 (except for aliases)
  228			put_attr(V2, pmi, MetaCopy)
  229		)
  230	;	(	get_attr(V2, pmi, Meta2)
  231		->	% only V2 is annotated
  232			duplicate_term(Meta2, MetaCopy),             % V1 gets a copy of the annotatinos
  233			set_aliased(MetaCopy, []),                   % of V2(except for aliases)
  234			put_attr(V1, pmi, MetaCopy)
  235		;	% V1 and V2 are not annotated -> nothing to merge
  236			true
  237		)
  238	).
 new_alias(+A, +B) is det
Add the alias B to the annotations of the variable A. If B is a variable and not yet contained in the annotated aliases of A, B is also unified to all aliases of A. This is checked in check_for_unifiers/2.
  249new_alias(A, B) :-
  250	(	get_attr(A, pmi, Meta)
  251	->	get_aliased(Meta, Aliases),
  252		(	eq_member(B, Aliases)
  253		->	true
  254		;	annotate(aliased(B), A),
  255			check_for_unifiers(Aliases, B)
  256		)
  257	;	annotate(aliased(B), A)
  258	).
 check_for_unifiers(+Aliases, +Var) is det
Compute the unifiers of Var and each alias in Aliases. The resulting unifiers are aliases which are added to the variable annotations using new_aliases/1.
  266check_for_unifiers([], _).
  267check_for_unifiers([Alias|Aliases], Var) :-
  268	unifiable(Alias, Var, Unifiers),
  269	new_aliases(Unifiers),
  270	check_for_unifiers(Aliases, Var).
  271
  272annotate_meta_vars_in_body(functor(Term, Functor, Arity), _Module) :-
  273	!,
  274	(	var(Term),
  275		var_is_meta_called(Term, Annotation)       % Term is metacalled
  276	->	(	var(Functor)
  277		->	(	integer(Arity)
  278			->	annotate(has_arity(Arity, Annotation), Functor) % Arity is given
  279			;	annotate(functor(Annotation), Functor)          % Arity is unknown
  280			)
  281		;	true
  282		),
  283		(	var(Arity)
  284		->	annotate(arity(Annotation), Arity)
  285		;	true
  286		)
  287	;	true
  288	).
 var_is_meta_called(+Var, -MetaAnnotations) is semidet
True when Var is metacalled, i.e. Var has at least one corresponding annotation (0..9, ^, // or database). MetaAnnotations is bound to this annotation (in case of one) or to a list of these annotations.
  296var_is_meta_called(Var, MetaAnnotations) :-
  297	get_attr(Var, pmi, Meta),
  298	setof(Annotation,
  299		(	get_metacalled(Meta, Metacalled),
  300			member(Annotation, Metacalled)
  301		;	get_existential(Meta, ^),
  302			Annotation = ^
  303		),
  304	MetaAnnotations0),
  305	remove_list_if_possible(MetaAnnotations0, MetaAnnotations),
  306	!.
  307
  308annotate_meta_vars_in_body(atom_concat(A, B, C), _Module) :-
  309	!,
  310	(	var(C)
  311	->	(	var_is_functor_or_meta_called(C, Annotation)
  312		->	annotate_atom_concat(A, B, Annotation)
  313		;	true
  314		)
  315	;	true
  316	).
 var_is_functor_or_meta_called(+Var, -MetaAnnotations) is semidet
True when Var is metacalled or the functor of a metacalled term, i.e. Var has at least one corresponding annotation (0..9, ^, //, database, functor/1 or has_arity/2). MetaAnnotations is bound to this annotation (in case of one) or to a list of these annotations.
  325var_is_functor_or_meta_called(Var, MetaAnnotations) :-
  326	get_attr(Var, pmi, Meta),
  327	setof(Annotation,
  328		(	get_metacalled(Meta, Metacalled),
  329			member(Annotation, Metacalled)
  330		;	get_existential(Meta, ^),
  331			Annotation = ^
  332		;	get_components(Meta, Components),
  333			member(Annotation, Components),
  334			(	Annotation = functor(_)
  335			;	Annotation = has_arity(_, _)
  336			)
  337		),
  338	MetaAnnotations0),
  339	remove_list_if_possible(MetaAnnotations0, MetaAnnotations),
  340	!.
  341
  342annotate_atom_concat(A, B, Annotation) :-
  343	var(A), var(B), !,
  344	annotate(is_prefix(Annotation), A),
  345	annotate(is_suffix(Annotation), B).
  346annotate_atom_concat(A, B, Annotation) :-
  347	atomic(A), var(B), !,
  348	annotate(add_prefix(A, Annotation), B).
  349annotate_atom_concat(A, B, Annotation) :-
  350	var(A), atomic(B), !,
  351	annotate(add_suffix(B, Annotation), A).
  352annotate_atom_concat(_, _, _).
  353
  354annotate_meta_vars_in_body(Term =.. List, _Module) :-
  355	!,
  356	(	var(Term),
  357		var_is_meta_called(Term, Annotation)
  358	->	(	var(List)
  359		->	annotate(univ_list(Annotation), List)
  360		;	(	List = [Functor|Args],
  361				var(Functor)
  362			->	(	finite_length(Args, ArgsLength)
  363				->	annotate(has_arity(ArgsLength, Annotation), Functor)
  364				;	annotate(functor(Annotation), Functor)
  365				)
  366			;	true
  367			)
  368		)
  369	;	true
  370	).
  371
  372annotate_meta_vars_in_body(asserta(Clause), _Module) :-
  373	!,
  374	annotate_database_argument(Clause).
  375annotate_meta_vars_in_body(assert(Clause), _Module) :-
  376	!,
  377	annotate_database_argument(Clause).
  378annotate_meta_vars_in_body(assertz(Clause), _Module) :-
  379	!,
  380	annotate_database_argument(Clause).
  381annotate_meta_vars_in_body(retract(Clause), _Module) :-
  382	!,
  383	annotate_database_argument(Clause).
  384annotate_meta_vars_in_body(retractall(Clause), _Module) :-
  385	!,
  386	annotate_database_argument(Clause).
  387
  388annotate_database_argument(Clause) :-
  389	var(Clause),
  390	!,
  391	annotate(database, Clause).
  392annotate_database_argument(Module:Clause) :-
  393	!,
  394	(	var(Module)
  395	->	annotate(m, Module)
  396	;	true
  397	),
  398	(	var(Clause)
  399	->	annotate(database, Clause)
  400	;	true
  401	).
  402annotate_database_argument(_Clause).
  403
  404annotate_meta_vars_in_body(Goal, Module) :-
  405	extended_meta_predicate(Module:Goal, Head),
  406	!,
  407	functor(Goal, _, Arity),
  408	annotate_meta_args(1, Arity, Goal, Head, Module).
  409annotate_meta_vars_in_body(Goal, Module) :- % TBD: do we trust this?
  410	predicate_property(Module:Goal, meta_predicate(Head)),
  411	!,
  412	functor(Goal, _, Arity),
  413	annotate_meta_args(1, Arity, Goal, Head, Module).
  414annotate_meta_vars_in_body(Goal, Module) :-
  415	inferred_meta(Module:Goal, Head),
  416	!,
  417	functor(Goal, _, Arity),
  418	annotate_meta_args(1, Arity, Goal, Head, Module).
  419annotate_meta_vars_in_body(_, _).
 annotate_meta_args(+Arg, +Arity, +Goal, +MetaSpec, +Module)
  424annotate_meta_args(I, Arity, Goal, MetaSpec, Module) :-
  425	I =< Arity, !,
  426	arg(I, MetaSpec, MetaArg),
  427	arg(I, Goal, Arg),
  428	annotate_meta_arg(MetaArg, Arg, Module),
  429	I2 is I + 1,
  430	annotate_meta_args(I2, Arity, Goal, MetaSpec, Module).
  431annotate_meta_args(_, _, _, _, _).
  432
  433annotate_meta_arg(Spec, Arg, _) :-
  434	var(Arg), !,
  435	annotate(Spec, Arg).
  436annotate_meta_arg(0, Arg, Module) :- !,
  437	annotate_meta_vars_in_body(Arg, Module).
  438annotate_meta_arg(N, Arg, Module) :-
  439	integer(N),
  440	callable(Arg), !,
  441	Arg =.. List,
  442	length(Extra, N),
  443	append(List, Extra, ListX),
  444	ArgX =.. ListX,
  445	annotate_meta_vars_in_body(ArgX, Module).
  446annotate_meta_arg(Spec, Arg, _) :-
  447	(	Spec == :
  448	;	meta_calling_specifier(Spec)
  449	),
  450	compound(Arg),
  451	Arg = Module:_,
  452	var(Module), !,
  453	annotate(m, Module).
  454annotate_meta_arg(_,_,_).
 annotate(+Annotation, +Position, +Var) is det
Add Annotation(s) meta( Aliased, a list of aliases (variables and terms) Metacalled, a list of metacall specifiers (database or 0..9) Components, functor and arity building components Existential, ^ ModuleSensitive, : Mode, one of +, - or ? Nothing * ) to the attributes of Var for this module
  470annotate([], _) :- !.
  471annotate([Annotation|Annotations], Var) :-
  472	!,
  473	annotate(Annotation, Var),
  474	annotate(Annotations, Var).
  475annotate(aliased(Alias), Var) :-
  476	!,
  477	get_or_create_meta(Var, Meta),
  478	add_aliased(Meta, Alias),
  479	put_attr(Var, pmi, Meta).
  480annotate(^, Var) :-
  481	!,
  482	get_or_create_meta(Var, Meta),
  483	add_existential(Meta, ^),
  484	put_attr(Var, pmi, Meta).
  485annotate(:, Var) :-
  486	!,
  487	get_or_create_meta(Var, Meta),
  488	add_msensitive(Meta, :),
  489	put_attr(Var, pmi, Meta).
  490annotate(m, Var) :-
  491	!,
  492	get_or_create_meta(Var, Meta),
  493	add_module(Meta, m),
  494	put_attr(Var, pmi, Meta).
  495annotate(*, Var) :-
  496	!,
  497	get_or_create_meta(Var, Meta),
  498	add_bottom(Meta, *),
  499	put_attr(Var, pmi, Meta).
  500annotate(MetaCall, Var) :-
  501	meta_calling_specifier(MetaCall),
  502	!,
  503	get_or_create_meta(Var, Meta),
  504	add_metacalled(Meta, MetaCall),
  505	put_attr(Var, pmi, Meta).
  506annotate(Component, Var) :-
  507	component_specifier(Component),
  508	!,
  509	get_or_create_meta(Var, Meta),
  510	add_components(Meta, Component),
  511	put_attr(Var, pmi, Meta).
  512annotate(Mode, Var) :-
  513	mode_specifier(Mode),
  514	!,
  515	get_or_create_meta(Var, Meta),
  516	add_mode(Meta, Mode),
  517	put_attr(Var, pmi, Meta).
  518annotate(_, _).
  519
  520% annotations of variables that are metacalled
  521meta_calling_specifier(I) :- integer(I), !.  % metacalled with I additional arguments
  522meta_calling_specifier(^).                   % metacalled 0 and can contain existential variables (called via setof/bagof/aggregate)
  523meta_calling_specifier(//).                  % DCG rule body
  524meta_calling_specifier(database).            % asserted or retracted
  525
  526% annotated var represents the ... 
  527% of a term metacalled according to _M 
  528component_specifier(functor(_M)).          % ... =               functor        
  529component_specifier(add_prefix(_P, _M)).   % ... = suffix of the functor with prefix _P 
  530component_specifier(add_suffix(_S, _M)).   % ... = prefix of the functor with suffix _S
  531component_specifier(is_prefix(_M)).        % ... = prefix of the functor (with unknown suffix)
  532component_specifier(is_suffix(_M)).        % ... = suffix of the functor (with unknown prefix)
  533
  534component_specifier(has_arity(_,_)).       % annotated var is the functor of a term with a fixed number of params
  535
  536component_specifier(arity(_)).             % annotated var represents the arity of another term     
  537
  538component_specifier(univ_list(_Meta)).     % The annotated var is a list from which a term is constructed via =.. 
  539                                       % The constructed term is metacalled according to _Meta
  540
  541mode_specifier(+).
  542mode_specifier(-).
  543mode_specifier(?).
  544
  545
  546get_or_create_meta(Var, Meta) :-
  547	(	get_attr(Var, pmi, Meta)
  548	->	true
  549	;	new_meta(Meta)
  550	).
  551	
  552new_meta( meta([],[],[],[],[],[],[],[]) ).
  553
  554get_aliased(Meta,Value)    :- arg(1,Meta,Value).
  555get_metacalled(Meta,Value) :- arg(2,Meta,Value).
  556get_components(Meta,Value) :- arg(3,Meta,Value).
  557get_existential(Meta,Value):- arg(4,Meta,Value).
  558get_msensitive(Meta,Value) :- arg(5,Meta,Value).
  559get_module(Meta,Value)     :- arg(6,Meta,Value).
  560get_mode(Meta,Value)       :- arg(7,Meta,Value).
  561get_bottom(Meta,Value)     :- arg(8,Meta,Value).
  562
  563set_aliased(Meta,Value)    :- setarg(1,Meta,Value).
  564set_metacalled(Meta,Value) :- setarg(2,Meta,Value).
  565set_components(Meta,Value) :- setarg(3,Meta,Value).
  566set_existential(Meta,Value):- setarg(4,Meta,Value).
  567set_msensitive(Meta,Value) :- setarg(5,Meta,Value).
  568set_module_(Meta,Value)    :- setarg(6,Meta,Value).
  569set_mode(Meta,Value)       :- setarg(7,Meta,Value).
  570set_bottom(Meta,Value)     :- setarg(8,Meta,Value).
  571
  572add_aliased(Meta,Value)    :- get_aliased(Meta, OldValue), merge_aliased([Value], OldValue, NewValue), set_aliased(Meta, NewValue).
  573add_metacalled(Meta,Value) :- get_metacalled(Meta, OldValue), merge_metacalled([Value], OldValue, NewValue), set_metacalled(Meta, NewValue).
  574add_components(Meta,Value) :- get_components(Meta, OldValue), merge_components([Value], OldValue, NewValue), set_components(Meta, NewValue).
  575add_existential(Meta,Value):- set_existential(Meta, Value).
  576add_msensitive(Meta,Value) :- set_msensitive(Meta, Value).
  577add_module(Meta,Value)     :- set_module_(Meta, Value).
  578add_mode(Meta,Value)       :- get_mode(Meta, OldValue), merge_mode(OldValue, Value, NewValue), set_mode(Meta, NewValue).
  579add_bottom(Meta,Value)     :- set_bottom(Meta, Value).
  580
  581merge_aliased(Value1, Value2, Value3)     :- eq_union(Value1, Value2, Value3).
  582
  583merge_metacalled(Value1, Value2, Value3)  :- eq_union(Value1, Value2, Value3).
  584
  585merge_components(Value1, Value2, Value3)  :- eq_union(Value1, Value2, Value3).
  586
  587merge_existential(Value, Value, Value) :- !.
  588merge_existential(_,     _,     ^    ).
  589
  590merge_msensitive(Value, Value, Value) :- !.
  591merge_msensitive(_,     _,     :    ).
  592
  593merge_module(Value, Value, Value) :- !.
  594merge_module(_,     _,     m    ).
  595
  596merge_mode(Value, Value, Value) :- !.
  597merge_mode([],    Value, Value) :- !.
  598merge_mode(Value, [],    Value) :- !.
  599merge_mode(_,     _,     ?    ).
  600
  601merge_bottom(Value, Value, Value) :- !.
  602merge_bottom(_,     _,     *    ).
 eq_union(+Set1, +Set2, ?Set3) is det
Same as lists:union(Set1, Set2, Set3) but uses eq_member/2 instead of lists:member/2.
  609eq_union([], L, L) :- !.
  610eq_union([H|T], L, R) :-
  611	eq_member(H, L),
  612	!,
  613	eq_union(T, L, R).
  614eq_union([H|T], L, [H|R]) :-
  615	eq_union(T, L, R).
 eq_member(+E, +L) is nondet
Same as lists:member(E, L) but uses ==/2 as check for membership.
  620eq_member(E, L) :-
  621	member(E2, L),
  622	E == E2.
 merge_annotations(+Meta1, +Meta2, -Meta) is det
Merge the annotations in the meta/N terms Meta1 and Meta2 into the new meta/N term Meta. Aliases are not considered.
  629merge_annotations(Meta1, Meta2, Meta) :-
  630	new_meta(Meta),
  631	get_metacalled( Meta1, Metacalled1) , get_metacalled( Meta2, Metacalled2) , merge_metacalled( Metacalled1,  Metacalled2,  Metacalled) , set_metacalled( Meta, Metacalled),
  632	get_components( Meta1, Components1) , get_components( Meta2, Components2) , merge_components( Components1,  Components2,  Components) , set_components( Meta, Components),
  633	get_existential(Meta1, Existential1), get_existential(Meta2, Existential2), merge_existential(Existential1, Existential2, Existential), set_existential(Meta, Existential),
  634	get_msensitive( Meta1, MSensitive1) , get_msensitive( Meta2, MSensitive2) , merge_msensitive( MSensitive1,  MSensitive2,  MSensitive) , set_msensitive( Meta, MSensitive),
  635	get_module(     Meta1, Module1)     , get_module(     Meta2, Module2)     , merge_module(     Module1,      Module2,      Module)     , set_module_(    Meta, Module),
  636	get_mode(       Meta1, Mode1)       , get_mode(       Meta2, Mode2)       , merge_mode(       Mode1,        Mode2,        Mode)       , set_mode(       Meta, Mode),
  637	get_bottom(     Meta1, Bottom1)     , get_bottom(     Meta2, Bottom2)     , merge_bottom(     Bottom1,      Bottom2,      Bottom)     , set_bottom(     Meta, Bottom).
 meta_term_head(+Head, -MetaTermHead) is semidet
True when MetaTermHead is the same term as Head, but the variables of Head are replaced by their annotated meta/N term and the information carried by the attributed variables in Head contains "real" meta-elements, not just mode information.
  646meta_term_head(Head, MetaTermHead) :-
  647	functor(Head, Name, Arity),
  648	functor(MetaTermHead, Name, Arity),
  649	meta_args(1, Arity, Head, MetaTermHead, HasMeta),
  650	HasMeta == true.   % fail if MetaTermHead contains just mode info
  651
  652meta_args(I, Arity, Head, Meta, HasMeta) :-
  653	I =< Arity, !,
  654	arg(I, Head, HeadArg),
  655	arg(I, Meta, MetaArg),
  656	meta_term_of_var(HeadArg, MetaArg, HasMeta),
  657	I2 is I + 1,
  658	meta_args(I2, Arity, Head, Meta, HasMeta).
  659meta_args(_, _, _, _, _).
 meta_term_of_var(+AnnotatedArg, -MetaTerm, -IsMetaArg) is det
MetaTerm is bound to the annotated meta/N term of the variable AnnotatedArg. The alias information in the meta/N is removed to avoid unifying of aliases in combine_meta_args/2. If the variable AnnotatedArg is not annotated, MetaTerm is bound to an empty meta/N term. IsMetaArg will be unified to "true" if the MetaTerm is not just mode information (+, -, ?). Otherwise, it is left unbound.
  670meta_term_of_var(HeadArg, MetaTerm, IsMetaArg) :-
  671	get_attr(HeadArg, pmi, MetaTerm),
  672	!,
  673	set_aliased(MetaTerm, []),
  674	(	has_meta_calling_or_component_or_msensitive_annotation(MetaTerm)
  675	->	IsMetaArg = true
  676	;	true
  677	).
  678meta_term_of_var(HeadArg, MetaTerm, IsMetaArg) :-
  679	compound(HeadArg),
  680	HeadArg = M:_,
  681	get_attr(M, pmi, MMeta),
  682	get_module(MMeta, m),
  683	!,
  684	new_meta(MetaTerm),
  685	set_msensitive(MetaTerm, :),
  686	IsMetaArg = true.
  687meta_term_of_var(_, NewMeta, _) :-
  688	new_meta(NewMeta).
  689
  690has_meta_calling_or_component_or_msensitive_annotation(Meta) :-
  691	(	get_metacalled(Meta, Metacalled),
  692		member(_, Metacalled)
  693	;	get_existential(Meta, ^)
  694	;	get_components(Meta, Components),
  695		member(_, Components)
  696	;	get_msensitive(Meta, :)
  697	),
  698	!.
  699
  700remove_list_if_possible([], _) :- !, fail.
  701remove_list_if_possible([X], X) :- !.
  702remove_list_if_possible(List, List).
 combine_meta_args(+Heads, -Head) is det
Combine multiple heads with meta/N terms from disjunctive branches
  708combine_meta_args([], []) :- !.
  709combine_meta_args([List], List) :- !.
  710combine_meta_args([Spec,Spec|Specs], CombinedArgs) :- !,
  711	combine_meta_args([Spec|Specs], CombinedArgs).
  712combine_meta_args([Spec1,Spec2|Specs], CombinedArgs) :-
  713	Spec1 =.. [Name|Args1],
  714	Spec2 =.. [Name|Args2],
  715	maplist(merge_annotations, Args1, Args2, Args),
  716	Spec =.. [Name|Args],
  717	combine_meta_args([Spec|Specs], CombinedArgs).
 meta_terms_to_arg_specs_in_head(+HeadWithMetaTerms, -MetaSpec) is det
Translate the head HeadWithMetaTerms with meta/N terms to the corresponding meta-specification MetaSpec.
  723meta_terms_to_arg_specs_in_head(HeadWithMetaTerms, MetaSpec) :-
  724	HeadWithMetaTerms =.. [Name|MetaTerms],
  725	maplist(meta_term_to_arg_spec, MetaTerms, ArgSpecs),
  726	MetaSpec =.. [Name|ArgSpecs].
 meta_term_to_arg_spec(+MetaTerm, -ArgSpec) is det
Translate the meta/N term MetaTerm to the corresponding meta argument specifier ArgSpec. ArgSpec is either one specifier or a list of multiple specifiers. The specifier is determined using the following partial order:
(0..9,database,//) components | / ^ / \ / : / \ / mode | *
  744meta_term_to_arg_spec(MetaTerm, ArgSpec) :-
  745	get_metacalled_or_existential_or_msensitive(MetaTerm, MetacalledOrExistential),
  746	get_components(MetaTerm, Components),
  747	append(MetacalledOrExistential, Components, ArgSpec0),
  748	sort(ArgSpec0, ArgSpec1),
  749	remove_list_if_possible(ArgSpec1, ArgSpec),
  750	!.
  751
  752meta_term_to_arg_spec(MetaTerm, ArgSpec) :-
  753	get_mode(MetaTerm, ArgSpec),
  754	ArgSpec \== [],
  755	!.
  756
  757meta_term_to_arg_spec(_, *).
  758
  759get_metacalled_or_existential_or_msensitive(MetaTerm, Metacalled) :-
  760	get_metacalled(MetaTerm, Metacalled),
  761	Metacalled \== [],
  762	!.
  763get_metacalled_or_existential_or_msensitive(MetaTerm, [^]) :-
  764	get_existential(MetaTerm, ^),
  765	!.
  766get_metacalled_or_existential_or_msensitive(MetaTerm, [:]) :-
  767	get_msensitive(MetaTerm, :),
  768	!.
  769get_metacalled_or_existential_or_msensitive(_MetaTerm, []).
  770
  771
  772attr_unify_hook(A0, Other) :-
  773	writeln(attr_unify_hook(A0, Other))