new warning for infinite recursion

Fergus Henderson fjh at cs.mu.oz.au
Sun Feb 9 03:15:04 AEDT 1997


Hi,

Simon, can you please review this change?

-----------------------------------------------------------------------------

Warn about obvious instances of infinite recursion:
directly recursive calls with the same (or equivalent) input arguments.

compiler/simplify.m:
	Check for new warning.

compiler/det_report.m:
	Add code to output the new warning.

compiler/common.m:
	Export common__vars_are_equivalent, for use by simplify.m.
	Also slightly tidy up the code for that predicate and its callers.

tests/warnings/Mmake:
tests/warnings/infinite_recursion.m:
tests/warnings/infinite_recursion.exp:
	Test case for new warning.


Index: simplify.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/simplify.m,v
retrieving revision 1.21
diff -u -r1.21 simplify.m
--- simplify.m	1997/01/30 15:08:00	1.21
+++ simplify.m	1997/02/08 15:47:21
@@ -370,12 +370,50 @@
 	;
 		Info1 = Info0
 	),
-	( simplify_do_calls(Info1) ->
+
+	%
+	% Check for recursive calls with the same input arguments,
+	% and warn about them (since they will lead to infinite loops).
+	%
+	(
+		simplify_do_warn(Info1),
+
+		%
+		% Is this a (directly) recursive call,
+		% i.e. is the procedure being called the same as the
+		% procedure we're analyzing?
+		%
+		simplify_info_get_det_info(Info1, DetInfo),
+		det_info_get_pred_id(DetInfo, PredId),
+		det_info_get_proc_id(DetInfo, ProcId),
+
+		%
+		% Are the input arguments the same (or equivalent)?
+		%
+		simplify_info_get_module_info(Info1, ModuleInfo1),
+		module_info_pred_proc_info(ModuleInfo1, PredId, ProcId,
+			_PredInfo1, ProcInfo1),
+		proc_info_headvars(ProcInfo1, HeadVars),
+		proc_info_argmodes(ProcInfo1, ArgModes),
+		simplify_info_get_common_info(Info1, CommonInfo1),
+		simplify__input_args_are_equiv(Args, HeadVars, ArgModes,
+			CommonInfo1, ModuleInfo1)
+	->
+		simplify_info_add_msg(Info1, warn_infinite_recursion(GoalInfo),
+				Info2)
+	;
+		Info2 = Info1
+	),
+
+	%
+	% check for duplicate calls to the same procedure
+	%
+	( simplify_do_calls(Info2) ->
 		common__optimise_call(PredId, ProcId, Args, Goal0, GoalInfo,
-			Goal, Info1, Info)
+			Goal, Info2, Info)
 	;
 		Goal = Goal0,
-		Info = Info1
+		Info = Info2
 	).
 
 simplify__goal_2(Goal0, GoalInfo0, Goal, GoalInfo, Info0, Info) :-
@@ -513,6 +551,31 @@
 		Info = Info0,
 		Goal = Goal0
 	).
