[m-rev.] for review: make sure that abstract instances have a corresponding concrete instance

Julien Fischer juliensf at cs.mu.OZ.AU
Wed Apr 13 15:01:28 AEST 2005


For review by anyone.

Estimated hours taken: 3
Branches: main, release

Have the compiler emit an error if an abstract instance declaration
in the interface of a module does not have a corresponding concrete
instance declaration in the implementation of the module.  Currently
the compiler just ignores this situation which leads to C compilation
errors in the MLDS grades and link-time errors in the LLDS grades.

compiler/check_typeclass.m:
	Emit an error message if an abstract instance declaration
	in the interface of a module does not have a corresponding
	concrete instance in the implementation.

	Use unexpected/2 in place of error/2.

	Fix the positioning of a few comments.

	Add an end_module declaration.

compiler/hlds_data.m:
	Make the instance_table type a multi_map (which it
	already effectively was).

tests/invalid/Mmakefile:
tests/invalid/missing_concrete_instance.m:
tests/invalid/missing_concrete_instance.err_exp:
	Test case for the above.

Julien.

Workspace:/home/swordfish/juliensf/ws75
Index: compiler/check_typeclass.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/check_typeclass.m,v
retrieving revision 1.67
diff -u -r1.67 check_typeclass.m
--- compiler/check_typeclass.m	12 Apr 2005 07:58:14 -0000	1.67
+++ compiler/check_typeclass.m	13 Apr 2005 04:44:04 -0000
@@ -87,10 +87,12 @@
 :- import_module int.
 :- import_module list.
 :- import_module map.
+:- import_module multi_map.
 :- import_module require.
 :- import_module set.
 :- import_module std_util.
 :- import_module string.
+:- import_module svmulti_map.
 :- import_module svset.
 :- import_module term.
 :- import_module varset.
@@ -100,6 +102,7 @@
 check_typeclass__check_typeclasses(!QualInfo, !ModuleInfo, FoundError, !IO) :-
 	check_typeclass__check_instance_decls(!QualInfo, !ModuleInfo,
 		FoundInstanceError, !IO),
+ 	check_for_missing_concrete_instances(!ModuleInfo, !IO),
 	module_info_classes(!.ModuleInfo, ClassTable),
 	check_for_cyclic_classes(ClassTable, FoundCycleError, !IO),
 	FoundError = bool.or(FoundInstanceError, FoundCycleError).
@@ -145,7 +148,8 @@
 			qual_info	:: qual_info
 		).

-	% check all the instances of one class.
+	% Check all the instances of one class.
+	%
 :- pred check_one_class(class_table::in,
 	pair(class_id, list(hlds_instance_defn))::in,
 	pair(class_id, list(hlds_instance_defn))::out,
@@ -187,7 +191,8 @@
 			!CheckTCInfo, !IO)
 	).

-	% check one instance of one class
+	% Check one instance of one class.
+	%
 :- pred check_class_instance(class_id::in, list(prog_constraint)::in,
 	list(tvar)::in, hlds_class_interface::in, class_interface::in,
 	tvarset::in, list(pred_id)::in,
@@ -434,7 +439,8 @@
 		UnivCs = OtherUnivCs,
 		ClassContext = constraints(UnivCs, ExistCs)
 	;
-		error("check_instance_pred: no constraint on class method")
+		unexpected(this_file,
+			"check_instance_pred: no constraint on class method")
 	),

 	MethodName0 = pred_info_name(PredInfo),
@@ -844,7 +850,7 @@
 	( term__var_list_to_term_list(ClassVars1, ClassVarTerms) ->
 		ClassVars = ClassVars1
 	;
-		error("ClassVarTerms are not vars")
+		unexpected(this_file, "ClassVarTerms are not vars.")
 	),

 		% Calculate the bindings
@@ -917,6 +923,143 @@
 	string__append_list([", `", String0, "'", String1], String).

 %---------------------------------------------------------------------------%
