[m-rev.] diff: detect duplicate sub-modules

Fergus Henderson fjh at cs.mu.OZ.AU
Thu Nov 15 23:16:07 AEDT 2001


This is still does not completely enforce the rules in 
the Mercury language reference manual, e.g. it allows
more than one `:- include_module' declaration for the
same sub-module.  But it does catch the case which
Pete encountered.

I'm currently running a bootcheck, and will go ahead
and commit this if it passes the tests.

----------

Estimated hours taken: 2
Branches: main

compiler/modules.m:
	Report an error if a sub-module is declared as both
	a nested sub-module and a separate sub-module.

tests/invalid/Mmakefile:
tests/invalid/duplicate_module.m:
tests/invalid/duplicate_module_test.m:
tests/invalid/duplicate_module_test.err_exp:
	A regression test.

Workspace: /home/earth/fjh/ws-earth4/mercury
Index: compiler/modules.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/modules.m,v
retrieving revision 1.200
diff -u -d -r1.200 modules.m
--- compiler/modules.m	6 Nov 2001 15:21:06 -0000	1.200
+++ compiler/modules.m	15 Nov 2001 11:54:55 -0000
@@ -390,8 +390,10 @@
 
 	% Given a module (well, a list of items), split it into
 	% its constituent sub-modules, in top-down order.
-	% Report an error if the `implementation' section of a sub-module
-	% is contained inside the `interface' section of its parent module.
+	% Also do some error checking:
+	% - report an error if the `implementation' section of a sub-module
+	%   is contained inside the `interface' section of its parent module
+	% - check for modules declared as both nested and separate sub-modules.
 
 :- type module_list == list(pair(module_name, item_list)).
 
@@ -4301,17 +4303,9 @@
 	% the previous entry will be a dummy entry that we inserted
 	% after trying to read the source file and failing.
 	%
-	% XXX We could make some effort here to catch the case where a
-	% module is defined as both a separate sub-module and also
-	% as a nested sub-module.  However, that doesn't seem worthwhile,
-	% since not all such cases would arrive here anyway --
-	% it would be nice to catch that case but this is not the
-	% place to catch it.
-	% (Currently for that case we just ignore the file containing the
-	% separate sub-module.  Since we don't consider the
-	% file containing the separate sub-module to be part of the
-	% program's source, there's no duplicate definition, and thus
-	% no requirement to report any error message.)
+	% Note that the case where a module is defined as both a
+	% separate sub-module and also as a nested sub-module is
+	% caught in split_into_submodules.
 	%
 :- pred insert_into_deps_map(module_imports, deps_map, deps_map).
 :- mode insert_into_deps_map(in, in, out) is det.
@@ -5122,7 +5116,19 @@
 	{ InParentInterface = no },
 	split_into_submodules_2(ModuleName, Items0, InParentInterface,
 		Items, ModuleList),
-	{ require(unify(Items, []), "modules.m: items after end_module") }.
+	{ require(unify(Items, []), "modules.m: items after end_module") },
+	%
+	% check for modules declared as both nested and separate sub-modules
+	%
+	{ get_children(Items0, NestedSubmodules) },
+	{ assoc_list__keys(ModuleList, SeparateSubModules) },
+	{ Duplicates = set__intersect(set__list_to_set(NestedSubmodules),
+				set__list_to_set(SeparateSubModules)) },
+	( { set__empty(Duplicates) } ->
+		[]
+	;
+		report_duplicate_modules(Duplicates, Items0)
+	).
 
 :- pred split_into_submodules_2(module_name, item_list, bool, item_list,
 				module_list, io__state, io__state).
@@ -5244,6 +5250,10 @@
 	% Perhaps we should be a bit more strict about this, for
 	% example by only allowing one `:- implementation' section
 	% and one `:- interface' section for each module?
+	% (That is what the Mercury language reference manual mandates.
+	% On the other hand, it also says that top-level modules
+	% should only have one `:- interface' and one `:- implementation'
+	% section, and we don't enforce that either...)
 	%
 	( map__search(SubModules0, ModuleName, ItemList0) ->
 		list__append(ModuleItemList, ItemList0, ItemList),
@@ -5278,6 +5288,50 @@
 	prog_out__write_context(Context),
 	io__write_string(
 		"  occurs in interface section of parent module.\n"),
+	io__set_exit_status(1).
+
+:- pred report_duplicate_modules(set(module_name), item_list,
+		io__state, io__state).
+:- mode report_duplicate_modules(in, in, di, uo) is det.
+
+report_duplicate_modules(Duplicates, Items) -->
+	{ IsDuplicateError = (pred(SubModuleName - Context::out) is nondet :-
+		list__member(Item, Items),
+		Item = module_defn(_VarSet, ModuleDefn) - Context,
+		( ModuleDefn = module(SubModuleName)
+		; ModuleDefn = include_module(SubModuleNames),
+		  list__member(SubModuleName, SubModuleNames)
+		),
+		set__member(SubModuleName, Duplicates)
+	  ) },
+	{ solutions(IsDuplicateError, DuplicateErrors) },
+	list__foldl(report_error_duplicate_module_decl, DuplicateErrors).
+
+:- pred report_error_duplicate_module_decl(pair(module_name, prog_context),
+		io__state, io__state).
+:- mode report_error_duplicate_module_decl(in, di, uo) is det.
+
+report_error_duplicate_module_decl(ModuleName - Context) -->
+	{ ModuleName = qualified(ParentModule0, ChildModule0) ->
+		ParentModule = ParentModule0,
+		ChildModule = ChildModule0
+	;
+		error("report_error_duplicate_module_decl")
+	},
+	% The error message should look this this:
+	% foo.m:123: In module `foo':
+	% foo.m:123:   error: sub-module `bar' declared as both
+	% foo.m:123:   a separate sub-module and a nested sub-module.
+	prog_out__write_context(Context),
+	io__write_string("In module `"),
+	prog_out__write_sym_name(ParentModule),
+	io__write_string("':\n"),
+	prog_out__write_context(Context),
+	io__write_string("  error: sub-module `"),
+	io__write_string(ChildModule),
+	io__write_string("' declared as both\n"),
+	prog_out__write_context(Context),
+	io__write_string("  a separate sub-module and a nested sub-module.\n"),
 	io__set_exit_status(1).
 
 	% Given a module (well, a list of items), extract the interface
Index: tests/invalid/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/Mmakefile,v
retrieving revision 1.100
diff -u -d -r1.100 Mmakefile
--- tests/invalid/Mmakefile	28 Oct 2001 12:25:58 -0000	1.100
+++ tests/invalid/Mmakefile	15 Nov 2001 12:03:49 -0000
@@ -10,6 +10,9 @@
 # Note: multi-module tests (including tests of nested modules)
 # need to be listed separately from single-module tests, since
 # we need to make dependencies only for multimodule tests.
+# However, multi-module tests where the error is detected when
+# building the dependencies (e.g. duplicate_module_test.m) should 
+# not be included in this list; we handle those specially (see below).
 
 MULTIMODULE_SOURCES= \
 	aditi_errors.m \
@@ -37,6 +40,7 @@
 	constructor_warning.m \
 	det_errors.m \
 	duplicate_modes.m \
+	duplicate_module_test.m \
 	errors.m \
 	errors1.m \
 	errors2.m \
@@ -179,9 +183,10 @@
 MCFLAGS-undef_mod_qual = 	--no-intermodule-optimization
 MCFLAGS-undef_symbol = 		--no-intermodule-optimization
 
-# The bug is caught when generating dependencies, so it is
-# easiest just to do that step.
+# For these test cases, the bug is caught when generating dependencies,
+# so it is easiest just to do that step.
 MCFLAGS-nested_impl_in_int =	--generate-dependencies
+MCFLAGS-duplicate_module_test =	--generate-dependencies
 
 MULTIMODULE_DEPENDS=	$(MULTIMODULE_SOURCES:%.m=%.depend)
 
Index: tests/invalid/duplicate_module.m
===================================================================
RCS file: tests/invalid/duplicate_module.m
diff -N tests/invalid/duplicate_module.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/invalid/duplicate_module.m	15 Nov 2001 12:04:26 -0000
@@ -0,0 +1,80 @@
+% Test of modules declared as both nested and separate modules.
+% This is referenced from duplicate_module_test.m.
+
+:- module duplicate_module.
+:- interface.
+:- import_module io.
+
+:- pred do_main(io__state::di, io__state::uo) is det.
+
+:- implementation.
+
+%-----------------------------------------------------------------------------%
+
+:- include_module duplicate_module:child.
+
+:- module duplicate_module: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("duplicate_module:child:hello\n").
+
+:- end_module duplicate_module:child.
+
+%-----------------------------------------------------------------------------%
+
+:- include_module child2.
+
+:- module duplicate_module:child2.
+:- interface.
+:- import_module io.
+
+:- type foo ---> bar ; baz(int).
+
+:- pred hello(io__state::di, io__state::uo) is det.
+
+:- implementation.
+
+hello --> io__write_string("duplicate_module:child2:hello\n").
+
+:- end_module duplicate_module:child2.
+
+%-----------------------------------------------------------------------------%
+
+:- include_module duplicate_module:child3.
+
+:- module child3.
+:- interface.
+:- import_module io.
+
+:- type foo ---> bar ; baz(int).
+
+:- pred hello(io__state::di, io__state::uo) is det.
+
+:- implementation.
+
+hello --> io__write_string("duplicate_module:child2:hello\n").
+
+:- end_module child3.
+
+%-----------------------------------------------------------------------------%
+
+% now we're back in the parent module.
+
+:- use_module duplicate_module:child.
+:- use_module duplicate_module:child2.
+:- use_module duplicate_module:child3.
+:- import_module std_util, require.
+
+do_main -->
+	duplicate_module:child:hello,
+	duplicate_module:child2:hello,
+	duplicate_module:child3:hello.
+
+:- end_module duplicate_module.
Index: tests/invalid/duplicate_module_test.err_exp
===================================================================
RCS file: tests/invalid/duplicate_module_test.err_exp
diff -N tests/invalid/duplicate_module_test.err_exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/invalid/duplicate_module_test.err_exp	15 Nov 2001 12:08:12 -0000
@@ -0,0 +1,19 @@
+duplicate_module.m:014: In module `duplicate_module':
+duplicate_module.m:014:   error: sub-module `child' declared as both
+duplicate_module.m:014:   a separate sub-module and a nested sub-module.
+duplicate_module.m:016: In module `duplicate_module':
+duplicate_module.m:016:   error: sub-module `child' declared as both
+duplicate_module.m:016:   a separate sub-module and a nested sub-module.
+duplicate_module.m:032: In module `duplicate_module':
+duplicate_module.m:032:   error: sub-module `child2' declared as both
+duplicate_module.m:032:   a separate sub-module and a nested sub-module.
+duplicate_module.m:034: In module `duplicate_module':
+duplicate_module.m:034:   error: sub-module `child2' declared as both
+duplicate_module.m:034:   a separate sub-module and a nested sub-module.
+duplicate_module.m:050: In module `duplicate_module':
+duplicate_module.m:050:   error: sub-module `child3' declared as both
+duplicate_module.m:050:   a separate sub-module and a nested sub-module.
+duplicate_module.m:052: In module `duplicate_module':
+duplicate_module.m:052:   error: sub-module `child3' declared as both
+duplicate_module.m:052:   a separate sub-module and a nested sub-module.
+For more information, try recompiling with `-E'.
Index: tests/invalid/duplicate_module_test.m
===================================================================
RCS file: tests/invalid/duplicate_module_test.m
diff -N tests/invalid/duplicate_module_test.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/invalid/duplicate_module_test.m	15 Nov 2001 12:05:16 -0000
@@ -0,0 +1,10 @@
+% Test of modules declared as both nested and separate modules.
+% The error occurs in the imported module duplicate_module.m,
+% but it is detected when making the dependencies for this module.
+:- module duplicate_module_test.
+:- interface.
+:- import_module io.
+:- pred main(io::di, io::uo) is det.
+:- implementation.
+:- import_module duplicate_module.
+main --> duplicate_module__do_main.

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
The University of Melbourne         |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
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