Proposal to fix the interaction of input_spec pragmas with other pragmas that refer to specific procedures ------------------------------------------------------------------ These other pragmas can be put into these groups: group 1: termination termination2 require_tail_rec group 2: obsolete_proc group 3: tabling method pragmas such as memo and minimal group 4: structure sharing structure reuse group 5: type_spec group 6: the gen pragmas that record analysis results For groups 1, 2, 3 and 4, adding the pragma to the HLDS looks up the specified procedure in the HLDS, and adds a notation to its proc_info. When input specialization duplicates a procedure, this automatically duplicates the notation as well. ------------------------------------------------------------------ GROUP 1 The pragmas in group 1 each call for some diagnostic about the procedure if some condition is not met. Duplicating the procedure will indirectly cause the duplication of these diagnostics as well, but this is not an issue if the diagnostics for each duplicate proc are identical to the diagnostics for the original procedure. However, the diagnostic text may include the argument modes, and these WILL differ in the duplicated copies. The way I propose to fix this is as follows. - When input specialization duplicates a procedure, mark the duplicate proc_info as being a duplicate created by input specialization. - When compiler passes that the generate diagnostics called for by the pragmas in group 1, simply to do not process the procedures marked in that way. - To ensure that all called-for diagnostics are in fact generated, keep the original procedure in its pred_info's proc_table EVEN WITH REPLACE_IN_MODE. - To ensure the absence of calls to any procedures that replace_in_mode logically deletes, mark each original procedure that input specialization creates duplicates of as either "original, logically deleted" (with replace_in_mode) or as "original, logically kept" (with add_to_in_mode), and - have simplification generate an error for each call to an "original, logically deleted" procedure, in roughly the same way as we generate warnings for calls to obsolete predicates. In general, this new notation could be a new field in proc_infos, whose type is something like this: :- type maybe_input_spec_proc ---> not_involved_in_input_spec(proc_id) ; input_spec_original_proc_kept(proc_id) ; input_spec_original_proc_deleted(proc_id) ; input_specialized_proc(proc_id). The proc_id fields would each id the *original* procedure that the annotated procedure is a (possibly modified) copy of. ------------------------------------------------------------------ GROUP 2 obsolete_proc pragmas differ from group 1 pragmas in that they call for diagnostics not for some property of the body of the procedure they name, but for *calls* to the procedure they name from other predicates. The only issue here is that input specialization may redirect a call to an obsolete proc to one of its input specialized variants, which would cause the diagnostic to refer to the specialized mode of the callee. The code that generates the diagnostic would use the maybe_input_spec_proc field on the call to map it back to the procedure that the obsolete_proc pragma is for. ------------------------------------------------------------------ GROUP 3 Tabling pragmas can generate diagnostics if they are not compatible with the predicate or procedure they are for. At the moment, most such diagnostics are generated when the pragma is added to the HLDS. Some are created much later, in table_gen.m, but I think all of these could be shifted to the ado-to-HLDS time. The diagnostics generated for tabling pragmas can be warnings or errors. The warnings are each accompanied by code that ignores the pragma, and ignored tabling pragmas can have no interaction with input_spec pragmas. The errors will each prevent compilation from succeeding, so we can simply turn off all input_spec pragmas in their presence, again preventing any interaction. The interaction we need to worry about concerns the code fragments that tabling wraps procedure bodies in: lookups in the call table and in the answer table, and additions to both tables. These all include references to compiler-generated per-procedure data structures, which may also be referred to by compiler-generated reset and statistics predicates. At the moment, some of those code fragments happen at add-to-HLDS time, before input specialization is run. This means that if input specialization duplicates a tabled procedure, the proc_ids seen by table_gen.m and the proc_ids used by add_pragma_tabling.m to create the bodies of the reset and statistics procedures won't match. This would lead to the generated .c file not being compilable (in the better case) or the generated executable accessing the wrong thing (in the worse case). The way I propose to handle this is to simply consider any change by input specialization to any predicate that contains a tabled procedure to be an error. The only raised by this decision is: what can a programmer do whose module contains both an input_spec and a tabling pragmas, and they clash? Deleting one or the other will work. Is this good enough, or do we need to add a flag to input_spec pragmas that says "if applying this input specialization to a predicate clashes with a tabling pragma for that predicate, then forget input specialization for that predicate"? I would say no, since the absence of input_specs is the current condition. ------------------------------------------------------------------ GROUP 4 It has been a *very* long time since I looked at the code that implements structure sharing/reuse pragmas. I don't know enough to say how they should be handled. I don't even know whether they work well enough for it to be worth worrying about how they would be affected. ------------------------------------------------------------------ GROUP 5 I think that logically, input specialization in its current form and type specialization are orthogonal, because - input specialization applies to args whose types are ground (at least for now), while - type specialization replaces type variables in argument types. I don't yet know whether the implementations are orthogonal, mostly because the existing implementation of type specialization is (I think) unnecessarily roundabout. By that I mean that creating a new predicate containing a type-specializable call just so that a later traversal of the module_info will find it and add an entry to a table is much more complicated than simply adding that same entry to a table. The roundabout way may have some advantage I don't know about, but it would be nice if it was documented somehere obvious. For now, I propose to generate an error if we are about to apply type specialization to predicate that was modified by input specialization. The justification for this is the same as for group 3: requiring users to delete the input_spec pragma would just get them back to the current condition. ------------------------------------------------------------------ GROUP 6 Any generated pragmas in .opt/.trans_opt files recording the results of e.g. unused arg or trailing analysis for a given procedure should be for either not_involved_in_input_spec or for input_spec_original_proc_{kept,deleted} procedures, using the proc_ids of original procedures. (Any such results should also apply to any input-specialized versions, but we don't opt-export any yet.)