+%
+% Check that every abstract instance in the interface of a module
+% has a corresponding concrete instance in the implementation.
+%
+
+:- pred check_for_missing_concrete_instances(
+	module_info::in, module_info::out, io::di, io::uo) is det.
+
+check_for_missing_concrete_instances(!ModuleInfo, !IO) :-
+	module_info_instances(!.ModuleInfo, InstanceTable),
+	%
+	% Grab all the abstract instance declarations in the interface
+	% of this module and all the concrete instances defined in the
+	% implementation.
+	%
+	gather_abstract_and_concrete_instances(InstanceTable,
+		AbstractInstances, ConcreteInstances),
+	map.foldl(check_for_corresponding_instances(ConcreteInstances),
+		AbstractInstances, !IO).
+
+	% gather_abstract_and_concrete_instances(Table,
+	% 	AbstractInstances, ConcreteInstances).
+	%
+	% Search the instance_table and create a table of abstract
+	% instances that occur in the module interface and a table of
+	% concrete instances that occur in the module implementation.
+	% Imported instances are not included at all.
+	%
+:- pred gather_abstract_and_concrete_instances(instance_table::in,
+	instance_table::out, instance_table::out) is det.
+
+gather_abstract_and_concrete_instances(InstanceTable, Abstracts,
+		Concretes) :-
+	map.foldl2(partition_instances_for_class, InstanceTable,
+		multi_map.init, Abstracts, multi_map.init, Concretes).
+
+	% Partition all the non-imported instances for a particular
+	% class into two groups, those that are abstract and in the
+	% module interface and those that are concrete and in the module
+	% implementation.  Concrete instances cannot occur in the
+	% interface and we ignore abstract instances in the
+	% implementation.
+	%
+:- pred partition_instances_for_class(class_id::in, list(hlds_instance_defn)::in,
+	instance_table::in, instance_table::out, instance_table::in,
+	instance_table::out) is det.
+
+partition_instances_for_class(ClassId, Instances, !Abstracts, !Concretes) :-
+	list.foldl2(partition_instances_for_class_2(ClassId), Instances,
+		!Abstracts, !Concretes).
+
+:- pred partition_instances_for_class_2(class_id::in, hlds_instance_defn::in,
+	instance_table::in, instance_table::out,
+	instance_table::in, instance_table::out) is det.
+
+partition_instances_for_class_2(ClassId, InstanceDefn, !Abstracts,
+		!Concretes) :-
+	ImportStatus = InstanceDefn ^ instance_status,
+	status_is_imported(ImportStatus, IsImported),
+	(
+		IsImported = no,
+		Body = InstanceDefn ^ instance_body,
+		(
+			Body = abstract,
+			status_is_exported_to_non_submodules(ImportStatus,
+				IsExported),
+			(
+				IsExported = yes,
+				svmulti_map.add(ClassId, InstanceDefn,
+					!Abstracts)
+			;
+				IsExported = no
+			)
+		;
+			Body = concrete(_),
+			svmulti_map.add(ClassId, InstanceDefn,
+				!Concretes)
+		)
+	;
+		IsImported = yes
+	).
+
+:- pred check_for_corresponding_instances(instance_table::in,
+	class_id::in, list(hlds_instance_defn)::in, io::di, io::uo) is det.
+
+check_for_corresponding_instances(Concretes, ClassId, InstanceDefns, !IO) :-
+	list.foldl(check_for_corresponding_instances_2(Concretes, ClassId),
+		InstanceDefns, !IO).
+
+:- pred check_for_corresponding_instances_2(instance_table::in, class_id::in,
+	hlds_instance_defn::in, io::di, io::uo) is det.
+
+check_for_corresponding_instances_2(Concretes, ClassId, AbstractInstance,
+		!IO) :-
+	AbstractTypes = AbstractInstance ^ instance_types,
+	( multi_map.search(Concretes, ClassId, ConcreteInstances) ->
+		(
+			list.member(ConcreteInstance, ConcreteInstances),
+			ConcreteTypes = ConcreteInstance ^ instance_types,
+			ConcreteTypes = AbstractTypes
+		->
+			MissingConcreteError = no
+		;
+			% There were concrete instances for ClassId in the
+			% implementation but none of them matches the
+			% abstract instance we have.
+			MissingConcreteError = yes
+		)
+	;
+		% There were no concrete instances for ClassId in the
+		% implementation.
+		MissingConcreteError = yes
+	),
+	(
+		MissingConcreteError = yes,
+		ClassId = class_id(ClassName, _),
+		prim_data.sym_name_to_string(ClassName, ClassNameString),
+		AbstractTypesString = mercury_type_list_to_string(
+			AbstractInstance ^ instance_tvarset, AbstractTypes),
+		AbstractInstanceName = "`" ++ ClassNameString ++
+			"(" ++ AbstractTypesString ++ ")'",
+		% XXX Should we mention any constraints on the instance
+		%     declaration here?
+		ErrorPieces = [words("Error: abstract instance declaration"),
+			words("for"), fixed(AbstractInstanceName),
+			words("has no corresponding concrete"),
+			words("instance in the implementation.")
+		],
+		AbstractInstanceContext = AbstractInstance ^ instance_context,
+		write_error_pieces(AbstractInstanceContext, 0, ErrorPieces,
+			!IO),
+		io.set_exit_status(1, !IO)
+	;
+		MissingConcreteError = no
+	).
+
+%-----------------------------------------------------------------------------%

 :- pred check_for_cyclic_classes(class_table::in, bool::out, io::di, io::uo)
 	is det.
