[m-rev.] for review: exception analysis of generic calls

Julien Fischer juliensf at cs.mu.OZ.AU
Mon Jul 25 18:16:22 AEST 2005


For review by anyone.

Estimated hours taken: 7
Branches: main

Add support for exception analysis through some generic_call sites.
At the moment this is restricted to higher-order calls, although
we may eventually extend it to method calls as well.

As with the recent change to generic_calls for termination analysis,
this is currently of limited usefulness because the procedures
dependency graph does not record information about higher-order calls.

I'll add some test cases when the dependency graph has been changed
to include this information.

compiler/exception_analysis.m:
	Use the results of closure analysis to perform exception
	analysis at generic_call sites.

Julien.

Index: compiler/exception_analysis.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/exception_analysis.m,v
retrieving revision 1.9
diff -u -r1.9 exception_analysis.m
--- compiler/exception_analysis.m	14 Apr 2005 07:47:31 -0000	1.9
+++ compiler/exception_analysis.m	25 Jul 2005 07:59:58 -0000
@@ -267,20 +267,23 @@
     ( goal_info_get_determinism(GoalInfo, erroneous) ->
         !:Result = !.Result ^ status := may_throw(user_exception)
     ;
-        check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result)
+        check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, GoalInfo,
+            !Result)
     ).

 :- pred check_goal_for_exceptions_2(scc::in, module_info::in, vartypes::in,
-    hlds_goal_expr::in, proc_result::in, proc_result::out) is det.
+    hlds_goal_expr::in, hlds_goal_info::in, proc_result::in, proc_result::out)
+    is det.

-check_goal_for_exceptions_2(_, _, _, unify(_, _, _, Kind, _), !Result) :-
+check_goal_for_exceptions_2(_, _, _, Goal, _, !Result) :-
+    Goal = unify(_, _, _, Kind, _),
     ( Kind = complicated_unify(_, _, _) ->
         unexpected(this_file,
             "complicated unify during exception analysis.")
     ;
         true
     ).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, _, !Result) :-
     Goal = call(CallPredId, CallProcId, CallArgs, _, _, _),
     CallPPId = proc(CallPredId, CallProcId),
     module_info_pred_info(ModuleInfo, CallPredId, CallPredInfo),
@@ -323,14 +326,76 @@
         check_nonrecursive_call(ModuleInfo, VarTypes, CallPPId, CallArgs,
             !Result)
     ).
-check_goal_for_exceptions_2(_, _, _, generic_call(_,_,_,_), !Result) :-
-    !:Result = !.Result ^ status := may_throw(user_exception).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, not(Goal), !Result) :-
+check_goal_for_exceptions_2(_, ModuleInfo, VarTypes, Goal, GoalInfo,
+        !Result) :-
+    Goal = generic_call(Details, Args, _ArgModes, _),
+    (
+        Details = higher_order(Var, _, _,  _),
+        ClosureValueMap = goal_info_get_ho_values(GoalInfo),
+        ( ClosureValues = ClosureValueMap ^ elem(Var) ->
+                (
+                    get_conditional_closures(ModuleInfo, ClosureValues,
+                        Conditional)
+                ->
+                    (
+                        Conditional = []
+                        % The values of the higher-order variable are
+                        % all procedures that are known not to throw
+                        % exceptions.
+                    ;
+                        Conditional = [_|_],
+                        %
+                        % For 'conditional' procedures we need to make
+                        % sure that if any type variables are bound at
+                        % the generic_call site, then this does not
+                        % cause the closure to throw an exception
+                        % (because of a user-defined equality or
+                        % comparison predicate that throws an
+                        % exception.)
+                        %
+                        % If we can resolve all of the polymorphism at
+                        % this generic_call site, then we can reach a
+                        % definite conclusion about it.
+                        %
+                        % If we cannot do so, then we propagate the
+                        % 'conditional' status to the current predicate
+                        % if all the type variables involved are
+                        % universally quantified, or mark it as throwing
+                        % an exception if some of them are existentially
+                        % quantified.
+                        %
+                        % XXX This is too conservative but we don't
+                        % currently perform a fine-grained enough
+                        % analysis of where out-of-line
+                        % unifications/comparisons occur to be able to
+                        % do better.
+                        %
+                        check_vars(ModuleInfo, VarTypes, Args, !Result)
+                    )
+                ;
+                    !:Result = !.Result ^ status := may_throw(user_exception)
+                )
+        ;
+            !:Result = !.Result ^ status := may_throw(user_exception)
+        )
+    ;
+        % XXX We could do better with class methods.
+        Details = class_method(_, _, _, _),
+        !:Result = !.Result ^ status := may_throw(user_exception)
+    ;
+        Details = unsafe_cast
+    ;
+        Details = aditi_builtin(_, _),
+        !:Result = !.Result ^ status := may_throw(user_exception)
+    ).
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, not(Goal), _,
+        !Result) :-
     check_goal_for_exceptions(SCC, ModuleInfo, VarTypes, Goal, !Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, _,
+        !Result) :-
     Goal = scope(_, ScopeGoal),
     check_goal_for_exceptions(SCC, ModuleInfo, VarTypes, ScopeGoal, !Result).
