[m-rev.] for review: Allow external files to be included in foreign_decl and foreign_code.

Paul Bone paul at bone.id.au
Sat Mar 15 15:59:48 AEDT 2014


On Thu, Mar 13, 2014 at 03:28:17PM +1100, Peter Wang wrote:
> 
> compiler/file_names.m:
> 	Add a convenience to get a module's source file name.
> 

Missing word?

> 	Add make_include_file_path.
> 
> 	Reorder a predicate.

reorder it's contents or it's position with respect to other predicates?

> diff --git a/compiler/elds_to_erlang.m b/compiler/elds_to_erlang.m
> index c91151b..3a9b808 100644
> --- a/compiler/elds_to_erlang.m
> +++ b/compiler/elds_to_erlang.m
> @@ -29,7 +30,8 @@
>      % and exported foreign_decls to the corresponding .hrl file.
>      % The file names are determined by the module name.
>      %
> -:- pred output_elds(module_info::in, elds::in, io::di, io::uo) is det.
> +:- pred output_elds(module_info::in, elds::in, bool::out, io::di, io::uo)
> +    is det.

Would it be better to create a new type to represent the meaning of the bool
output.  I guess this is for success/failure:

    :- type success
        --->    success
        ;       failure.

Although this is equivalent to bool it allows someone reading the
code to clearly see if true means success or if true meas failure - by not
using true/false or yes/no at all.

> diff --git a/compiler/make.module_dep_file.m b/compiler/make.module_dep_file.m
> index bc4ea81..8c2bb40 100644
> --- a/compiler/make.module_dep_file.m
> +++ b/compiler/make.module_dep_file.m
> @@ -63,6 +63,22 @@
>  :- import_module term.
>  :- import_module term_io.
>  
> +    % The version 1 module_dep file format is the same as version 2 except that
> +    % it does not include a list of files included by `pragma foreign_decl' and
> +    % `pragma foreign_code'. We continue to write version 1 files when
> +    % possible.
> +    %

Why bother?  I don't think that there's a need to share .dep files between
Mercury versions, so it's be simpler to always use version 2 files.

> @@ -397,12 +450,42 @@ read_module_dependencies_2(Globals, RebuildModuleDeps, SearchDirs, ModuleName,
>          SearchResult, !IO),
>      (
>          SearchResult = ok(ModuleDir),
> -        parser.read_term(ImportsTermResult, !IO),
> +        parser.read_term(TermResult, !IO),
>          io.set_input_stream(OldInputStream, ModuleDepStream, !IO),
>          io.close_input(ModuleDepStream, !IO),
>          (
> -            ImportsTermResult = term(_, ImportsTerm),
> -            ImportsTerm = term.functor(term.atom("module"), ModuleArgs, _),
> +            TermResult = term(_, Term),
> +            read_module_dependencies_3(Globals, SearchDirs, ModuleName,
> +                ModuleDir, Term, Result, !Info, !IO)
> +        ;
> +            TermResult = eof,
> +            Result = error("unexpected eof")
> +        ;
> +            TermResult = error(ParseError, _),
> +            Result = error("parse error: " ++ ParseError)
> +        ),
> +        (
> +            Result = ok
> +        ;
> +            Result = error(Msg),
> +            read_module_dependencies_remake(Globals, RebuildModuleDeps,
> +                ModuleName, Msg, !Info, !IO)
> +        )
> +    ;
> +        SearchResult = error(_),
> +        % XXX should use the error message.
> +        read_module_dependencies_remake(Globals, RebuildModuleDeps, ModuleName,
> +            "couldn't find `.module_dep' file", !Info, !IO)
> +    ).

Did you forget this before committing?

