[m-rev.] for review: emit errors about purity mismatches with foreign clauses

Ian MacLarty maclarty at cs.mu.OZ.AU
Thu Jul 6 10:32:34 AEST 2006


On Wed, Jul 05, 2006 at 05:54:08PM +1000, Julien Fischer wrote:
> 
> For review by anyone.
> 
> Estimated hours taken: 4
> Branches: main, release
> 
> Make it an error for the (promised) purity of a foreign clause to disagree
> with the declared purity of the corresponding predicate or function
> declaration.  We only perform this check in the absence of a
> promise_{pure,semipure} pragma for the predicate or function.
> 

What happens if the predicate is declared impure, but there is a pragma
promise_pure on the predicate?  Is that allowed?  If so what does it
mean?

> Previously this situation was sometimes picked up by purity analysis but not
> in all cases.  For example, if a predicate was declared impure but the
> foreign_proc was promised pure it wasn't reported.  In that particular case
> it was a problem because if the foreign_proc did not have any outputs, then
> simplify.m might have optimised its body away (which is how I noticed this).
> 
> compiler/add_pramga.m:
> 	In the absence of promise_{pure,semipure} pragmas emit error messages
> 	about mismatches between the declared purity of a procedure and the
> 	(promised) purity of a foreign clause for it.
> 
> library/private_builtin.m:
> library/solutions.m:
> 	Delete bogus purity promises from foreign_proc attributes reported by
> 	the new error.
> 
> tests/invalid/Mmakefile:
> tests/invalid/foreign_purity_mismatch.{m,err_exp}:
> 	Test case for the new error.
> 
> compiler/simplify.m:
> compiler/prog_io_pragma.m:
> 	Fix some formatting.
> 
> Julien.
> 
> Index: compiler/add_pragma.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/add_pragma.m,v
> retrieving revision 1.35
> diff -u -r1.35 add_pragma.m
> --- compiler/add_pragma.m	15 Jun 2006 19:36:57 -0000	1.35
> +++ compiler/add_pragma.m	5 Jul 2006 07:15:37 -0000
> @@ -1422,11 +1422,12 @@
>      PredModule = pred_info_module(!.PredInfo),
>      pred_info_clauses_info(!.PredInfo, Clauses0),
>      pred_info_get_purity(!.PredInfo, Purity),
> +    pred_info_get_markers(!.PredInfo, Markers),
> 
>      % Add the code for this `pragma import' to the clauses_info.
>      clauses_info_add_pragma_foreign_proc(Purity, Attributes, PredId, ProcId,
>          VarSet, PragmaVars, ArgTypes, PragmaImpl, Context, PredOrFunc,
> -        qualified(PredModule, PredName), Arity, Clauses0, Clauses,
> +        qualified(PredModule, PredName), Arity, Markers, Clauses0, Clauses,
>          !ModuleInfo, !IO),
> 
>      % Store the clauses_info etc. back into the pred_info.
> @@ -1573,10 +1574,11 @@
>                  pred_info_clauses_info(!.PredInfo, Clauses0),
>                  pred_info_get_arg_types(!.PredInfo, ArgTypes),
>                  pred_info_get_purity(!.PredInfo, Purity),
> +                pred_info_get_markers(!.PredInfo, Markers),
>                  clauses_info_add_pragma_foreign_proc(Purity, Attributes,
>                      PredId, ProcId, ProgVarSet, PVars, ArgTypes, PragmaImpl,
> -                    Context, PredOrFunc, PredName, Arity, Clauses0, Clauses,
> -                    !ModuleInfo, !IO),
> +                    Context, PredOrFunc, PredName, Arity, Markers,
> +                    Clauses0, Clauses, !ModuleInfo, !IO),
>                  pred_info_set_clauses_info(Clauses, !PredInfo),
>                  pred_info_update_goal_type(pragmas, !PredInfo),
>                  map.det_update(Preds0, PredId, !.PredInfo, Preds),
> @@ -2364,12 +2366,13 @@
>      pragma_foreign_proc_attributes::in, pred_id::in, proc_id::in,
>      prog_varset::in, list(pragma_var)::in, list(mer_type)::in,
>      pragma_foreign_code_impl::in, prog_context::in, pred_or_func::in,
> -    sym_name::in, arity::in, clauses_info::in, clauses_info::out,
> -    module_info::in, module_info::out, io::di, io::uo) is det.
> +    sym_name::in, arity::in, pred_markers::in,
> +    clauses_info::in, clauses_info::out, module_info::in, module_info::out,
> +    io::di, io::uo) is det.
> 
>  clauses_info_add_pragma_foreign_proc(Purity, Attributes0, PredId, ProcId,
>          PVarSet, PVars, OrigArgTypes, PragmaImpl0, Context, PredOrFunc,
> -        PredName, Arity, !ClausesInfo, !ModuleInfo, !IO) :-
> +        PredName, Arity, Markers, !ClausesInfo, !ModuleInfo, !IO) :-
> 
>      !.ClausesInfo = clauses_info(VarSet0, ExplicitVarTypes, TVarNameMap,
>          InferredVarTypes, HeadVars, ClauseRep, RttiVarMaps,
> @@ -2434,7 +2437,43 @@
>          % Build the foreign_proc.
>          goal_info_init(GoalInfo0),
>          goal_info_set_context(Context, GoalInfo0, GoalInfo1),
> +        %
> +        % Check that the purity of the predicate/function declaration agrees
> +        % with the (promised) purity of the foreign_proc.  It is only okay for
> +        % them to disagree if there is a `:- pragma promise_pure' or
> +        % `:- pramga promise_semipure' declaration for the predicate or
> +        % function.
> +        %

Could you explain why it's okay for them to disagree in this case?  In
fact I did a test and the compiler issues a warning if the pred is
declared impure, but there is a pragma promise_pure.  Actually there
seems to be another bug because while the compiler reports a
warning, it doesn't generate an executable and a non-zero exit status is
returned (even with --no-halt-at-warn).  Try compiling the following
program to see what I mean:

:- module pure.

:- interface.

:- import_module io.

:- pred main(io::di, io::uo) is det.

:- implementation.

main(!IO) :-
        nl(!IO).

:- pragma promise_pure(p/0).

:- impure pred p is det.

:- pragma foreign_proc("C",
        p,
        [will_not_call_mercury],
"
        printf(\"hello\");
").

> +        (
> +            ( check_marker(Markers, promised_pure)
> +            ; check_marker(Markers, promised_semipure)
> +            )
> +        ->
> +            true
> +        ;
> +            ForeignAttributePurity = purity(Attributes),
> +            (
> +                ForeignAttributePurity \= Purity
> +            ->
> +                purity_name(ForeignAttributePurity, ForeignAttributePurityStr),
> +                purity_name(Purity, PurityStr),
> +                ErrorMsg = [
> +                    words("Error: foreign clause for"),
> +                    pred_or_func(PredOrFunc),
> +                    sym_name_and_arity(PredName / Arity),
> +                    words("has purity " ++ ForeignAttributePurityStr),
> +                    words("but that"), pred_or_func(PredOrFunc),
> +                    words("has been declared " ++ PurityStr), suffix(".")
> +                ],
> +                write_error_pieces(Context, 0, ErrorMsg, !IO),
> +                io.set_exit_status(1, !IO)
> +            ;
> +                true
> +            )
> +        ),
> +        %
>          % Put the purity in the goal_info in case this foreign code is inlined.
> +        %
>          add_goal_info_purity_feature(Purity, GoalInfo1, GoalInfo),
>          make_foreign_args(HeadVars, ArgInfo, OrigArgTypes, ForeignArgs),
>          HldsGoal0 = foreign_proc(Attributes, PredId, ProcId, ForeignArgs, [],
> Index: compiler/prog_io_pragma.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/prog_io_pragma.m,v
> retrieving revision 1.105
> diff -u -r1.105 prog_io_pragma.m
> --- compiler/prog_io_pragma.m	15 Jun 2006 19:37:10 -0000	1.105
> +++ compiler/prog_io_pragma.m	5 Jul 2006 06:15:28 -0000
> @@ -5,12 +5,13 @@
>  % This file may only be copied under the terms of the GNU General
>  % Public License - see the file COPYING in the Mercury distribution.
>  %-----------------------------------------------------------------------------%
> -
> +%
>  % File: prog_io_pragma.m.
>  % Main authors: fjh, dgj.
> -
> +%
>  % This module handles the parsing of pragma directives.
> -
> +%
> +%-----------------------------------------------------------------------------%
>  %-----------------------------------------------------------------------------%
> 
>  :- module parse_tree.prog_io_pragma.
> Index: compiler/simplify.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/simplify.m,v
> retrieving revision 1.178
> diff -u -r1.178 simplify.m
> --- compiler/simplify.m	28 Jun 2006 04:46:17 -0000	1.178
> +++ compiler/simplify.m	5 Jul 2006 05:02:18 -0000
> @@ -5,10 +5,10 @@
>  % This file may only be copied under the terms of the GNU General
>  % Public License - see the file COPYING in the Mercury distribution.
>  %-----------------------------------------------------------------------------%
> -
> +%
>  % File: simplify.m.
>  % Main authors: zs, stayl.
> -
> +%
>  % The two jobs of the simplification module are
>  %
>  %   to find and exploit opportunities for simplifying the internal form
> @@ -27,6 +27,7 @@
>  % works properly.
>  %
>  %-----------------------------------------------------------------------------%
> +%-----------------------------------------------------------------------------%
> 
>  :- module check_hlds.simplify.
>  :- interface.
> Index: library/private_builtin.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/library/private_builtin.m,v
> retrieving revision 1.157
> diff -u -r1.157 private_builtin.m
> --- library/private_builtin.m	28 Jun 2006 04:46:19 -0000	1.157
> +++ library/private_builtin.m	5 Jul 2006 06:14:36 -0000
> @@ -1006,7 +1006,7 @@
> 
>  :- pragma foreign_proc("C",
>      free_heap(Val::di),
> -    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
> +    [will_not_call_mercury, thread_safe, will_not_modify_trail],
>  "
>      MR_free_heap((void *) Val);
>  ").
> Index: library/solutions.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/library/solutions.m,v
> retrieving revision 1.4
> diff -u -r1.4 solutions.m
> --- library/solutions.m	19 Apr 2006 05:17:56 -0000	1.4
> +++ library/solutions.m	5 Jul 2006 07:22:15 -0000
> @@ -630,26 +630,26 @@
> 
>  :- pragma foreign_proc("C",
>      partial_deep_copy(SolutionsHeapPtr::in, OldVal::in, NewVal::out),
> -    [will_not_call_mercury, thread_safe, promise_pure],
> +    [will_not_call_mercury, thread_safe],
>  "
>      MR_PARTIAL_DEEP_COPY(SolutionsHeapPtr, OldVal, NewVal, TypeInfo_for_T);
>  ").
>  :- pragma foreign_proc("C",
>      partial_deep_copy(SolutionsHeapPtr::in, OldVal::mdi, NewVal::muo),
> -    [will_not_call_mercury, thread_safe, promise_pure],
> +    [will_not_call_mercury, thread_safe],
>  "
>      MR_PARTIAL_DEEP_COPY(SolutionsHeapPtr, OldVal, NewVal, TypeInfo_for_T);
>  ").
>  :- pragma foreign_proc("C",
>      partial_deep_copy(SolutionsHeapPtr::in, OldVal::di, NewVal::uo),
> -    [will_not_call_mercury, thread_safe, promise_pure],
> +    [will_not_call_mercury, thread_safe],
>  "
>      MR_PARTIAL_DEEP_COPY(SolutionsHeapPtr, OldVal, NewVal, TypeInfo_for_T);
>  ").
> 
>  :- pragma foreign_proc("C#",
>      partial_deep_copy(_SolutionsHeapPtr::in, OldVal::in, NewVal::out),
> -    [will_not_call_mercury, thread_safe, promise_pure],
> +    [will_not_call_mercury, thread_safe],
>  "
>      //
>      // For the IL back-end, we don't do heap reclamation on failure,
> @@ -660,20 +660,20 @@
>  ").
>  :- pragma foreign_proc("C#",
>      partial_deep_copy(_SolutionsHeapPtr::in, OldVal::mdi, NewVal::muo),
> -    [will_not_call_mercury, thread_safe, promise_pure],
> +    [will_not_call_mercury, thread_safe],
>  "
>      NewVal = OldVal;
>  ").
>  :- pragma foreign_proc("C#",
>      partial_deep_copy(_SolutionsHeapPtr::in, OldVal::di, NewVal::uo),
> -    [will_not_call_mercury, thread_safe, promise_pure],
> +    [will_not_call_mercury, thread_safe],
>  "
>      NewVal = OldVal;
>  ").
> 
>  :- pragma foreign_proc("Java",
>      partial_deep_copy(_SolutionsHeapPtr::in, OldVal::in, NewVal::out),
> -    [will_not_call_mercury, thread_safe, promise_pure],
> +    [will_not_call_mercury, thread_safe],
>  "
>      /*
>      ** For the Java back-end, as for the .NET implementation,
> @@ -685,13 +685,13 @@
>  ").
>  :- pragma foreign_proc("Java",
>      partial_deep_copy(_SolutionsHeapPtr::in, OldVal::mdi, NewVal::muo),
> -    [will_not_call_mercury, thread_safe, promise_pure],
> +    [will_not_call_mercury, thread_safe],
>  "
>      NewVal = OldVal;
>  ").
>  :- pragma foreign_proc("Java",
>      partial_deep_copy(_SolutionsHeapPtr::in, OldVal::di, NewVal::uo),
> -    [will_not_call_mercury, thread_safe, promise_pure],
> +    [will_not_call_mercury, thread_safe],
>  "
>      NewVal = OldVal;
>  ").
> Index: tests/invalid/Mmakefile
> ===================================================================
> RCS file: /home/mercury1/repository/tests/invalid/Mmakefile,v
> retrieving revision 1.193
> diff -u -r1.193 Mmakefile
> --- tests/invalid/Mmakefile	16 Jun 2006 07:24:41 -0000	1.193
> +++ tests/invalid/Mmakefile	5 Jul 2006 05:49:22 -0000
> @@ -80,6 +80,7 @@
>  	ext_type \
>  	ext_type_bug \
>  	field_syntax_error \
> +	foreign_purity_mismatch \
>  	foreign_singleton \
>  	foreign_type_2 \
>  	foreign_type_visibility \
> Index: tests/invalid/foreign_purity_mismatch.err_exp
> ===================================================================
> RCS file: tests/invalid/foreign_purity_mismatch.err_exp
> diff -N tests/invalid/foreign_purity_mismatch.err_exp
> --- /dev/null	1 Jan 1970 00:00:00 -0000
> +++ tests/invalid/foreign_purity_mismatch.err_exp	5 Jul 2006 05:46:26 -0000
> @@ -0,0 +1,38 @@
> +foreign_purity_mismatch.m:020: Error: foreign clause for predicate
> +foreign_purity_mismatch.m:020:   `foreign_purity_mismatch.pure_with_impure'/1
> +foreign_purity_mismatch.m:020:   has purity impure but that predicate has been
> +foreign_purity_mismatch.m:020:   declared pure.
> +foreign_purity_mismatch.m:027: Error: foreign clause for predicate
> +foreign_purity_mismatch.m:027:   `foreign_purity_mismatch.pure_with_semipure'/1
> +foreign_purity_mismatch.m:027:   has purity semipure but that predicate has
> +foreign_purity_mismatch.m:027:   been declared pure.
> +foreign_purity_mismatch.m:034: Error: foreign clause for predicate
> +foreign_purity_mismatch.m:034:   `foreign_purity_mismatch.semipure_with_impure'/1
> +foreign_purity_mismatch.m:034:   has purity impure but that predicate has been
> +foreign_purity_mismatch.m:034:   declared semipure.
> +foreign_purity_mismatch.m:041: Error: foreign clause for predicate
> +foreign_purity_mismatch.m:041:   `foreign_purity_mismatch.semipure_with_pure'/1
> +foreign_purity_mismatch.m:041:   has purity pure but that predicate has been
> +foreign_purity_mismatch.m:041:   declared semipure.
> +foreign_purity_mismatch.m:048: Error: foreign clause for predicate
> +foreign_purity_mismatch.m:048:   `foreign_purity_mismatch.impure_with_pure'/1
> +foreign_purity_mismatch.m:048:   has purity pure but that predicate has been
> +foreign_purity_mismatch.m:048:   declared impure.
> +foreign_purity_mismatch.m:055: Error: foreign clause for predicate
> +foreign_purity_mismatch.m:055:   `foreign_purity_mismatch.impure_with_semipure'/1
> +foreign_purity_mismatch.m:055:   has purity semipure but that predicate has
> +foreign_purity_mismatch.m:055:   been declared impure.
> +foreign_purity_mismatch.m:006: In predicate
> +foreign_purity_mismatch.m:006:   `foreign_purity_mismatch.pure_with_impure/1':
> +foreign_purity_mismatch.m:006:   purity error: predicate is impure.
> +foreign_purity_mismatch.m:006:   It must be declared `impure' or promised pure.
> +foreign_purity_mismatch.m:007: In predicate
> +foreign_purity_mismatch.m:007:   `foreign_purity_mismatch.pure_with_semipure/1':
> +foreign_purity_mismatch.m:007:   purity error: predicate is semipure.
> +foreign_purity_mismatch.m:007:   It must be declared `semipure' or promised
> +foreign_purity_mismatch.m:007:   pure.
> +foreign_purity_mismatch.m:009: In predicate
> +foreign_purity_mismatch.m:009:   `foreign_purity_mismatch.semipure_with_impure/1':
> +foreign_purity_mismatch.m:009:   purity error: predicate is impure.
> +foreign_purity_mismatch.m:009:   It must be declared `impure' or promised
> +foreign_purity_mismatch.m:009:   semipure.
> Index: tests/invalid/foreign_purity_mismatch.m
> ===================================================================
> RCS file: tests/invalid/foreign_purity_mismatch.m
> diff -N tests/invalid/foreign_purity_mismatch.m
> --- /dev/null	1 Jan 1970 00:00:00 -0000
> +++ tests/invalid/foreign_purity_mismatch.m	5 Jul 2006 05:46:26 -0000
> @@ -0,0 +1,60 @@
> +:- module foreign_purity_mismatch.
> +:- interface.
> +
> +:- import_module string.
> +
> +:- pred pure_with_impure(string::in) is det.
> +:- pred pure_with_semipure(string::in) is det.
> +
> +:- semipure pred semipure_with_impure(string::in) is det.
> +:- semipure pred semipure_with_pure(string::in) is det.
> +
> +	% This one was particularly bad since the compiler was
> +	% optimising away the foreign_proc goal(!).
> +	%
> +:- impure pred impure_with_pure(string::in) is det.
> +:- impure pred impure_with_semipure(string::in) is det.
> +
> +:- implementation.
> +
> +:- pragma foreign_proc("C",
> +	pure_with_impure(S::in),
> +	[will_not_call_mercury],
> +"
> +	/* S */
> +").
> +
> +:- pragma foreign_proc("C",
> +	pure_with_semipure(S::in),
> +	[will_not_call_mercury, promise_semipure],
> +"
> +	/* S */
> +").
> +
> +:- pragma foreign_proc("C",
> +	semipure_with_impure(S::in),
> +	[will_not_call_mercury],
> +"
> +	/* S */
> +").
> +
> +:- pragma foreign_proc("C",
> +	semipure_with_pure(S::in),
> +	[will_not_call_mercury, promise_pure],
> +"
> +	/* S */
> +").
> +
> +:- pragma foreign_proc("C",
> +	impure_with_pure(S::in),
> +	[will_not_call_mercury, promise_pure],
> +"
> +	/* S */
> +").
> +
> +:- pragma foreign_proc("C",
> +	impure_with_semipure(S::in),
> +	[will_not_call_mercury, promise_semipure],
> +"
> +	/* S */
> +").
> 

Otherwise that looks fine.

Ian.
--------------------------------------------------------------------------
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