%-----------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %-----------------------------------------------------------------------------% % Copyright (C) 2001-2006 University of Melbourne. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %-----------------------------------------------------------------------------% % File: recompilation.m. % Main author: stayl. % Type declarations for smart recompilation. % Predicates to record program items used by a compilation. % % A module must be recompiled if % - The file itself has changed. % - An imported item used in compiling the module has changed or been removed. % - An item has been added to an imported module which could cause an % ambiguity with an item used in compiling the module. % % Currently smart recompilation does not work properly with % inter-module optimization. If a `.opt' file changes, all modules % importing it need to be recompiled. % %-----------------------------------------------------------------------------% :- module recompilation. :- interface. :- import_module check_hlds. :- import_module hlds. :- import_module libs. :- import_module mdbcomp. :- import_module parse_tree. :- import_module libs.timestamp. :- import_module mdbcomp.prim_data. :- import_module parse_tree.prog_data. :- import_module io. :- import_module map. :- import_module set. :- import_module std_util. :- import_module term. :- include_module recompilation__check. :- include_module recompilation__usage. :- include_module recompilation__version. % Identify a particular version of a program item. % This could be done using a timestamp or a hash value. :- type version_number == timestamp. :- pred write_version_number(version_number::in, io::di, io::uo) is det. :- func term_to_version_number(term(T)) = version_number is semidet. :- func term_to_timestamp(term(T)) = timestamp is semidet. %-----------------------------------------------------------------------------% :- type item_id ---> item_id(item_type, item_name). :- type item_name == pair(sym_name, arity). :- type item_type ---> type_item % Just the name of the type, not its body. % It is common for a value of a type to % be passed through a predicate without % inspecting the value -- such predicates % do not need to be recompiled if the % body of the type changes (except for % equivalence types). ; type_body_item ; mode_item ; inst_item ; typeclass_item ; functor_item % The RHS of a var-functor unification. ; predicate_item ; function_item ; mutable_item. :- inst simple_item ---> type_item ; type_body_item ; mode_item ; inst_item ; typeclass_item. :- inst pred_or_func ---> predicate_item ; function_item. :- pred is_simple_item_type(item_type::(ground >> simple_item)) is semidet. :- pred is_pred_or_func_item_type(item_type::(ground >> pred_or_func)) is semidet. :- pred string_to_item_type(string, item_type). :- mode string_to_item_type(in, out) is semidet. :- mode string_to_item_type(out, in) is det. :- func pred_or_func_to_item_type(pred_or_func::in) = (item_type::out(pred_or_func)) is det. %-----------------------------------------------------------------------------% :- type recompilation_info ---> recompilation_info( % Name of the current module. module_name :: module_name, % Used items imported from other modules. used_items :: used_items, % For now we only record dependencies of imported items % on equivalence types. The rest of the dependencies can be % found by examining the pred_infos, type_defns etc of the % items recorded in the used_items field above. dependencies :: map(item_id, set(item_id)), version_numbers :: map(module_name, version_numbers) ). :- func init_recompilation_info(module_name) = recompilation_info. % recompilation__add_used_item(ItemType, UnqualifiedId, QualifiedId, % !Info). % % Record a reference to UnqualifiedId, for which QualifiedId % is the only match. If a new declaration is added so that % QualifiedId is not the only match, we need to recompile. % :- pred recompilation__record_used_item(item_type::in, item_name::in, item_name::in, recompilation_info::in, recompilation_info::out) is det. % For each imported item we need to record which equivalence types % are used because equiv_type.m removes all references to the % equivalence types, and at that point we don't know which imported % items are going to be used by the compilation. % % For predicates declared using `with_type` annotations, % the version number in the interface file and the % version_numbers map will refer to the arity before expansion % of the `with_type` annotation, so that needs to be recorded % here as well. % :- pred recompilation__record_expanded_items(item_id::in, set(item_id)::in, recompilation_info::in, recompilation_info::out) is det. %-----------------------------------------------------------------------------% :- type item_id_set(Map, Set, Cons) ---> item_id_set( types :: Map, type_bodies :: Map, modes :: Map, insts :: Map, typeclasses :: Map, functors :: Cons, predicates :: Set, functions :: Set, mutables :: Set ). :- type item_id_set(T) == item_id_set(T, T, T). :- func init_item_id_set(T) = item_id_set(T). :- func init_item_id_set(Simple, PorF, Cons) = item_id_set(Simple, PorF, Cons). %-----------------------------------------------------------------------------% % A simple_item_set records the single possible match for an item. % :- type simple_item_set == map(pair(string, arity), map(module_qualifier, module_name)). % For constructors, predicates and functions we can't work out % which item is actually used until we've run typechecking. % :- type pred_or_func_set == simple_item_set. :- type functor_set == simple_item_set. % Items which are used by local items. :- type used_items == item_id_set( simple_item_set, pred_or_func_set, functor_set ). :- func init_used_items = used_items. %-----------------------------------------------------------------------------% % % Access functions for item_id_sets. % :- func extract_simple_item_set(item_id_set(Simple, PorF, Cons)::in, item_type::in(simple_item)) = (Simple::out) is det. :- func update_simple_item_set(item_id_set(Simple, PorF, Cons)::in, item_type::in(simple_item), Simple::in) = (item_id_set(Simple, PorF, Cons)::out) is det. :- func extract_pred_or_func_set(item_id_set(Simple, PorF, Cons)::in, item_type::in(pred_or_func)) = (PorF::out) is det. :- func update_pred_or_func_set(item_id_set(Simple, PorF, Cons)::in, item_type::in(pred_or_func), PorF::in) = (item_id_set(Simple, PorF, Cons)::out) is det. :- func extract_ids(item_id_set(T), item_type) = T. :- func update_ids(item_id_set(T), item_type, T) = item_id_set(T). :- func map_ids((func(item_type, T) = U), item_id_set(T), U) = item_id_set(U). %-----------------------------------------------------------------------------% % Version numbers for items in a single module. :- type version_numbers ---> version_numbers( item_version_numbers, instance_version_numbers ). % The constructors set should always be empty - % constructors are never imported separately. :- type item_version_numbers == item_id_set(version_number_map). :- type version_number_map == map(pair(string, arity), version_number). % For each interface file, we keep a version number for each class. :- type instance_version_numbers == map(item_name, version_number). %-----------------------------------------------------------------------------% % unqualified("") if the symbol was unqualified. :- type module_qualifier == module_name. :- func find_module_qualifier(sym_name) = module_qualifier. :- func module_qualify_name(module_qualifier, string) = sym_name. %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% :- implementation. :- import_module hlds.passes_aux. :- import_module libs.globals. :- import_module libs.options. :- import_module parse_tree.modules. :- import_module parse_tree.prog_util. :- import_module bool. :- import_module int. :- import_module list. :- import_module string. :- import_module time. %-----------------------------------------------------------------------------% term_to_version_number(Term) = term_to_timestamp(Term). term_to_timestamp(term__functor(term__string(TimestampString), [], _)) = string_to_timestamp(TimestampString). write_version_number(VersionNumber, !IO) :- io__write_string("""", !IO), io__write_string(timestamp_to_string(VersionNumber), !IO), io__write_string("""", !IO). %-----------------------------------------------------------------------------% pred_or_func_to_item_type(predicate) = predicate_item. pred_or_func_to_item_type(function) = function_item. is_simple_item_type(type_item). is_simple_item_type(type_body_item). is_simple_item_type(inst_item). is_simple_item_type(mode_item). is_simple_item_type(typeclass_item). is_pred_or_func_item_type(predicate_item). is_pred_or_func_item_type(function_item). string_to_item_type("type", type_item). string_to_item_type("type_body", type_body_item). string_to_item_type("inst", inst_item). string_to_item_type("mode", mode_item). string_to_item_type("typeclass", typeclass_item). string_to_item_type("predicate", predicate_item). string_to_item_type("function", function_item). string_to_item_type("functor", functor_item). string_to_item_type("mutable", mutable_item). %-----------------------------------------------------------------------------% init_item_id_set(Init) = item_id_set(Init, Init, Init, Init, Init, Init, Init, Init, Init). init_item_id_set(Simple, PorF, Cons) = item_id_set(Simple, Simple, Simple, Simple, Simple, Cons, PorF, PorF, PorF). init_used_items = item_id_set(map__init, map__init, map__init, map__init, map__init, map__init, map__init, map__init, map__init). extract_simple_item_set(Items, type_item) = Items ^ types. extract_simple_item_set(Items, type_body_item) = Items ^ type_bodies. extract_simple_item_set(Items, mode_item) = Items ^ modes. extract_simple_item_set(Items, inst_item) = Items ^ insts. extract_simple_item_set(Items, typeclass_item) = Items ^ typeclasses. update_simple_item_set(Items, type_item, IdMap) = Items ^ types := IdMap. update_simple_item_set(Items, type_body_item, IdMap) = Items ^ type_bodies := IdMap. update_simple_item_set(Items, mode_item, IdMap) = Items ^ modes := IdMap. update_simple_item_set(Items, inst_item, IdMap) = Items ^ insts := IdMap. update_simple_item_set(Items, typeclass_item, IdMap) = Items ^ typeclasses := IdMap. extract_pred_or_func_set(Items, predicate_item) = Items ^ predicates. extract_pred_or_func_set(Items, function_item) = Items ^ functions. update_pred_or_func_set(Items, predicate_item, Set) = Items ^ predicates := Set. update_pred_or_func_set(Items, function_item, Set) = Items ^ functions := Set. extract_ids(Items, type_item) = Items ^ types. extract_ids(Items, type_body_item) = Items ^ type_bodies. extract_ids(Items, mode_item) = Items ^ modes. extract_ids(Items, inst_item) = Items ^ insts. extract_ids(Items, typeclass_item) = Items ^ typeclasses. extract_ids(Items, functor_item) = Items ^ functors. extract_ids(Items, predicate_item) = Items ^ predicates. extract_ids(Items, function_item) = Items ^ functions. extract_ids(Items, mutable_item) = Items ^ mutables. update_ids(Items, type_item, IdMap) = Items ^ types := IdMap. update_ids(Items, type_body_item, IdMap) = Items ^ type_bodies := IdMap. update_ids(Items, mode_item, IdMap) = Items ^ modes := IdMap. update_ids(Items, inst_item, IdMap) = Items ^ insts := IdMap. update_ids(Items, typeclass_item, IdMap) = Items ^ typeclasses := IdMap. update_ids(Items, predicate_item, IdMap) = Items ^ predicates := IdMap. update_ids(Items, function_item, IdMap) = Items ^ functions := IdMap. update_ids(Items, functor_item, IdMap) = Items ^ functors := IdMap. update_ids(Items, mutable_item, IdMap) = Items ^ mutables := IdMap. map_ids(Func, Items0, Init) = Items :- Items1 = init_item_id_set(Init), Items = list__foldl( (func(ItemType, NewItems0) = update_ids(NewItems0, ItemType, Func(ItemType, extract_ids(Items0, ItemType))) ), [type_item, type_body_item, mode_item, inst_item, typeclass_item, functor_item, predicate_item, function_item], Items1). %-----------------------------------------------------------------------------% find_module_qualifier(unqualified(_)) = unqualified(""). find_module_qualifier(qualified(ModuleName, _)) = ModuleName. module_qualify_name(Qualifier, Name) = ( Qualifier = unqualified("") -> unqualified(Name) ; qualified(Qualifier, Name) ). %-----------------------------------------------------------------------------% init_recompilation_info(ModuleName) = recompilation_info( ModuleName, init_used_items, map__init, map__init ). recompilation__record_used_item(ItemType, Id, QualifiedId, !Info) :- ( % Don't record builtin items (QualifiedId may be unqualified % for predicates, functions and functors because they aren't % qualified until after typechecking). ItemType \= predicate_item, ItemType \= function_item, ItemType \= functor_item, QualifiedId = unqualified(_) - _ -> true ; ItemSet0 = !.Info ^ used_items, IdSet0 = extract_ids(ItemSet0, ItemType), QualifiedId = QualifiedName - Arity, unqualify_name(QualifiedName, UnqualifiedName), ModuleName = find_module_qualifier(QualifiedName), UnqualifiedId = UnqualifiedName - Arity, Id = SymName - _, ModuleQualifier = find_module_qualifier(SymName), ( map__search(IdSet0, UnqualifiedId, MatchingNames0) -> MatchingNames1 = MatchingNames0 ; map__init(MatchingNames1) ), ( map__contains(MatchingNames1, ModuleQualifier) -> true ; map__det_insert(MatchingNames1, ModuleQualifier, ModuleName, MatchingNames), map__set(IdSet0, UnqualifiedId, MatchingNames, IdSet), ItemSet = update_ids(ItemSet0, ItemType, IdSet), !:Info = !.Info ^ used_items := ItemSet ) ). recompilation__record_expanded_items(Item, ExpandedItems, !Info) :- ( set__empty(ExpandedItems) -> true ; DepsMap0 = !.Info ^ dependencies, ( map__search(DepsMap0, Item, Deps0) -> Deps1 = Deps0 ; set__init(Deps1) ), set__union(Deps1, ExpandedItems, Deps), map__set(DepsMap0, Item, Deps, DepsMap), !:Info = !.Info ^ dependencies := DepsMap ). %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------%