> diff --git a/compiler/mercury_compile.m b/compiler/mercury_compile.m
> index 82626d3..1d51907 100644
> --- a/compiler/mercury_compile.m
> +++ b/compiler/mercury_compile.m
> @@ -1654,18 +1659,24 @@ mercury_compile_after_front_end(NestedSubModules, FindTimestampFiles,
>              % than stdout.
>              io.stdout_stream(Stdout, !IO),
>              output_x86_64_asm(Stdout, X86_64_Asm, !IO),
> +            Succeeded = yes,
>              ExtraObjFiles = []
>          ;
>              Target = target_erlang,
>              erlang_backend(!.HLDS, ELDS, !DumpInfo, !IO),
> -            elds_to_erlang(!.HLDS, ELDS, !IO),
> +            elds_to_erlang(!.HLDS, ELDS, Succeeded, !IO),
>              ExtraObjFiles = []
>          ),
> +        (
> +            Succeeded = yes,
>              recompilation.usage.write_usage_file(!.HLDS, NestedSubModules,
>                  MaybeTimestamps, !IO),
>              FindTimestampFiles(ModuleName, TimestampFiles, !IO),
>              list.foldl(touch_datestamp(Globals), TimestampFiles, !IO)
>          ;
> +            Succeeded = no
> +        )

I assume this error is raised elsewhere.  I'm not saying that this is wrong,
just that it looks suspicious and a comment could re-assure the reader of
the context doesn't make it clear enough.

Also this should probably not be a bool but a new type.

> diff --git a/compiler/mercury_compile_mlds_back_end.m b/compiler/mercury_compile_mlds_back_end.m
> index 25776f2..609dca4 100644
> --- a/compiler/mercury_compile_mlds_back_end.m
> +++ b/compiler/mercury_compile_mlds_back_end.m
> @@ -38,13 +38,17 @@
>  :- pred maybe_mark_static_terms(bool::in, bool::in,
>      module_info::in, module_info::out, io::di, io::uo) is det.
>  
> -:- pred mlds_to_high_level_c(globals::in, mlds::in, io::di, io::uo) is det.
> +:- pred mlds_to_high_level_c(globals::in, mlds::in, bool::out,
> +    io::di, io::uo) is det.
>  
> -:- pred mlds_to_java(module_info::in, mlds::in, io::di, io::uo) is det.
> +:- pred mlds_to_java(module_info::in, mlds::in, bool::out,
> +    io::di, io::uo) is det.
>  
> -:- pred mlds_to_csharp(module_info::in, mlds::in, io::di, io::uo) is det.
> +:- pred mlds_to_csharp(module_info::in, mlds::in, bool::out,
> +    io::di, io::uo) is det.
>  
> -:- pred mlds_to_il_assembler(globals::in, mlds::in, io::di, io::uo) is det.
> +:- pred mlds_to_il_assembler(globals::in, mlds::in, bool::out,
> +    io::di, io::uo) is det.
>  

More bools used as result types.

> diff --git a/compiler/mlds_to_c.m b/compiler/mlds_to_c.m
> index 8fa8d8a..20ef4af 100644
> --- a/compiler/mlds_to_c.m
> +++ b/compiler/mlds_to_c.m
> @@ -31,11 +31,12 @@
>  :- import_module libs.globals.
>  :- import_module ml_backend.mlds.
>  
> +:- import_module bool.
>  :- import_module io.
>  
>  %-----------------------------------------------------------------------------%
>  
> -    % output_c_mlds(MLDS, Globals, Suffix):
> +    % output_c_mlds(MLDS, Globals, Suffix, Succeeded):
>      %
>      % Output C code the the appropriate C file and C declarations to the
>      % appropriate header file. The file names are determined by the module
> @@ -43,28 +44,28 @@
>      % for debugging dumps. For normal output, the suffix should be the empty
>      % string.)
>      %
> -:- pred output_c_mlds(mlds::in, globals::in, string::in, io::di, io::uo)
> -    is det.
> +:- pred output_c_mlds(mlds::in, globals::in, string::in, bool::out,
> +    io::di, io::uo) is det.

And here.