-check_goal_for_exceptions_2(_, _, _, Goal, !Result) :-
+check_goal_for_exceptions_2(_, _, _, Goal, _, !Result) :-
     Goal = foreign_proc(Attributes, _, _, _, _, _),
     ( may_call_mercury(Attributes) = may_call_mercury ->
         may_throw_exception(Attributes) = MayThrowException,
@@ -346,22 +411,25 @@
     ;
         true
     ).
-check_goal_for_exceptions_2(_, _, _, shorthand(_), _, _) :-
+check_goal_for_exceptions_2(_, _, _, shorthand(_), _, _, _) :-
     unexpected(this_file,
         "shorthand goal encountered during exception analysis.").
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, _, !Result) :-
     Goal = switch(_, _, Cases),
     CaseGoals = list.map((func(case(_, CaseGoal)) = CaseGoal), Cases),
     check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, CaseGoals, !Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, _, !Result) :-
     Goal = if_then_else(_, If, Then, Else),
     check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, [If, Then, Else],
         !Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, disj(Goals), !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, disj(Goals), _,
+        !Result) :-
     check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, Goals, !Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, par_conj(Goals), !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, par_conj(Goals), _,
+        !Result) :-
     check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, Goals, !Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, conj(Goals), !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, conj(Goals), _,
+        !Result) :-
     check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, Goals, !Result).

 :- pred check_goals_for_exceptions(scc::in, module_info::in, vartypes::in,
@@ -380,6 +448,37 @@
       then  true
       else  check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, Goals,
                 !Result)
+    ).
+
+%----------------------------------------------------------------------------%
+%
+% Further code to handle higher-order variables
+%
+
+    % Given a list of procedure ids extract those whose exception status
+    % has been set to 'conditional'.  Fails if one of the procedures in
+    % the set has an exception status that indicate it may throw an
+    % exception, or if the exception status for a procedure has not yet
+    % been set.
+    %
+:- pred get_conditional_closures(module_info::in, set(pred_proc_id)::in,
+    list(pred_proc_id)::out) is semidet.
+
+get_conditional_closures(ModuleInfo, Closures, Conditionals) :-
+    module_info_exception_info(ModuleInfo, ExceptionInfo),
+    set.fold(get_conditional_closure(ExceptionInfo), Closures,
+        [], Conditionals).
+
+:- pred get_conditional_closure(exception_info::in, pred_proc_id::in,
+    list(pred_proc_id)::in, list(pred_proc_id)::out) is semidet.
+
+get_conditional_closure(ExceptionInfo, PPId, !Conditionals) :-
+    ExceptionInfo ^ elem(PPId) = Status,
+    (
+        Status = conditional,
+        list.cons(PPId, !Conditionals)
+    ;
+        Status = will_not_throw
     ).

 %----------------------------------------------------------------------------%

--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list