%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 1993-2012 The University of Melbourne. % Copyright (C) 2014-2026 The Mercury team. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %---------------------------------------------------------------------------% % % File: typecheck.m. % Main author: fjh. % % This file contains the Mercury type-checker. % % The predicates in this module are named as follows: % % - Predicates that type check a particular language construct % (goal, clause, etc.) are called typecheck_*. These will eventually % have to iterate over every type assignment in the type assignment set. % % - Predicates that unify two things with respect to a single type assignment, % as opposed to a type assignment set are called type_assign_*. % % There are four sorts of types: % % 1 Discriminated union types, such as % :- type tree(T) ---> nil ; t(tree(T), T, tree(T)). % % 2 Equivalence types, such as % :- type real == float. % Any number of types can be equivalent; the *canonical* one is the one % which is not defined using "==". % % Currently references to equivalence types are expanded in a separate pass % by mercury_compile_front_end.m. It would be better to avoid expanding them % (and instead modify the type unification algorithm to handle equivalent % types) because this would give better error messages. However, this is % not a high priority. % % 3 Higher-order predicate and function types % pred, pred(T), pred(T1, T2), pred(T1, T2, T3), ... % func(T1) = T2, func(T1, T2) = T3, ... % % 4 Builtin types, such as % character, int, float, string. % These types have special syntax for constants. There may be other types % (list(T), unit, univ, etc.) provided by the system, but they can just be % part of the standard library. % % Each exported predicate must have a `:- pred' declaration specifying the % types of the arguments for that predicate. For predicates that are % local to a module, we can infer the types. % %---------------------------------------------------------------------------% % % Known Bugs: % % XXX Type inference doesn't handle ambiguity as well as it could do. % We should do a topological sort, and then typecheck it all bottom-up. % If we infer an ambiguous type for a pred, we should not reject it % immediately; instead we should give it an overloaded type, and keep going. % When we have finished type inference, we should then delete unused % overloadings, and only then should we report ambiguity errors, % if any overloading still remains. % % Wish list: % % - We should handle equivalence types here. % %---------------------------------------------------------------------------% :- module check_hlds.typecheck. :- interface. :- import_module hlds. :- import_module hlds.hlds_clauses. :- import_module hlds.hlds_module. :- import_module parse_tree. :- import_module parse_tree.error_spec. :- import_module io. :- import_module list. %---------------------------------------------------------------------------% :- type number_of_iterations ---> within_iteration_limit ; exceeded_iteration_limit. % typecheck_module(ProgressStream, !ModuleInfo, Specs, FoundSyntaxError, % NumberOfIterations): % % Type checks ModuleInfo and annotates it with variable type information. % Specs is set to the list of errors and warnings found, plus messages % about the predicates and functions whose types have been inferred. % We set FoundSyntaxError to `some_clause_syntax_errors' if some of % the clauses in the typechecked predicates contained syntax errors. % We set NumberOfIterations to `exceeded_iteration_limit' % iff the type inference iteration limit was reached. % :- pred typecheck_module(io.text_output_stream::in, module_info::in, module_info::out, list(error_spec)::out, maybe_clause_syntax_errors::out, number_of_iterations::out) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module check_hlds.type_assign. :- import_module check_hlds.typecheck_clauses. :- import_module check_hlds.typecheck_error_overload. :- import_module check_hlds.typecheck_error_undef. :- import_module check_hlds.typecheck_errors. :- import_module check_hlds.typecheck_info. :- import_module check_hlds.typecheck_msgs. :- import_module check_hlds.typeclasses. :- import_module hlds.goal_util. :- import_module hlds.hlds_args. :- import_module hlds.hlds_class. :- import_module hlds.hlds_data. :- import_module hlds.hlds_error_util. :- import_module hlds.hlds_goal. :- import_module hlds.hlds_markers. :- import_module hlds.hlds_pred. :- import_module hlds.hlds_rtti. :- import_module hlds.instmap. :- import_module hlds.make_goal. :- import_module hlds.passes_aux. :- import_module hlds.pred_name. :- import_module hlds.pred_table. :- import_module hlds.special_pred. :- import_module hlds.status. :- import_module hlds.var_table_hlds. :- import_module libs. :- import_module libs.globals. :- import_module libs.options. :- import_module mdbcomp. :- import_module mdbcomp.builtin_modules. :- import_module mdbcomp.prim_data. :- import_module mdbcomp.sym_name. :- import_module parse_tree.builtin_lib_types. :- import_module parse_tree.error_util. :- import_module parse_tree.file_names. % undesirable dependency :- import_module parse_tree.prog_data. :- import_module parse_tree.prog_type. :- import_module parse_tree.prog_type_scan. :- import_module parse_tree.prog_type_subst. :- import_module parse_tree.prog_type_unify. :- import_module parse_tree.var_table. :- import_module parse_tree.vartypes. :- import_module parse_tree.write_error_spec. :- import_module assoc_list. :- import_module bool. :- import_module int. :- import_module map. :- import_module maybe. :- import_module pair. :- import_module require. :- import_module set. :- import_module set_tree234. :- import_module term_context. :- import_module term_subst. :- import_module varset. %---------------------------------------------------------------------------% typecheck_module(ProgressStream, !ModuleInfo, Specs, FoundSyntaxError, NumberOfIterations) :- module_info_get_globals(!.ModuleInfo, Globals), globals.lookup_int_option(Globals, type_inference_iteration_limit, MaxIterations), module_info_get_valid_pred_id_set(!.ModuleInfo, OrigValidPredIdSet), OrigValidPredIds = set_tree234.to_sorted_list(OrigValidPredIdSet), typecheck_to_fixpoint(ProgressStream, 1, MaxIterations, !ModuleInfo, OrigValidPredIds, OrigValidPredIdSet, FinalValidPredIdSet, CheckSpecs, FoundSyntaxError, NumberOfIterations), construct_type_inference_messages(!.ModuleInfo, FinalValidPredIdSet, OrigValidPredIds, [], InferSpecs), Specs = InferSpecs ++ CheckSpecs. % Repeatedly typecheck the code for a group of predicates % until a fixpoint is reached, or until some errors are detected. % :- pred typecheck_to_fixpoint(io.text_output_stream::in, int::in, int::in, module_info::in, module_info::out, list(pred_id)::in, set_tree234(pred_id)::in, set_tree234(pred_id)::out, list(error_spec)::out, maybe_clause_syntax_errors::out, number_of_iterations::out) is det. typecheck_to_fixpoint(ProgressStream, Iteration, MaxIterations, !ModuleInfo, OrigValidPredIds, OrigValidPredIdSet, FinalValidPredIdSet, Specs, FoundSyntaxError, NumberOfIterations) :- module_info_get_pred_id_table(!.ModuleInfo, PredIdTable0), map.to_assoc_list(PredIdTable0, PredIdsInfos0), typecheck_module_one_iteration(ProgressStream, !.ModuleInfo, OrigValidPredIdSet, PredIdsInfos0, [], RevPredIdsInfos, [], NewlyInvalidPredIds, [], CurSpecs, no_clause_syntax_errors, CurFoundSyntaxError, next_iteration_is_not_needed, NextIteration), map.from_rev_sorted_assoc_list(RevPredIdsInfos, PredIdTable), module_info_set_pred_id_table(PredIdTable, !ModuleInfo), module_info_make_pred_ids_invalid(NewlyInvalidPredIds, !ModuleInfo), module_info_get_valid_pred_id_set(!.ModuleInfo, NewValidPredIdSet), module_info_get_globals(!.ModuleInfo, Globals), ( if ( NextIteration = next_iteration_is_not_needed ; contains_errors(Globals, CurSpecs) = yes ) then FinalValidPredIdSet = NewValidPredIdSet, Specs = CurSpecs, FoundSyntaxError = CurFoundSyntaxError, NumberOfIterations = within_iteration_limit else globals.lookup_bool_option(Globals, debug_types, DebugTypes), ( DebugTypes = yes, construct_type_inference_messages(!.ModuleInfo, NewValidPredIdSet, OrigValidPredIds, [], ProgressSpecs), trace [io(!IO)] ( module_info_get_name(!.ModuleInfo, ModuleName), get_debug_output_stream(Globals, ModuleName, OutputStream, !IO), write_error_specs(OutputStream, Globals, ProgressSpecs, !IO) ) ; DebugTypes = no ), ( if Iteration < MaxIterations then typecheck_to_fixpoint(ProgressStream, Iteration + 1, MaxIterations, !ModuleInfo, OrigValidPredIds, OrigValidPredIdSet, FinalValidPredIdSet, Specs, FoundSyntaxError, NumberOfIterations) else FinalValidPredIdSet = NewValidPredIdSet, Specs = [typecheck_report_max_iterations_exceeded(MaxIterations)], FoundSyntaxError = CurFoundSyntaxError, NumberOfIterations = exceeded_iteration_limit ) ). %---------------------------------------------------------------------------% :- type next_iteration ---> next_iteration_is_not_needed ; next_iteration_is_needed. % Iterate over the list of pred_ids in a module. % :- pred typecheck_module_one_iteration(io.text_output_stream::in, module_info::in, set_tree234(pred_id)::in, assoc_list(pred_id, pred_info)::in, assoc_list(pred_id, pred_info)::in, assoc_list(pred_id, pred_info)::out, list(pred_id)::in, list(pred_id)::out, list(error_spec)::in, list(error_spec)::out, maybe_clause_syntax_errors::in, maybe_clause_syntax_errors::out, next_iteration::in, next_iteration::out) is det. typecheck_module_one_iteration(_, _, _, [], !RevPredIdsInfos, !NewlyInvalidPredIds, !Specs, !FoundSyntaxError, !NextIteration). typecheck_module_one_iteration(ProgressStream, ModuleInfo, ValidPredIdSet, [HeadPredIdInfo0 | TailPredIdsInfos0], !RevPredIdsInfos, !NewlyInvalidPredIds, !Specs, !FoundSyntaxError, !NextIteration) :- HeadPredIdInfo0 = PredId - PredInfo0, ( if ( pred_info_is_imported(PredInfo0) ; not set_tree234.contains(ValidPredIdSet, PredId) ) then !:RevPredIdsInfos = [HeadPredIdInfo0 | !.RevPredIdsInfos], typecheck_module_one_iteration(ProgressStream, ModuleInfo, ValidPredIdSet, TailPredIdsInfos0, !RevPredIdsInfos, !NewlyInvalidPredIds, !Specs, !FoundSyntaxError, !NextIteration) else % Potential parallelization site. typecheck_pred_if_needed(ProgressStream, ModuleInfo, PredId, PredInfo0, PredInfo, PredSpecs, PredSyntaxError, ContainsErrors, PredNextIteration), ( ContainsErrors = no ; ContainsErrors = yes, % This code is not needed at the moment, since currently we don't % run mode analysis if there are any type errors. And this code % also causes problems: if there are undefined modes, it can end up % calling error/1, since post_finish_ill_typed_pred assumes that % there are no undefined modes. % % If we get an error, we need to call post_finish_ill_typed_pred % on the pred, to ensure that its mode declaration gets properly % module qualified; then we call `remove_predid', so that the % predicate's definition will be ignored by later passes % (the declaration will still be used to check any calls to it). % % post_finish_ill_typed_pred(ModuleInfo0, PredId, % PredInfo1, PredInfo) !:NewlyInvalidPredIds = [PredId | !.NewlyInvalidPredIds] ), HeadPredIdInfo = PredId - PredInfo, !:Specs = PredSpecs ++ !.Specs, ( PredSyntaxError = some_clause_syntax_errors, !:FoundSyntaxError = some_clause_syntax_errors ; PredSyntaxError = no_clause_syntax_errors ), ( PredNextIteration = next_iteration_is_not_needed ; PredNextIteration = next_iteration_is_needed, !:NextIteration = next_iteration_is_needed ), !:RevPredIdsInfos = [HeadPredIdInfo | !.RevPredIdsInfos], typecheck_module_one_iteration(ProgressStream, ModuleInfo, ValidPredIdSet, TailPredIdsInfos0, !RevPredIdsInfos, !NewlyInvalidPredIds, !Specs, !FoundSyntaxError, !NextIteration) ). :- pred typecheck_pred_if_needed(io.text_output_stream::in, module_info::in, pred_id::in, pred_info::in, pred_info::out, list(error_spec)::out, maybe_clause_syntax_errors::out, bool::out, next_iteration::out) is det. typecheck_pred_if_needed(ProgressStream, ModuleInfo, PredId, !PredInfo, !:Specs, FoundSyntaxError, ContainsErrors, NextIteration) :- ( if is_pred_created_type_correct(ModuleInfo, !PredInfo) then !:Specs = [], FoundSyntaxError = no_clause_syntax_errors, ContainsErrors = no, NextIteration = next_iteration_is_not_needed else pred_info_get_clauses_info(!.PredInfo, ClausesInfo0), clauses_info_get_had_syntax_errors(ClausesInfo0, FoundSyntaxError), handle_stubs(ModuleInfo, PredId, !PredInfo, FoundSyntaxError, !:Specs, MaybeNeedTypecheck), ( MaybeNeedTypecheck = do_not_need_typecheck(ContainsErrors, NextIteration) ; MaybeNeedTypecheck = do_need_typecheck, do_typecheck_pred(ProgressStream, ModuleInfo, PredId, !PredInfo, !Specs, NextIteration), module_info_get_globals(ModuleInfo, Globals), ContainsErrors = contains_errors(Globals, !.Specs) ) ). :- pred is_pred_created_type_correct(module_info::in, pred_info::in, pred_info::out) is semidet. is_pred_created_type_correct(ModuleInfo, !PredInfo) :- ( if ( % Most compiler-generated unify and compare predicates are created % already type-correct, so there is no need to typecheck them. % The exceptions are unify and compare predicates that either % % - call a user-defined predicate, or % - involve existentially quantified type variables. is_unify_index_or_compare_pred(!.PredInfo), not special_pred_needs_typecheck(ModuleInfo, !.PredInfo) ; % Most predicates for builtins are also created already % type-correct. The exceptions still need to have their stub % clauses generated; these are marked with marker_builtin_stub. % XXX Why the delay? pred_info_is_builtin(!.PredInfo), pred_info_get_markers(!.PredInfo, Markers), not marker_is_present(Markers, marker_builtin_stub) ) then pred_info_get_clauses_info(!.PredInfo, ClausesInfo0), clauses_info_get_clauses_rep(ClausesInfo0, ClausesRep0, _ItemNumbers), IsEmpty = clause_list_is_empty(ClausesRep0), ( IsEmpty = yes, pred_info_mark_as_external(!PredInfo) ; IsEmpty = no ) else fail ). :- type maybe_need_typecheck ---> do_not_need_typecheck( notc_contains_errors :: bool, notc_next_iteration :: next_iteration ) ; do_need_typecheck. % This predicate handles stubs, and implements the --allow-stubs % and --warn-stubs options. % % If --allow-stubs is set, and there are no clauses, then % - issue a warning (if --warn-stubs is set), and then % - generate a "stub" clause that just throws an exception. % :- pred handle_stubs(module_info::in, pred_id::in, pred_info::in, pred_info::out, maybe_clause_syntax_errors::in, list(error_spec)::out, maybe_need_typecheck::out) is det. handle_stubs(ModuleInfo, PredId, !PredInfo, FoundSyntaxError, !:Specs, MaybeNeedTypecheck) :- pred_info_get_markers(!.PredInfo, Markers0), pred_info_get_clauses_info(!.PredInfo, ClausesInfo0), clauses_info_get_clauses_rep(ClausesInfo0, ClausesRep0, _ItemNumbers0), clause_list_is_empty(ClausesRep0) = ClausesRep0IsEmpty, ( ClausesRep0IsEmpty = yes, module_info_get_globals(ModuleInfo, Globals), % There are no clauses, so there can be no clause non-contiguity % errors. ( if globals.lookup_bool_option(Globals, allow_stubs, yes), not marker_is_present(Markers0, marker_class_method) then !:Specs = maybe_report_no_clauses_stub(ModuleInfo, PredId, !.PredInfo), generate_and_add_stub_clause(ModuleInfo, PredId, !PredInfo) else if marker_is_present(Markers0, marker_builtin_stub) then !:Specs = [], generate_and_add_stub_clause(ModuleInfo, PredId, !PredInfo) else !:Specs = [] ) ; ClausesRep0IsEmpty = no, !:Specs = [] ), % The above code may add stub clauses to the predicate, which would % invalidate ClausesInfo0. pred_info_get_clauses_info(!.PredInfo, ClausesInfo1), clauses_info_get_clauses_rep(ClausesInfo1, ClausesRep1, _ItemNumbers), clause_list_is_empty(ClausesRep1) = ClausesRep1IsEmpty, ( ClausesRep1IsEmpty = yes, expect(unify(!.Specs, []), $pred, "starting Specs not empty"), % There are no clauses for class methods. The clauses are generated % later on, in polymorphism.expand_class_method_bodies. % XXX Why the delay? ( if marker_is_present(Markers0, marker_class_method) then % For the moment, we just insert the types of the head vars % into the clauses_info. clauses_info_get_headvar_list(ClausesInfo1, HeadVars), pred_info_get_arg_types(!.PredInfo, _ArgTypeVarSet, _ExistQVars, ArgTypes), vartypes_from_corresponding_lists(HeadVars, ArgTypes, VarTypes), clauses_info_get_varset(ClausesInfo1, VarSet), corresponding_vars_types_to_var_table(ModuleInfo, VarSet, HeadVars, ArgTypes, VarTable), clauses_info_set_explicit_vartypes(VarTypes, ClausesInfo1, ClausesInfo2), clauses_info_set_var_table(VarTable, ClausesInfo2, ClausesInfo), pred_info_set_clauses_info(ClausesInfo, !PredInfo), % We also need to set the external_type_params field % to indicate that all the existentially quantified tvars % in the head of this pred are indeed bound by this predicate. type_vars_in_types(ArgTypes, HeadVarsInclExistentials), pred_info_set_external_type_params(HeadVarsInclExistentials, !PredInfo), ContainsErrors = no, !:Specs = [] else ContainsErrors = yes, ( FoundSyntaxError = no_clause_syntax_errors, !:Specs = maybe_report_no_clauses(ModuleInfo, PredId, !.PredInfo) ; FoundSyntaxError = some_clause_syntax_errors, % There were clauses, they just had errors. Printing a message % saying that there were no clauses would be misleading, % and the messages for the syntax errors will mean that % this compiler invocation won't succeed anyway. !:Specs = [] ) ), MaybeNeedTypecheck = do_not_need_typecheck(ContainsErrors, next_iteration_is_not_needed) ; ClausesRep1IsEmpty = no, ( FoundSyntaxError = no_clause_syntax_errors, MaybeNeedTypecheck = do_need_typecheck ; FoundSyntaxError = some_clause_syntax_errors, % Printing the messages we generated above could be misleading, % and the messages for the syntax errors will mean that % this compiler invocation won't succeed anyway. !:Specs = [], ContainsErrors = yes, MaybeNeedTypecheck = do_not_need_typecheck(ContainsErrors, next_iteration_is_not_needed) ) ). %---------------------------------------------------------------------------% :- pred do_typecheck_pred(io.text_output_stream::in, module_info::in, pred_id::in, pred_info::in, pred_info::out, list(error_spec)::in, list(error_spec)::out, next_iteration::out) is det. do_typecheck_pred(ProgressStream, ModuleInfo, PredId, !PredInfo, !Specs, NextIteration) :- some [!Info, !TypeAssignSet, !ClausesInfo, !ExternalTypeParams] ( pred_info_get_clauses_info(!.PredInfo, !:ClausesInfo), clauses_info_get_clauses_rep(!.ClausesInfo, ClausesRep0, ItemNumbers), clauses_info_get_headvar_list(!.ClausesInfo, HeadVars), clauses_info_get_varset(!.ClausesInfo, ClauseVarSet), clauses_info_get_explicit_vartypes(!.ClausesInfo, ExplicitVarTypes0), pred_info_get_status(!.PredInfo, PredStatus), pred_info_get_typevarset(!.PredInfo, TypeVarSet0), pred_info_get_arg_types(!.PredInfo, _ArgTypeVarSet, ExistQVars0, ArgTypes0), pred_info_get_markers(!.PredInfo, Markers0), ( if marker_is_present(Markers0, marker_infer_type) then % For a predicate whose type is inferred, the predicate is allowed % to bind the type variables in the head of the predicate's type % declaration. Such predicates are given an initial type % declaration of `pred foo(T1, T2, ..., TN)' by make_hlds.m. Inferring = yes, trace [io(!IO)] ( maybe_write_pred_progress_message(ProgressStream, ModuleInfo, "Inferring type of", PredId, !IO) ), !:ExternalTypeParams = [], PredConstraints = univ_exist_constraints([], []) else Inferring = no, trace [io(!IO)] ( maybe_write_pred_progress_message(ProgressStream, ModuleInfo, "Type-checking", PredId, !IO) ), type_vars_in_types(ArgTypes0, !:ExternalTypeParams), pred_info_get_class_context(!.PredInfo, PredConstraints), constraint_list_get_tvars(PredConstraints ^ univ_constraints, UnivTVars), !:ExternalTypeParams = UnivTVars ++ !.ExternalTypeParams, list.sort_and_remove_dups(!ExternalTypeParams), list.delete_elems(!.ExternalTypeParams, ExistQVars0, !:ExternalTypeParams) ), module_info_get_class_table(ModuleInfo, ClassTable), make_head_hlds_constraint_db(ClassTable, TypeVarSet0, PredConstraints, ConstraintDb), type_assign_set_init(TypeVarSet0, ExplicitVarTypes0, !.ExternalTypeParams, ConstraintDb, !:TypeAssignSet), pred_info_get_markers(!.PredInfo, PredMarkers0), typecheck_info_init(ProgressStream, ModuleInfo, PredId, !.PredInfo, ClauseVarSet, PredStatus, PredMarkers0, !.Specs, !:Info), get_clause_list_for_replacement(ClausesRep0, Clauses0), typecheck_clauses(HeadVars, ArgTypes0, Clauses0, Clauses, !TypeAssignSet, !Info), typecheck_info_get_rhs_lambda(!.Info, MaybeRHSLambda), ( MaybeRHSLambda = has_no_rhs_lambda ; MaybeRHSLambda = has_rhs_lambda, add_marker(marker_has_rhs_lambda, PredMarkers0, PredMarkers), pred_info_set_markers(PredMarkers, !PredInfo) ), % We need to perform a final pass of context reduction at the end, % before checking the typeclass constraints. pred_info_get_context(!.PredInfo, Context), perform_context_reduction(Context, !TypeAssignSet, !Info), typecheck_check_for_ambiguity(Context, whole_pred, HeadVars, !.TypeAssignSet, !Info), typecheck_check_remaining_coercion_constraints(!.TypeAssignSet, !Info), type_assign_set_get_final_info(!.TypeAssignSet, !.ExternalTypeParams, ExistQVars0, ExplicitVarTypes0, TypeVarSet, !:ExternalTypeParams, InferredVarTypes, InferredTypeConstraints0, ConstraintProofMap, ConstraintMap, TVarRenaming, ExistTypeRenaming), vartypes_to_sorted_assoc_list(InferredVarTypes, VarsTypes), vars_types_to_var_table(ModuleInfo, ClauseVarSet, VarsTypes, VarTable0), var_table_optimize(VarTable0, VarTable), clauses_info_set_var_table(VarTable, !ClausesInfo), % Apply substitutions to the explicit vartypes. ( ExistQVars0 = [], ExplicitVarTypes1 = ExplicitVarTypes0 ; ExistQVars0 = [_ | _], rename_vars_in_vartypes(ExistTypeRenaming, ExplicitVarTypes0, ExplicitVarTypes1) ), rename_vars_in_vartypes(TVarRenaming, ExplicitVarTypes1, ExplicitVarTypes), clauses_info_set_explicit_vartypes(ExplicitVarTypes, !ClausesInfo), set_clause_list(Clauses, ClausesRep), clauses_info_set_clauses_rep(ClausesRep, ItemNumbers, !ClausesInfo), pred_info_set_clauses_info(!.ClausesInfo, !PredInfo), pred_info_set_typevarset(TypeVarSet, !PredInfo), pred_info_set_constraint_proof_map(ConstraintProofMap, !PredInfo), pred_info_set_constraint_map(ConstraintMap, !PredInfo), % Split the inferred type class constraints into those that apply % only to the head variables, and those that apply to type variables % which occur only in the body. lookup_var_types(InferredVarTypes, HeadVars, ArgTypes), type_vars_in_types(ArgTypes, ArgTypeVars), restrict_constraints_to_head_vars(ArgTypeVars, InferredTypeConstraints0, InferredTypeConstraints, UnprovenBodyConstraints), % If there are any as-yet-unproven constraints on type variables % in the body, then save these in the pred_info. If it turns out that % this pass was the last pass of type inference, the post_typecheck % pass will report an error. But we can't report an error now, because % a later pass of type inference could cause some type variables % to become bound to types that make the constraints satisfiable, % causing the error to go away. pred_info_set_unproven_body_constraints(UnprovenBodyConstraints, !PredInfo), ( Inferring = yes, % We need to infer which of the head variable types must be % existentially quantified. infer_existential_types(ArgTypeVars, ExistQVars, !ExternalTypeParams), % Now save the information we inferred in the pred_info. pred_info_set_external_type_params(!.ExternalTypeParams, !PredInfo), pred_info_set_arg_types(TypeVarSet, ExistQVars, ArgTypes, !PredInfo), pred_info_get_class_context(!.PredInfo, OldTypeConstraints), pred_info_set_class_context(InferredTypeConstraints, !PredInfo), % Check if anything changed. ( if ( % If the argument types and the type constraints are % identical up to renaming, then nothing has changed. pred_info_get_tvar_kind_map(!.PredInfo, TVarKindMap), argtypes_identical_up_to_renaming(TVarKindMap, ExistQVars0, ArgTypes0, OldTypeConstraints, ExistQVars, ArgTypes, InferredTypeConstraints) ; % Promises cannot be called from anywhere. Therefore % even if the types of their arguments have changed, % this fact won't affect the type analysis of any other % predicate. pred_info_get_goal_type(!.PredInfo, GoalType), GoalType = goal_for_promise(_) ) then NextIteration = next_iteration_is_not_needed else NextIteration = next_iteration_is_needed ) ; Inferring = no, pred_info_set_external_type_params(!.ExternalTypeParams, !PredInfo), pred_info_get_origin(!.PredInfo, Origin0), % Leave the original argtypes etc., but apply any substitutions % that map existentially quantified type variables to other % type vars, and then rename them all to match the new typevarset, % so that the type variables names match up (e.g. with the type % variables in the constraint_proofs) % Apply any type substitutions that map existentially quantified % type variables to other type vars. ( ExistQVars0 = [], % Optimize common case. ExistQVars1 = [], ArgTypes1 = ArgTypes0, PredConstraints1 = PredConstraints, Origin1 = Origin0 ; ExistQVars0 = [_ | _], term_subst.apply_renaming_in_vars(ExistTypeRenaming, ExistQVars0, ExistQVars1), apply_renaming_to_types(ExistTypeRenaming, ArgTypes0, ArgTypes1), apply_renaming_to_univ_exist_constraints( ExistTypeRenaming, PredConstraints, PredConstraints1), rename_instance_method_constraints(ExistTypeRenaming, Origin0, Origin1) ), % Rename them all to match the new typevarset. term_subst.apply_renaming_in_vars(TVarRenaming, ExistQVars1, ExistQVars), apply_renaming_to_types(TVarRenaming, ArgTypes1, RenamedOldArgTypes), apply_renaming_to_univ_exist_constraints(TVarRenaming, PredConstraints1, RenamedOldConstraints), rename_instance_method_constraints(TVarRenaming, Origin1, Origin), % Save the results in the pred_info. pred_info_set_arg_types(TypeVarSet, ExistQVars, RenamedOldArgTypes, !PredInfo), pred_info_set_class_context(RenamedOldConstraints, !PredInfo), pred_info_set_origin(Origin, !PredInfo), NextIteration = next_iteration_is_not_needed ), typecheck_info_get_all_errors(!.Info, !:Specs) ). % Mark the predicate as a stub, and generate a clause of the form %
(...) :-
% PredName = " (...) :-
% PredName = "