> diff --git a/compiler/mlds_to_cs.m b/compiler/mlds_to_cs.m
> index e8d2659..aa43ebd 100644
> --- a/compiler/mlds_to_cs.m
> +++ b/compiler/mlds_to_cs.m
> @@ -32,11 +32,13 @@
>  :- import_module hlds.hlds_module.
>  :- import_module ml_backend.mlds.
>  
> +:- import_module bool.
>  :- import_module io.
>  
>  %-----------------------------------------------------------------------------%
>  
> -:- pred output_csharp_mlds(module_info::in, mlds::in, io::di, io::uo) is det.
> +:- pred output_csharp_mlds(module_info::in, mlds::in, bool::out,
> +    io::di, io::uo) is det.
>  

And in this file.

> diff --git a/compiler/mlds_to_ilasm.m b/compiler/mlds_to_ilasm.m
> index e17ba5b..6a08de6 100644
> --- a/compiler/mlds_to_ilasm.m
> +++ b/compiler/mlds_to_ilasm.m
> @@ -21,13 +21,15 @@
>  :- import_module libs.globals.
>  :- import_module ml_backend.mlds.
>  
> +:- import_module bool.
>  :- import_module io.
>  
>  %-----------------------------------------------------------------------------%
>  
>      % Convert the MLDS to IL and write it to a file.
>      %
> -:- pred output_mlds_via_ilasm(globals::in, mlds::in, io::di, io::uo) is det.
> +:- pred output_mlds_via_ilasm(globals::in, mlds::in, bool::out,
> +    io::di, io::uo) is det.
>  

And here.

> diff --git a/compiler/mlds_to_java.m b/compiler/mlds_to_java.m
> index fcf3b90..867a64c 100644
> --- a/compiler/mlds_to_java.m
> +++ b/compiler/mlds_to_java.m
> @@ -5239,14 +5268,16 @@ indent_line(N, !IO) :-
>      string::in, prog_context::in, io::di, io::uo) is det.
>  
>  write_string_with_context_block(Info, Indent, Code, Context, !IO) :-
> -    indent_line(Info, marker_begin_block, mlds_make_context(Context),
> -        Indent, !IO),
> +    indent_line_prog_context(Info, marker_begin_block, Context, Indent, !IO),
>      io.write_string(Code, !IO),
>      io.nl(!IO),
> +    % The num_lines(Code) call is supposed to count the number of lines
> +    % occupied by Code in the source file. The result will be incorrect if
> +    % there were any escape sequences representing CR or LF characters --
> +    % they are expanded out in Code.
>      Context = context(File, Lines0),
>      ContextEnd = context(File, Lines0 + num_lines(Code)),
> -    indent_line(Info, marker_end_block, mlds_make_context(ContextEnd),
> -        Indent, !IO).
> +    indent_line_prog_context(Info, marker_end_block, ContextEnd, Indent, !IO).

I can look into this.  I was wondering the line number markers which are
used by mfilterjavac are compatible with including foreign code from
external files, it looks like they are because the context includes File.

