[m-rev.] for review: fix singleton warnings for code with explicit quantification

Julien Fischer jfischer at opturion.com
Sat Jun 10 17:03:00 AEST 2023


On Sat, 10 Jun 2023, Zoltan Somogyi wrote:

> Fix singleton warnings for code with 'some [...]' goals.
> 
> The code in make_hlds_warn.m that is intended to generate singleton warnings
> hasn't ever been able to handle code containing 'some [...]' goals properly.
> The reason is that
> 
> - add_clause.m invokes make_hlds_warn.m only *after* it does quantification
>   on the body of the clause being added to the HLDS, but
> 
> - quantification has always replaced all lists of quantified variables
>   with the empty list.
> 
> This meant that
> 
> - we never could report code in which the only occurrence of a variable
>   was in a list of quantified variables, which is something we *should*
>   warn about, and
> 
> - we always did generate a singleton warning for code such as
>   "some [Val] map.search(Map, Key, Val)", which is something we *should not*
>   warn about.
> 
> This diff fixes this problem.
> 
> The main change is a mechanism that allows us to tell quantification.m
> to keep lists of quantified variables intact. However, since the rest
> of the compiler does not react well to these lists not being empty,
> this diff
> 
> - gets make_hlds_warn.m to report whether the clause body goal, in which
>   quantification.m was told to preserve any lists of quantified variables,
>   *actually contained* any nonempty lists of quantified variables, and
> 
> - if it did, then we invoke quantification.m again, this time telling it
>   to nuke all lists of quantified variables.
> 
> This nuking has to be done relatively rarely, because only a very small
> fraction of clauses contain any explicit quantification.
> 
> (An alternative design would be for make_hlds_warn.m to always nuke
> any noempty list of quantified variables it traversed. However, this would

s/noempty/nonempty/

> require *always* rebuilding the clause body goal, which would probably
> be slower on average.

You're missing a closing parenthesis there.

> The above is the main change in this diff. However, the change that is
> responsible for the bulk of the diff is the addition of a flag to
> exist_quant scopes to specify whether that scope was created by the user
> or by the compiler. This is needed because if make_hlds_warn.m sees code
> such as "some [Val] map.search(Map, Key, Val)", it definitely *should*
> generate a warning about Val being singleton (if it does not occur outside
> this code) if the "some [Val]" scope was put there by the compiler.

Do you mean was *not* put there by the compiler? That last sentence is
not particularly clear.

...

> diff --git a/compiler/quantification.m b/compiler/quantification.m
> index 7e41c73b9..a20c78994 100644
> --- a/compiler/quantification.m
> +++ b/compiler/quantification.m
> @@ -87,8 +87,34 @@
>  :- pred requantify_proc_general(nonlocals_to_recompute::in,
>      proc_info::in, proc_info::out) is det.
> 
> +:- type maybe_keep_quant
> +    --->    do_not_keep_quant       % Throw away lists of quantified vars.
> +    ;       keep_quant.             % Keep lists of quantified vars.

I would name that type "maybe_keep_quant_vars" and also append "_vars"
to each of its constructors.

> +
> +    % implicitly_quantify_clause_body_general_vs(NonLocalsToRecompute,
> +    %   KeepQuant, HeadVars, Warnings, !Goal,
> +    %   !VarSet, !VarTypes, !RttiVarMaps):
> +    % implicitly_quantify_clause_body_general_vs(NonLocalsToRecompute,
> +    %   HeadVars, Warnings, !Goal, !VarTable, !RttiVarMaps):
> +    %
> +    % Do the job described at the top-of-module comment. Find out the scope
> +    % of each variable, rename (actually, re-number) apart variables with
> +    % distinct scopes that happen to share the same name, and fill in
> +    % the nonlocals slots of all the goal_infos. Use either !VarSet and
> +    % !VarTypes, or !VarTable, to create renamed-apart copies of variables.
> +    %
> +    % If KeepQuant is keep_quant, the keep the variables listed in exist_quant
> +    % scopes, and in the quantified-vars slots of if-then-elses. If KeepQuant
> +    % is do_not_keep_quant, then replace both with the empty list of variables.
> +    % Since obviously the var_table version is invoked only by compiler phases
> +    % that run when var_tables are available, i.e. after typecheck, and
> +    % we (actually, make_hlds_warn.m) need the lists of quantified variables
> +    % only during the make-hlds phase of the compiler, the var_table version
> +    % does not take a maybe_keep_quant argument, but does its work
> +    % as if it was set to do_not_keep_quant.
> +    %
>  :- pred implicitly_quantify_clause_body_general_vs(nonlocals_to_recompute::in,
> -    list(prog_var)::in, list(quant_warning)::out,
> +    maybe_keep_quant::in, list(prog_var)::in, list(quant_warning)::out,
>      hlds_goal::in, hlds_goal::out, prog_varset::in, prog_varset::out,
>      vartypes::in, vartypes::out, rtti_varmaps::in, rtti_varmaps::out) is det.
>  :- pred implicitly_quantify_clause_body_general(nonlocals_to_recompute::in,

...

> @@ -333,10 +365,13 @@ quantify_goal(NonLocalsToRecompute, Goal0, Goal, !Info) :-
>          Goal = hlds_goal(!.GoalExpr, !.GoalInfo)
>      ).
> 
> -    % After this pass, explicit quantifiers are redundant, since all variables
> -    % which were explicitly quantified have been renamed apart. So we don't
> -    % keep them. We need to keep the structure, though, so that mode analysis
> -    % doesn't try to reorder through quantifiers. (Actually it would make sense

Missing closing parenthesis.

> +    % If the qi_keep_quant is do_not_keep_quant, then we are past the compiler

s/qi_keep_quant/qi_keep_quant field/

> +    % phases that needed explicit quantifiers, and the code of quantification.m
> +    % never needs it, since all variables which were explicitly quantified
> +    % would have been renamed apart during its first invocation. So we keep
> +    % lists of quantified variables only if qi_keep_quant is keep_quant.
> +    % We need to keep the structure, though, so that mode analysis doesn't
> +    % try to reorder through quantifiers. (Actually it would make sense
>      % to allow mode analysis to do that, but the reference manual says it
>      % doesn't, so we don't.) Thus we replace `scope(exist_quant(Vars), Goal0)'
>      % with an empty quantifier `scope(exist_quant([]), Goal)'.

That looks fine otherwise.

Julien.


More information about the reviews mailing list