+
+%-----------------------------------------------------------------------------%
+
+	% simplify__input_args_are_equiv(Args, HeadVars, Modes,
+	% 		CommonInfo, ModuleInfo1):
+	% Succeeds if all the input arguments (determined by looking at
+	% `Modes') in `Args' are equivalent (according to the equivalence
+	% class specified by `CommonInfo') to the corresponding variables
+	% in HeadVars.  HeadVars, Modes, and Args should all be lists of
+	% the same length.
+
+:- pred simplify__input_args_are_equiv(list(var), list(var), list(mode),
+	common_info, module_info).
+:- mode simplify__input_args_are_equiv(in, in, in, in, in) is semidet.
+
+simplify__input_args_are_equiv([], [], _, _, _).
+simplify__input_args_are_equiv([Arg|Args], [HeadVar|HeadVars], [Mode|Modes],
+		CommonInfo, ModuleInfo1) :-
+	( mode_is_input(ModuleInfo1, Mode) ->
+		common__vars_are_equivalent(Arg, HeadVar, CommonInfo)
+	;
+		true
+	),
+	simplify__input_args_are_equiv(Args, HeadVars, Modes,
+			CommonInfo, ModuleInfo1).
 
 %-----------------------------------------------------------------------------%
 
Index: det_report.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/det_report.m,v
retrieving revision 1.30
diff -u -r1.30 det_report.m
--- det_report.m	1997/02/02 21:34:24	1.30
+++ det_report.m	1997/02/08 13:14:57
@@ -32,6 +32,9 @@
 				% warning about calls to predicates
 				% for which there is a `:- pragma obsolete'
 				% declaration.
+		;	warn_infinite_recursion(hlds__goal_info)
+				% warning about recursive calls
+				% which would cause infinite loops.
 		;	duplicate_call(seen_call_id, term__context,
 				hlds__goal_info)
 				% multiple calls with the same input args.
@@ -825,6 +828,7 @@
 det_msg_get_type(negated_goal_cannot_fail(_), warning).
 det_msg_get_type(negated_goal_cannot_succeed(_), warning).
 det_msg_get_type(warn_obsolete(_, _), warning).
+det_msg_get_type(warn_infinite_recursion(_), warning).
 det_msg_get_type(duplicate_call(_, _, _), warning).
 det_msg_get_type(cc_pred_in_wrong_context(_, _, _, _), error).
 det_msg_get_type(higher_order_cc_pred_in_wrong_context(_, _), error).
@@ -895,6 +899,27 @@
 	io__write_string("Warning: call to obsolete "),
 	hlds_out__write_pred_id(ModuleInfo, PredId),
 	io__write_string(".\n").
+det_report_msg(warn_infinite_recursion(GoalInfo), _ModuleInfo) -->
+	{ goal_info_get_context(GoalInfo, Context) },
+/*
+% it would be better if we supplied more information
+% than just the line number.
+	prog_out__write_context(Context),
+	io__write_string("In "),
+	hlds_out__write_pred_id(ModuleInfo, PredId),
+	io__write_string(":\n"),
+*/
+	prog_out__write_context(Context),
+	io__write_string(
+		"Warning: recursive call will lead to infinite recursion.\n"),
+	globals__io_lookup_bool_option(verbose_errors, VerboseErrors),
+	( { VerboseErrors = yes } ->
+		io__write_string(
+"\tIf this recursive call is executed, the procedure will call itself
+\twith exactly the same input arguments, leading to infinite recursion.\n")
+	;
+		[]
+	).
 det_report_msg(duplicate_call(SeenCall, PrevContext, GoalInfo), ModuleInfo) -->
 	{ goal_info_get_context(GoalInfo, Context) },
 	prog_out__write_context(Context),
Index: common.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/common.m,v
retrieving revision 1.35
diff -u -r1.35 common.m
--- common.m	1997/01/30 15:07:51	1.35
+++ common.m	1997/02/08 13:38:16
@@ -60,6 +60,10 @@
 :- mode common__optimise_higher_order_call(in, in, in, in, in, in, out,
 		in, out) is det.
 
+	% succeeds if the two variables are equivalent
+	% according to the specified equivalence class.
+:- pred common__vars_are_equivalent(var, var, common_info).
+:- mode common__vars_are_equivalent(in, in, in) is semidet.
 
 	% Assorted stuff used here that simplify.m doesn't need to know about.
 :- type common_info.
@@ -226,7 +230,7 @@
 		ConsId = StructConsId,
 		(
 			UniType = construction,
-			common__vars_are_equivalent(ArgVars,
+			common__var_lists_are_equiv(ArgVars,
 				StructArgVars, VarEqv),
 
 			% Two structures of the same shape may have different 
@@ -235,7 +239,7 @@
 			common__compatible_types(VarType, StructType)
 		;
 			UniType = deconstruction,
-			common__vars_are_equivalent([Var], [OldVar], VarEqv)
+			common__vars_are_equiv(Var, OldVar, VarEqv)
 		)
 	->
 		OldStruct = Struct
@@ -270,26 +274,40 @@
 
 %---------------------------------------------------------------------------%
 
-:- pred common__vars_are_equivalent(list(var), list(var), eqvclass(var)).
-:- mode common__vars_are_equivalent(in, in, in) is semidet.
+	% succeeds if the two lists of variables are equivalent
+	% according to the specified equivalence class.
+:- pred common__var_lists_are_equiv(list(var), list(var), eqvclass(var)).
+:- mode common__var_lists_are_equiv(in, in, in) is semidet.
+
+common__var_lists_are_equiv([], [], _VarEqv).
+common__var_lists_are_equiv([X | Xs], [Y | Ys], VarEqv) :-
+	common__vars_are_equiv(X, Y, VarEqv),
+	common__var_lists_are_equiv(Xs, Ys, VarEqv).
+
+common__vars_are_equivalent(X, Y, CommonInfo) :-
+	CommonInfo = common(EqvVars, _, _, _),
+	common__vars_are_equiv(X, Y, EqvVars).
+
+	% succeeds if the two variables are equivalent
+	% according to the specified equivalence class.
+:- pred common__vars_are_equiv(var, var, eqvclass(var)).
+:- mode common__vars_are_equiv(in, in, in) is semidet.
 
-common__vars_are_equivalent([], [], _VarEqv).
-common__vars_are_equivalent([Arg | Args], [Struct | Structs], VarEqv) :-
+common__vars_are_equiv(X, Y, VarEqv) :-
 	% write('looking for equivalence of '),
-	% write(Arg),
+	% write(X),
 	% write(' and '),
-	% write(Struct),
+	% write(Y),
 	% nl,
 	(
-		Arg = Struct
+		X = Y
 	;
-		eqvclass__is_member(VarEqv, Arg),
-		eqvclass__is_member(VarEqv, Struct),
-		eqvclass__same_eqvclass(VarEqv, Arg, Struct)
-	),
+		eqvclass__is_member(VarEqv, X),
+		eqvclass__is_member(VarEqv, Y),
+		eqvclass__same_eqvclass(VarEqv, X, Y)
+	).
 	% write('they are equivalent'),
-	% nl,
-	common__vars_are_equivalent(Args, Structs, VarEqv).
+	% nl.
 
 %---------------------------------------------------------------------------%
 
@@ -533,7 +551,7 @@
 common__find_previous_call([SeenCall | SeenCalls], InputArgs,
 		Eqv, OutputArgs2, PrevContext) :-
 	SeenCall = call_args(PrevContext, InputArgs1, OutputArgs1),
-	( common__vars_are_equivalent(InputArgs, InputArgs1, Eqv) ->
+	( common__var_lists_are_equiv(InputArgs, InputArgs1, Eqv) ->
 		OutputArgs2 = OutputArgs1
 	;
 		common__find_previous_call(SeenCalls, InputArgs, Eqv,

-- 
Fergus Henderson <fjh at cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3         |     -- the last words of T. S. Garp.



More information about the developers mailing list