> diff --git a/compiler/prog_io_typeclass.m b/compiler/prog_io_typeclass.m
> index 5d19908..4d383a2 100644
> --- a/compiler/prog_io_typeclass.m
> +++ b/compiler/prog_io_typeclass.m
> @@ -30,7 +30,7 @@
>  
>      % Parse a typeclass declaration.
>      %
> -:- pred parse_typeclass(module_name::in, varset::in, list(term)::in,
> +:- pred parse_typeclass(module_name::in,varset::in, list(term)::in,
>      prog_context::in, int::in, maybe1(item_typeclass_info)::out) is semidet.
>  
>      % Parse an instance declaration.

Whitespace

> diff --git a/compiler/write_deps_file.m b/compiler/write_deps_file.m
> index bc7e0f9..33b6e77 100644
> --- a/compiler/write_deps_file.m
> +++ b/compiler/write_deps_file.m
> @@ -36,6 +36,9 @@
>      % `.trans_opt' file may depend on.  This is set to `no' if the
>      % dependency list is not available.
>      %
> +    % XXX we do not yet write dependencies on files referenced by pragma
> +    % foreign_decl or pragma foreign_code
> +    %
>  :- pred write_dependency_file(globals::in, module_and_imports::in,
>      set(module_name)::in, maybe(list(module_name))::in, io::di, io::uo) is det.
> 

Does this mean that if my pragma says use the file "foo.c" that mmc doesn't
know if foo.c depends upon bar.c?  That seems okay to me.  If I
misunderstand please let me know.

> diff --git a/doc/reference_manual.texi b/doc/reference_manual.texi
> index a7a34b2..525aa26 100644
> --- a/doc/reference_manual.texi
> +++ b/doc/reference_manual.texi
> @@ -9522,6 +9522,7 @@ The University of Melbourne Mercury implementation supports the following
>  extensions to the Mercury language:
>  
>  @menu
> +* Foreign include files::       Including foreign code from external files.
>  * Fact tables::                 Support for very large tables of facts.
>  @c XXX STM
>  @c The documentation of STM is commented out because its support is
> @@ -9545,6 +9546,29 @@ extensions to the Mercury language:
>  @c     implementation-specific...
>  @c * Reserved tag::		Support for Herbrand constraint solvers.
>  
> + at node Foreign include files
> + at section Foreign include files
> +
> +Foreign declarations and code may be included from external files
> +using extensions of the @samp{pragma foreign_decl} and
> + at samp{pragma foreign_code} declarations:

I'd say "...using the @samp{pragma foreign_decl}...".  This might be an
implementation specific extension to the Mercury language, but I don't think
it needs to be explained as an extension to this syntax.

> +
> + at example
> +:- pragma foreign_decl("@var{Lang}", include_file("@var{Path}")).
> +:- pragma foreign_decl("@var{Lang}", local, include_file("@var{Path}")).
> +:- pragma foreign_code("@var{Lang}", include_file("@var{Path}")).
> + at end example
> +
> +These have the same effects as the standard forms except that the
> +contents of the file referenced by @var{Path} are included in place of
> +the string literal in the last argument, without further interpretation.
> + at var{Path} may be an absolute path to a file or a path relative to the
> +directory that contains the source file of the module containing the
> +declaration. The interpretation of the path is platform-dependent.

This is a little hard to parse and readers my interpret it to mean that Path
is either a file (absolute path to a file) or a directory (when relative).
I suggest:

    @var{Path} refers to the file to be included during compilation, it may
    either be absolute or relative to the directory that contains the source
    file of the module containing the declaration.


> diff --git a/tests/mmc_make/Mmakefile b/tests/mmc_make/Mmakefile
> index 7fe6bc6..00dc31d 100644
> --- a/tests/mmc_make/Mmakefile
> +++ b/tests/mmc_make/Mmakefile
> @@ -30,6 +32,12 @@ include $(TESTS_DIR)/Mmake.common
>  
>  complex_test.log: install_libs
>  
> +# Check that included files are identified as dependencies of the target code.
> +include_file2.runtest: include_file
> +	sleep 1 && touch inc/code.c inc/code.java inc/code.cs inc/code.erl
> +	$(MCM) --verbose-make include_file > include_file2.err 2>&1
> +	grep '^Making Mercury/.*/include_file[.]' include_file2.err
> +

s/MCM/MMC ?

Please document why this should "sleep 1".

> diff --git a/tests/mmc_make/include_file.m b/tests/mmc_make/include_file.m
> new file mode 100644
> index 0000000..26fc20d
> --- /dev/null
> +++ b/tests/mmc_make/include_file.m
> @@ -0,0 +1,64 @@
> +:- module include_file.
> +:- interface.
> +
> +:- import_module io.
> +
> +:- pred main(io::di, io::uo) is det.
> +
> +:- implementation.
> +
> +:- pragma foreign_decl("C", include_file("inc/decl.h")).
> +:- pragma foreign_code("C", include_file("inc/code.c")).

That's cool, I like the way this feature works.


Thanks Peter, the rest of the patch looks good.


-- 
Paul Bone



More information about the reviews mailing list