@@ -1002,7 +1145,8 @@
 report_cyclic_classes(ClassTable, ClassPath, !IO) :-
 	(
 		ClassPath = [],
-		error("report_cyclic_classes: empty cycle found")
+		unexpected(this_file,
+			"report_cyclic_classes: empty cycle found.")
 	;
 		ClassPath = [ClassId | Tail],
 		Context = map.lookup(ClassTable, ClassId) ^ class_context,
@@ -1021,6 +1165,13 @@

 add_path_element(class_id(Name, Arity), RevPieces0) =
 	[sym_name_and_arity(Name/Arity), words("<=") | RevPieces0].
+
+
+%---------------------------------------------------------------------------%
+
+:- func this_file = string.
+
+this_file = "check_typeclass.m".

 %---------------------------------------------------------------------------%
 :- end_module check_typeclass.
Index: compiler/hlds_data.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/hlds_data.m,v
retrieving revision 1.91
diff -u -r1.91 hlds_data.m
--- compiler/hlds_data.m	1 Apr 2005 14:28:56 -0000	1.91
+++ compiler/hlds_data.m	13 Apr 2005 00:53:55 -0000
@@ -820,7 +820,8 @@

 	% For each class, we keep track of a list of its instances, since there
 	% can be more than one instance of each class.
-:- type instance_table == map(class_id, list(hlds_instance_defn)).
+	%
+:- type instance_table == multi_map(class_id, hlds_instance_defn).

 	% Information about a single `instance' declaration
 :- type hlds_instance_defn --->
Index: tests/invalid/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/Mmakefile,v
retrieving revision 1.162
diff -u -r1.162 Mmakefile
--- tests/invalid/Mmakefile	12 Apr 2005 07:58:19 -0000	1.162
+++ tests/invalid/Mmakefile	13 Apr 2005 03:53:34 -0000
@@ -97,6 +97,7 @@
 	make_opt_error \
 	merge_ground_any \
 	method_impl \
+	missing_concrete_instance \
 	missing_det_decls \
 	missing_init_pred \
 	missing_interface_import \
Index: tests/invalid/missing_concrete_instance.err_exp
===================================================================
RCS file: tests/invalid/missing_concrete_instance.err_exp
diff -N tests/invalid/missing_concrete_instance.err_exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/invalid/missing_concrete_instance.err_exp	13 Apr 2005 04:12:31 -0000
@@ -0,0 +1,5 @@
+missing_concrete_instance.m:011: Error: abstract instance declaration for
+missing_concrete_instance.m:011:   `missing_concrete_instance.foo(int)' has no
+missing_concrete_instance.m:011:   corresponding concrete instance in the
+missing_concrete_instance.m:011:   implementation.
+For more information, try recompiling with `-E'.
Index: tests/invalid/missing_concrete_instance.m
===================================================================
RCS file: tests/invalid/missing_concrete_instance.m
diff -N tests/invalid/missing_concrete_instance.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/invalid/missing_concrete_instance.m	13 Apr 2005 03:56:34 -0000
@@ -0,0 +1,11 @@
+%
+% rotd-2005-05-12 and before did not emit
+% an error about the missing concrete instance.
+%
+:- module missing_concrete_instance.
+
+:- interface.
+
+:- typeclass foo(T) where [].
+
+:- instance foo(int).

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