[m-rev.] for review: don't allow nondefault mode functions in terms

Peter Wang novalazy at gmail.com
Fri Oct 30 15:21:59 AEDT 2015


On Fri, 30 Oct 2015 13:58:43 +1100 (AEDT), "Zoltan Somogyi" <zoltan.somogyi at runbox.com> wrote:
> The language reference manual already disallows putting functions with
> nondefault mode and determinism signatures into terms (in section 8.3)
> We allowed it so far, and even used such code ourselves, even though
> it could lead to crashes. So this diff, which disallows such code, would
> de facto, though not de jure, change the language. Any objections to that?

As mentioned, the manual doesn't disallow it completely.  I agree with
disallowing construction of terms with a non-default mode function as a
limitation of the implementation.

> Don't allow putting functions with nondefault signatures into terms.
> 
> The code that takes the function out of the term can use the function
> as if it had the default signature. If it doesn't, this can lead to
> This fixes Mantis bug 264.

lead to crashes.

> 
> compiler/mode_errors.m:
>     Provide a way to record such errors, and to print error messages for them.
> 
> compiler/modecheck_unify.m:
>     Look for such errors when constructing terms.
> 
> browser/declarative_user.m:
>     Don't put a function with a nondefault signature (it used a subtype)
>     into a term. Enforce the subtype with a runtime assertion instead.
> 
> tests/invalid/default_ho_inst.{m,err_exp}:
>     The test case for Mantis bug 264.
> 
> tests/invalid/Mmakefile:
>     Enable the new test case.
> 
> tests/valid/Mmakefile:
>     Disable the old ho_func_call test case, since it RELIES on putting
>     functions with nondefault modes into terms. It also uses I/O states
>     in ui modes.
> 

> diff --git a/compiler/mode_errors.m b/compiler/mode_errors.m
> index db0f6cd..bde9f12 100644
> --- a/compiler/mode_errors.m
> +++ b/compiler/mode_errors.m
> @@ -142,6 +142,13 @@
>              % One of the head variables did not have the expected final inst
>              % on exit from the proc.
>  
> +    ;       mode_error_nondefault_mode_func(prog_var, cons_id,
> +                list(prog_var), one_or_more(prog_var))
> +            % mode_error_nondefault_mode_func(X, ConsId, Ys, BadYs):
> +            % In the unification X = ConsId(Ys), the BadYs are functions
> +            % that do NOT have the default mode and determinism, i.e.
> +            % they aren't functions of the form f(in, ..., in) = out is det.
> +
>      ;       purity_error_should_be_in_promise_purity_scope(
>                  negated_context_desc, prog_var)
>              % The condition of an if-then-else or the body of a negation
> @@ -344,6 +351,11 @@ mode_error_to_spec(ModeInfo, ModeError) = Spec :-
>          Spec = mode_error_unify_var_functor_to_spec(ModeInfo, Var, Name,
>              Args, Inst, ArgInsts)
>      ;
> +        ModeError = mode_error_nondefault_mode_func(Var, Name,
> +            AllArgVars, BadArgVars),
> +        Spec = mode_error_nondefault_mode_func_to_spec(ModeInfo, Var, Name,
> +            AllArgVars, BadArgVars)
> +    ;
>          ModeError = mode_error_conj(Errors, Culprit),
>          Spec = mode_error_conj_to_spec(ModeInfo, Errors, Culprit)
>      ;
> @@ -1118,6 +1130,44 @@ mode_error_unify_var_functor_to_spec(ModeInfo, X, ConsId, Args,
>  
>  %-----------------------------------------------------------------------------%
>  
> +:- func mode_error_nondefault_mode_func_to_spec(mode_info, prog_var,
> +    cons_id, list(prog_var), one_or_more(prog_var)) = error_spec.
> +
> +mode_error_nondefault_mode_func_to_spec(ModeInfo, X, ConsId,
> +        AllArgVars, BadArgVars) = Spec :-
> +    Preamble = mode_info_context_preamble(ModeInfo),
> +    mode_info_get_context(ModeInfo, Context),
> +    mode_info_get_varset(ModeInfo, VarSet),
> +    mode_info_get_module_info(ModeInfo, ModuleInfo),
> +    FunctorConsIdStr = functor_cons_id_to_string(ModuleInfo, VarSet,
> +        print_name_only, ConsId, AllArgVars),
> +    BadArgVars = one_or_more(HeadBadArgVar, TailBadArgVars),
> +    (
> +        TailBadArgVars = [],
> +        ArgPieces = [words("Argument"),
> +            quote(mercury_var_to_name_only(VarSet, HeadBadArgVar)),
> +            words("is a function")]
> +    ;
> +        TailBadArgVars = [_ | _],
> +        ArgPieces = [words("Arguments"),
> +            quote(mercury_vars_to_name_only(VarSet,
> +                [HeadBadArgVar | TailBadArgVars])),
> +            words("are functions")]
> +    ),
> +    Pieces = [words("mode error in unification of"),
> +        quote(mercury_var_to_name_only(VarSet, X)),
> +        words("and"), words_quote(FunctorConsIdStr), suffix("."), nl] ++
> +        ArgPieces ++ [words("whose argument modes and/or determinism"),
> +        words("differ from the default function signature"),
> +        words("(f(in, ..., in) = out is det)."),
> +        words("This is not allowed, because taking such arguments"),
> +        words("out of the term being constructed"),
> +        words("would lose their mode and determinism information."), nl],
> +    Spec = error_spec(severity_error, phase_mode_check(report_in_any_mode),
> +        [simple_msg(Context, [always(Preamble ++ Pieces)])]).

You can delete the comma after "allowed".

The information is not necessarily lost so I suggest replacing "would
lose" with "may lose" or "would likely lose".

> diff --git a/compiler/modecheck_unify.m b/compiler/modecheck_unify.m
> index 6a51d63..4ff5272 100644
> --- a/compiler/modecheck_unify.m
> +++ b/compiler/modecheck_unify.m
...
> +:- pred is_input_mode(mer_mode::in) is semidet.
> +
> +is_input_mode(Mode) :-
> +    (
> +        % The structural version of the input mode.
> +        Mode = (ground_inst -> ground_inst)
> +    ;
> +        % The named version of the input mode.
> +        Mode = in_mode
> +    ).
> +
> +:- pred is_output_mode(mer_mode::in) is semidet.
> +
> +is_output_mode(Mode) :-
> +    (
> +        % The structural version of the output mode.
> +        Mode = (free -> ground_inst)
> +    ;
> +        % The named version of the output mode.
> +        Mode = out_mode
> +    ).

Can you use mode_is_fully_input/output here?
Or perhaps move these to mode_util.m.

Peter



More information about the reviews mailing list