diff: nested modules: check for import of inaccessible modules
Fergus Henderson
fjh at cs.mu.OZ.AU
Thu Mar 19 04:29:50 AEDT 1998
Estimated hours taken: 4
Fix a limitation of the current nested module support:
ensure that we check for attempts to import inaccessible modules.
compiler/modules.m:
Check for attempts to import inaccessible modules
(modules whose parent has not been imported,
or for which there is no `include_module'
declaration in the parent's interface).
Also export make_pseudo_decl/2, for use by intermod.m.
compiler/intermod.m:
Modify the way we import `.opt' files. The previous method
temporarily set the items field of the module_imports
structure to [], but check_module_accessibility relies
on the assumption that the items field contains the
items for all modules previously read in.
tests/invalid/Mmakefile:
tests/invalid/test_nested.m:
tests/invalid/test_nested.err_exp:
tests/invalid/parent.m:
tests/invalid/parent.private_child.m:
tests/invalid/parent.public_child.m:
tests/invalid/parent.undeclared_child.m:
tests/invalid/parent.undeclared_child.err_exp:
tests/invalid/parent2.m:
tests/invalid/parent2.child.m:
Add some tests for the above change.
doc/reference_manual.texi:
Update the "bugs and limitations" sub-section of the modules
chapter to reflect the new status quo.
cvs diff -N compiler/intermod.m compiler/modules.m doc/reference_manual.texi tests/invalid/Mmakefile tests/invalid/parent.m tests/invalid/parent.private_child.m tests/invalid/parent.public_child.m tests/invalid/parent.undeclared_child.err_exp tests/invalid/parent.undeclared_child.m tests/invalid/parent2.child.m tests/invalid/parent2.m tests/invalid/test_nested.err_exp tests/invalid/test_nested.m
Index: compiler/intermod.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/intermod.m,v
retrieving revision 1.48
diff -u -r1.48 intermod.m
--- intermod.m 1998/03/18 08:07:36 1.48
+++ intermod.m 1998/03/18 16:27:13
@@ -1234,20 +1234,29 @@
% Read in and process the optimization interfaces.
intermod__grab_optfiles(Module0, Module, FoundError) -->
- %
- % Let make_hlds know the opt_imported stuff is coming.
- %
- { append_pseudo_decl(Module0, opt_imported, Module1) },
- { module_imports_get_items(Module1, Items0) },
%
% Read in the .opt files for imported and ancestor modules.
%
- { Module1 = module_imports(ModuleName, Ancestors0, InterfaceDeps0,
+ { Module0 = module_imports(ModuleName, Ancestors0, InterfaceDeps0,
ImplementationDeps0, _, _, _, _, _) },
{ list__condense([Ancestors0, InterfaceDeps0, ImplementationDeps0],
OptFiles) },
read_optimization_interfaces(OptFiles, [], OptItems, no, OptError),
+
+ %
+ % Append the items to the current item list, using
+ % a `opt_imported' psuedo-declaration to let
+ % make_hlds know the opt_imported stuff is coming.
+ %
+ { module_imports_get_items(Module0, Items0) },
+ { make_pseudo_decl(opt_imported, OptImportedDecl) },
+ { list__append(Items0, [OptImportedDecl | OptItems], Items1) },
+ { module_imports_set_items(Module0, Items1, Module1) },
+
+ %
+ % Figure out which .int files are needed by the .opt files
+ %
{ get_dependencies(OptItems, NewImportDeps0, NewUseDeps0) },
{ list__append(NewImportDeps0, NewUseDeps0, NewDeps0) },
{ set__list_to_set(NewDeps0, NewDepsSet0) },
@@ -1258,12 +1267,10 @@
% Read in the .int, and .int2 files needed by the .opt files.
% (XXX do we also need to read in .int0 files here?)
%
- { module_imports_set_items(Module1, [], Module2) },
process_module_long_interfaces(NewDeps, ".int", [], NewIndirectDeps,
- Module2, Module3),
+ Module1, Module2),
process_module_indirect_imports(NewIndirectDeps, ".int2",
- Module3, Module4),
- { module_imports_get_items(Module4, InterfaceItems) },
+ Module2, Module3),
%
% Get the :- pragma unused_args(...) declarations created
@@ -1280,28 +1287,25 @@
Item = pragma(PragmaType) - _,
PragmaType = unused_args(_,_,_,_,_)
)) },
- { list__filter(IsPragmaUnusedArgs, LocalItems, PragmaItems) }
+ { list__filter(IsPragmaUnusedArgs, LocalItems, PragmaItems) },
+
+ { module_imports_get_items(Module3, Items3) },
+ { list__append(Items3, PragmaItems, Items) },
+ { module_imports_set_items(Module3, Items, Module) }
;
- { PragmaItems = [] },
+ { Module = Module3 },
{ UAError = no }
),
%
% Figure out whether anything went wrong
%
- { module_imports_get_error(Module4, FoundError0) },
+ { module_imports_get_error(Module, FoundError0) },
{ ( FoundError0 \= no ; OptError = yes ; UAError = yes) ->
FoundError = yes
;
FoundError = no
- },
-
- %
- % Concatenate everything together.
- %
- { list__condense([Items0, PragmaItems, OptItems, InterfaceItems],
- Items) },
- { module_imports_set_items(Module4, Items, Module) }.
+ }.
:- pred read_optimization_interfaces(list(module_name)::in, item_list::in,
item_list::out, bool::in, bool::out,
Index: compiler/modules.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/modules.m,v
retrieving revision 1.63
diff -u -r1.63 modules.m
--- modules.m 1998/03/18 08:07:43 1.63
+++ modules.m 1998/03/18 16:28:09
@@ -194,6 +194,13 @@
module_imports).
:- mode module_imports_set_indirect_deps(in, in, out) is det.
+ % make an item_and_context for a module declaration
+ % or pseudo-declaration such as `:- imported'
+ % (which is inserted by the compiler, but can't be used
+ % in user code).
+:- pred make_pseudo_decl(module_defn, item_and_context).
+:- mode make_pseudo_decl(in, out) is det.
+
% append_pseudo_decl(Module0, PseudoDecl, Module):
% append the specified module declaration to the list
% of items in Module0 to give Module.
@@ -1022,8 +1029,6 @@
IndirectDeps, PublicChildren, FactDeps,
Items, Error).
-:- pred make_pseudo_decl(module_defn, item_and_context).
-:- mode make_pseudo_decl(in, out) is det.
make_pseudo_decl(PseudoDecl, Item) :-
term__context_init(Context),
varset__init(Varset),
@@ -2476,7 +2481,9 @@
{ ModImplementationImports = ModImplementationImports0 }
;
{ ModImplementationImports =
- [Import | ModImplementationImports0] }
+ [Import | ModImplementationImports0] },
+ check_module_accessibility(ModuleName, Import,
+ ModItems0)
),
{ get_dependencies(Items, IndirectImports1, IndirectUses1) },
{ list__append(IndirectImports0, IndirectImports1,
@@ -2492,6 +2499,114 @@
process_module_long_interfaces(Imports, Ext,
IndirectImports3, IndirectImports, Module1, Module)
).
+
+:- pred check_module_accessibility(module_name, module_name, item_list,
+ io__state, io__state).
+:- mode check_module_accessibility(in, in, in, di, uo) is det.
+
+check_module_accessibility(ModuleName, ImportedModule, Items) -->
+ ( { ImportedModule = qualified(ParentModule, SubModule) } ->
+ %
+ % Check that the imported/used module is accessible,
+ % by searching through the current item list (we should
+ % have already read in the imported module's parent module
+ % at this point, so the item list should include the items
+ % in the parent's interface) looking for an `include_module'
+ % declaration that names it.
+ %
+ (
+ { get_children(Items, AccessibleSubModules) },
+ { list__member(ImportedModule, AccessibleSubModules) }
+ ->
+ []
+ ;
+ % The user attempted to import an inaccessible
+ % sub-module, so report an error.
+ % Unfortunately we didn't get passed the
+ % context of the `import_module' or `use_module'
+ % declaration(s), so we need to search the item
+ % list again to find them.
+ { FindImports = lambda([Item::in] is semidet, (
+ Item = module_defn(_, ModuleDefn) - _,
+ ( ModuleDefn = import(module(Mods))
+ ; ModuleDefn = use(module(Mods))
+ ),
+ list__member(ImportedModule, Mods)
+ )) },
+ { list__filter(FindImports, Items, ImportItems) },
+ { ImportItems = [] ->
+ error("check_parent_module")
+ ;
+ true
+ },
+ list__foldl(report_inaccessible_module_error(
+ ModuleName, ParentModule, SubModule),
+ ImportItems)
+ )
+ ;
+ []
+ ).
+
+:- pred report_inaccessible_module_error(module_name, module_name, string,
+ item_and_context, io__state, io__state).
+:- mode report_inaccessible_module_error(in, in, in, in, di, uo) is det.
+
+/*
+The error message should come out like this
+(the second sentence is included only with --verbose-errors):
+very_long_name.m:123: In module `very_long_name':
+very_long_name.m:123: error in `import_module' declaration:
+very_long_name.m:123: module `parent_module:sub_module' is inaccessible.
+very_long_name.m:123: Either there was no `import_module' or `use_module'
+very_long_name.m:123: declaration importing module `parent_module',
+very_long_name.m:123: or the interface for module `parent_module'
+very_long_name.m:123: does not contain an `include_module' declaration
+very_long_name.m:123: for module `sub_module'.
+*/
+
+report_inaccessible_module_error(ModuleName, ParentModule, SubModule,
+ Item - Context) -->
+ { Item = module_defn(_, import(module(_))) ->
+ DeclName = "import_module"
+ ; Item = module_defn(_, use(module(_))) ->
+ DeclName = "use_module"
+ ;
+ error("report_inaccessible_parent_error: invalid item")
+ },
+ prog_out__write_context(Context),
+ io__write_string("In module `"),
+ prog_out__write_sym_name(ModuleName),
+ io__write_string("':\n"),
+ prog_out__write_context(Context),
+ io__write_strings([" error in `", DeclName, "' declaration:\n"]),
+ prog_out__write_context(Context),
+ io__write_string(" module `"),
+ prog_out__write_sym_name(qualified(ParentModule, SubModule)),
+ io__write_string("' is inaccessible.\n"),
+ globals__io_lookup_bool_option(verbose_errors, VerboseErrors),
+ ( { VerboseErrors = yes } ->
+ prog_out__write_context(Context),
+ io__write_string(" Either there was no `import_module' or "),
+ io__write_string("`use_module'\n"),
+ prog_out__write_context(Context),
+ io__write_string(" declaration to import module `"),
+ prog_out__write_sym_name(ParentModule),
+ io__write_string("',\n"),
+ prog_out__write_context(Context),
+ io__write_string(" or the interface for module `"),
+ prog_out__write_sym_name(ParentModule),
+ io__write_string("\n"),
+ prog_out__write_context(Context),
+ io__write_strings([" does not contain an `include_module' ",
+ "declaration\n"]),
+ prog_out__write_context(Context),
+ io__write_string(" for module `"),
+ io__write_string(SubModule),
+ io__write_string("'.\n")
+ ;
+ []
+ ),
+ io__set_exit_status(1).
%-----------------------------------------------------------------------------%
Index: doc/reference_manual.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/reference_manual.texi,v
retrieving revision 1.89
diff -u -r1.89 reference_manual.texi
--- reference_manual.texi 1998/03/18 07:58:45 1.89
+++ reference_manual.texi 1998/03/18 17:18:11
@@ -2702,12 +2702,9 @@
@itemize @bullet
@item it supports only separate sub-modules, not nested sub-modules;
- at item module qualifiers must use the fully-qualified module name;
- at item the compiler doesn't check that the parent module is imported/used
- before allowing import/use of its sub-modules;
- at item it doesn't check that there is an include_module declaration in the
- parent for each module claiming to be a child of that parent;
- at item privacy of private sub-modules is not enforced
+ at item if you mix an @samp{import_module} declaration for a parent module
+ with a @samp{use_module} declaration for the child, or vice versa,
+ then the compiler may report some spurious errors.
@end itemize
@node Type classes
Index: tests/invalid/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/Mmakefile,v
retrieving revision 1.15
diff -u -r1.15 Mmakefile
--- Mmakefile 1998/03/18 10:22:12 1.15
+++ Mmakefile 1998/03/18 16:55:31
@@ -38,6 +38,7 @@
prog_io_erroneous.m \
qual_basic_test2.m \
qualified_cons_id2.m \
+ test_nested.m \
type_inf_loop.m \
type_loop.m \
type_mismatch.m \
@@ -53,6 +54,7 @@
vars_in_wrong_places.m
# we do not yet pass the following tests:
+# parent.undeclared_child.m
# freefree.m (need bromage's aliasing stuff)
# no_exports.m (this is really a WISHLIST item)
#
@@ -83,7 +85,9 @@
errs: $(ERRS)
-depend:
+# we only need to make the dependencies for test cases consisting of
+# multiple modules; currently the only such test case is test_nested.m
+depend: test_nested.depend
clean:
rm -f *.c *.o *.err *.err2 *.d *.err_res
Index: tests/invalid/parent.m
===================================================================
RCS file: parent.m
diff -N parent.m
--- /dev/null Thu Mar 19 04:25:16 1998
+++ parent.m Thu Mar 19 03:44:05 1998
@@ -0,0 +1,10 @@
+% This is part of the test_nested.m test case.
+
+:- module parent.
+:- interface.
+
+:- include_module public_child.
+
+:- implementation.
+
+:- include_module private_child.
Index: tests/invalid/parent.private_child.m
===================================================================
RCS file: parent.private_child.m
diff -N parent.private_child.m
--- /dev/null Thu Mar 19 04:25:16 1998
+++ parent.private_child.m Thu Mar 19 03:40:20 1998
@@ -0,0 +1,11 @@
+:- module parent:private_child.
+:- interface.
+:- import_module io.
+
+:- type foo ---> bar ; baz(int).
+
+:- pred hello(io__state::di, io__state::uo) is det.
+
+:- implementation.
+
+hello --> io__write_string("parent:private_child:hello\n").
Index: tests/invalid/parent.public_child.m
===================================================================
RCS file: parent.public_child.m
diff -N parent.public_child.m
--- /dev/null Thu Mar 19 04:25:16 1998
+++ parent.public_child.m Thu Mar 19 03:40:20 1998
@@ -0,0 +1,11 @@
+:- module parent:public_child.
+:- interface.
+:- import_module io.
+
+:- type foo ---> bar ; baz(int).
+
+:- pred hello(io__state::di, io__state::uo) is det.
+
+:- implementation.
+
+hello --> io__write_string("parent:public_child:hello\n").
Index: tests/invalid/parent.undeclared_child.err_exp
===================================================================
RCS file: parent.undeclared_child.err_exp
diff -N parent.undeclared_child.err_exp
--- /dev/null Thu Mar 19 04:25:16 1998
+++ parent.undeclared_child.err_exp Thu Mar 19 03:58:08 1998
@@ -0,0 +1,4 @@
+parent.undeclared_child.m:004: Error in module `parent:undeclared_child':
+parent.undeclared_child.m:004: module `parent' has no `include_module'
+parent.undeclared_child.m:004: declaration for module `undeclared_child'.
+For more information, try recompiling with `-E'.
Index: tests/invalid/parent.undeclared_child.m
===================================================================
RCS file: parent.undeclared_child.m
diff -N parent.undeclared_child.m
--- /dev/null Thu Mar 19 04:25:16 1998
+++ parent.undeclared_child.m Thu Mar 19 03:45:22 1998
@@ -0,0 +1,14 @@
+% This module is invalid, because there is no `include_module' declaration
+% for it in parent.m.
+
+:- module parent:undeclared_child.
+:- interface.
+:- import_module io.
+
+:- type foo ---> bar ; baz(int).
+
+:- pred hello(io__state::di, io__state::uo) is det.
+
+:- implementation.
+
+hello --> io__write_string("parent:undeclared_child:hello\n").
Index: tests/invalid/parent2.child.m
===================================================================
RCS file: parent2.child.m
diff -N parent2.child.m
--- /dev/null Thu Mar 19 04:25:16 1998
+++ parent2.child.m Thu Mar 19 03:43:10 1998
@@ -0,0 +1,10 @@
+% This is part of the test_nested.m test case.
+
+:- module parent2:child.
+:- interface.
+
+:- type foo.
+
+:- implementation.
+
+:- type foo == int.
Index: tests/invalid/parent2.m
===================================================================
RCS file: parent2.m
diff -N parent2.m
--- /dev/null Thu Mar 19 04:25:16 1998
+++ parent2.m Thu Mar 19 03:43:37 1998
@@ -0,0 +1,6 @@
+% This is part of the test_nested.m test case.
+
+:- module parent2.
+:- interface.
+
+:- include_module child.
Index: tests/invalid/test_nested.err_exp
===================================================================
RCS file: test_nested.err_exp
diff -N test_nested.err_exp
--- /dev/null Thu Mar 19 04:25:16 1998
+++ test_nested.err_exp Thu Mar 19 03:55:58 1998
@@ -0,0 +1,12 @@
+test_nested.m:009: In module `test_nested':
+test_nested.m:009: error in `use_module' declaration:
+test_nested.m:009: module `parent:private_child' is inaccessible.
+test_nested.m:010: In module `test_nested':
+test_nested.m:010: error in `use_module' declaration:
+test_nested.m:010: module `parent:undeclared_child' is inaccessible.
+test_nested.m:013: In module `test_nested':
+test_nested.m:013: error in `use_module' declaration:
+test_nested.m:013: module `parent2:child' is inaccessible.
+test_nested.m:015: In definition of type `test_nested:foo'/0:
+test_nested.m:015: error: undefined type `parent:nonexistent_child:foo'/0.
+For more information, try recompiling with `-E'.
Index: tests/invalid/test_nested.m
===================================================================
RCS file: test_nested.m
diff -N test_nested.m
--- /dev/null Thu Mar 19 04:25:16 1998
+++ test_nested.m Thu Mar 19 03:46:04 1998
@@ -0,0 +1,23 @@
+:- module test_nested.
+:- interface.
+
+:- type foo.
+
+:- implementation.
+:- use_module parent.
+:- use_module parent:public_child.
+:- use_module parent:private_child.
+:- use_module parent:undeclared_child.
+% :- use_module parent:nonexistent_child.
+
+:- use_module parent2:child.
+
+:- type foo --->
+ foo(
+ parent:public_child:foo,
+ parent:private_child:foo,
+ parent:undeclared_child:foo,
+ parent:nonexistent_child:foo,
+ parent2:child:foo
+ ).
+
--
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