123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)openPpopenCErrorsopenUtilopenNamesopenLibobject(************************************************************************)(*s Low-level interning/externing of libraries to files *)letraw_extern_libraryf=ObjFile.open_out~file:fletraw_intern_libraryf=System.with_magic_number_check(funfile->ObjFile.open_in~file)f(************************************************************************)(** Serialized objects loaded on-the-fly *)exceptionFaultyofstringmoduleDelayed:sigtype'adelayedvalin_delayed:string->ObjFile.in_handle->segment:string->'adelayed*Digest.tvalfetch_delayed:'adelayed->'aend=structtype'adelayed={del_file:string;del_off:int64;del_digest:Digest.t;}letin_delayedfch~segment=letseg=ObjFile.get_segmentch~segmentinletdigest=seg.ObjFile.hashin{del_file=f;del_digest=digest;del_off=seg.ObjFile.pos;},digest(** Fetching a table of opaque terms at position [pos] in file [f],
expecting to find first a copy of [digest]. *)letfetch_delayeddel=let{del_digest=digest;del_file=f;del_off=pos;}=delinletch=open_in_binfinletobj,digest'=trylet()=LargeFile.seek_inchposinletobj=System.marshal_infchinletdigest'=Digest.inputchinobj,digest'withe->close_inch;raiseeinclose_inch;ifnot(String.equaldigestdigest')thenraise(Faultyf);objendopenDelayed(************************************************************************)(*s Modules on disk contain the following informations (after the magic
number, and before the digest). *)typecompilation_unit_name=DirPath.ttypelibrary_disk={md_compiled:Safe_typing.compiled_library;md_objects:Declaremods.library_objects;}typesummary_disk={md_name:compilation_unit_name;md_deps:(compilation_unit_name*Safe_typing.vodigest)array;md_ocaml:string;}(*s Modules loaded in memory contain the following informations. They are
kept in the global table [libraries_table]. *)typelibrary_t={library_name:compilation_unit_name;library_data:library_disk;library_deps:(compilation_unit_name*Safe_typing.vodigest)array;library_digests:Safe_typing.vodigest;library_extra_univs:Univ.ContextSet.t;}typelibrary_summary={libsum_name:compilation_unit_name;libsum_digests:Safe_typing.vodigest;}(* This is a map from names to loaded libraries *)letlibraries_table:library_summaryDPmap.tref=Summary.refDPmap.empty~name:"LIBRARY"(* This is the map of loaded libraries filename *)(* (not synchronized so as not to be caught in the states on disk) *)letlibraries_filename_table=refDPmap.empty(* These are the _ordered_ sets of loaded, imported and exported libraries *)letlibraries_loaded_list=Summary.ref[]~name:"LIBRARY-LOAD"(* Opaque proof tables *)(* various requests to the tables *)letfind_librarydir=DPmap.finddir!libraries_tablelettry_find_librarydir=tryfind_librarydirwithNot_found->user_err(str"Unknown library "++DirPath.printdir++str".")letregister_library_filenamedirf=(* Not synchronized: overwrite the previous binding if one existed *)(* from a previous play of the session *)libraries_filename_table:=DPmap.adddirf!libraries_filename_tableletlibrary_full_filenamedir=tryDPmap.finddir!libraries_filename_tablewithNot_found->"<unavailable filename>"letlibrary_is_loadeddir=trylet_=find_librarydirintruewithNot_found->false(* If a library is loaded several time, then the first occurrence must
be performed first, thus the libraries_loaded_list ... *)letregister_loaded_librarym=letlibname=m.libsum_nameinletrecaux=function|[]->ifFlags.get_native_compiler()thenbeginletdirname=Filename.dirname(library_full_filenamelibname)inNativelib.enable_librarydirnamelibnameend;[libname]|m'::_aslwhenDirPath.equalm'libname->l|m'::l'->m'::auxl'inlibraries_loaded_list:=aux!libraries_loaded_list;libraries_table:=DPmap.addlibnamem!libraries_tableletloaded_libraries()=!libraries_loaded_list(** Delayed / available tables of opaque terms *)typetable_status=|ToFetchofOpaques.opaque_diskdelayed|FetchedofOpaques.opaque_diskletopaque_tables=ref(DPmap.empty:table_statusDPmap.t)letadd_opaque_tabledpst=opaque_tables:=DPmap.adddpst!opaque_tablesletaccess_tablewhattablesdpi=lett=matchDPmap.finddp!tableswith|Fetchedt->t|ToFetchf->letdir_path=Names.DirPath.to_stringdpinFlags.if_verboseFeedback.msg_info(str"Fetching "++strwhat++str" from disk for "++strdir_path);lett=tryfetch_delayedfwithFaultyf->user_err(str"The file "++strf++str" (bound to "++strdir_path++str") is corrupted,\ncannot load some "++strwhat++str" in it.\n")intables:=DPmap.adddp(Fetchedt)!tables;tinOpaques.get_opaque_diskitletaccess_opaque_tableo=let(sub,ci,dp,i)=Opaqueproof.reproinletans=ifDirPath.equaldp(Global.current_dirpath())thenOpaques.get_current_opaqueielseletwhat="opaque proofs"inaccess_tablewhatopaque_tablesdpiinmatchanswith|None->None|Some(c,ctx)->let(c,ctx)=Discharge.cook_opaque_prooftermci(c,ctx)inletc=Mod_subst.(List.fold_rightsubst_mpssubc)inSome(c,ctx)letindirect_accessor={Global.access_proof=access_opaque_table;}(************************************************************************)(* Internalise libraries *)typeseg_sum=summary_disktypeseg_lib=library_disktypeseg_univ=(* true = vivo, false = vi *)Univ.ContextSet.t*booltypeseg_proofs=Opaques.opaque_diskletmk_librarysdmddigestsunivs={library_name=sd.md_name;library_data=md;library_deps=sd.md_deps;library_digests=digests;library_extra_univs=univs;}letmk_summarym={libsum_name=m.library_name;libsum_digests=m.library_digests;}letintern_from_filef=letch=raw_intern_libraryfinlet(lsd:seg_sum),digest_lsd=ObjFile.marshal_in_segmentch~segment:"summary"inlet((lmd:seg_lib),digest_lmd)=ObjFile.marshal_in_segmentch~segment:"library"inlet(univs:seg_univoption),digest_u=ObjFile.marshal_in_segmentch~segment:"universes"inlet((del_opaque:seg_proofsdelayed),_)=in_delayedfch~segment:"opaques"inObjFile.close_inch;System.check_caml_version~caml:lsd.md_ocaml~file:f;register_library_filenamelsd.md_namef;add_opaque_tablelsd.md_name(ToFetchdel_opaque);letopenSafe_typinginmatchunivswith|None->mk_librarylsdlmd(Dvo_or_vidigest_lmd)Univ.ContextSet.empty|Some(uall,true)->mk_librarylsdlmd(Dvivo(digest_lmd,digest_u))uall|Some(_,false)->mk_librarylsdlmd(Dvo_or_vidigest_lmd)Univ.ContextSet.emptyletrecintern_library~lib_resolver(needed,contents)(dir,f)from=(* Look if in the current logical environment *)try(find_librarydir).libsum_digests,(needed,contents)withNot_found->(* Look if already listed and consequently its dependencies too *)try(DPmap.finddircontents).library_digests,(needed,contents)withNot_found->Feedback.feedback(Feedback.FileDependency(from,DirPath.to_stringdir));(* [dir] is an absolute name which matches [f] which must be in loadpath *)letf=matchfwithSomef->f|None->lib_resolverdirinletm=intern_from_filefinifnot(DirPath.equaldirm.library_name)thenuser_err(str"The file "++strf++str" contains library"++spc()++DirPath.printm.library_name++spc()++str"and not library"++spc()++DirPath.printdir++str".");Feedback.feedback(Feedback.FileLoaded(DirPath.to_stringdir,f));m.library_digests,intern_library_deps~lib_resolver(needed,contents)dirmfandintern_library_deps~lib_resolverlibsdirmfrom=letneeded,contents=Array.fold_left(intern_mandatory_library~lib_resolverdirfrom)libsm.library_depsin(dir::needed,DPmap.adddirmcontents)andintern_mandatory_library~lib_resolvercallerfromlibs(dir,d)=letdigest,libs=intern_library~lib_resolverlibs(dir,None)(Somefrom)inifnot(Safe_typing.digest_match~actual:digest~required:d)thenuser_err(str"Compiled library "++DirPath.printcaller++str" (in file "++strfrom++str") makes inconsistent assumptions \
over library "++DirPath.printdir);libsletrec_intern_library~lib_resolverlibs(dir,f)=let_,libs=intern_library~lib_resolverlibs(dir,Somef)Noneinlibsletnative_name_from_filenamef=letch=raw_intern_libraryfinlet(lmd:seg_sum),digest_lmd=ObjFile.marshal_in_segmentch~segment:"summary"inNativecode.mod_uid_of_dirpathlmd.md_name(**********************************************************************)(*s [require_library] loads and possibly opens a library. This is a
synchronized operation. It is performed as follows:
preparation phase: (functions require_library* ) the library and its
dependencies are read from to disk (using intern_* )
[they are read from disk to ensure that at section/module
discharging time, the physical library referred to outside the
section/module is the one that was used at type-checking time in
the section/module]
execution phase: (through add_leaf and cache_require)
the library is loaded in the environment and Nametab, the objects are
registered etc, using functions from Declaremods (via load_library,
which recursively loads its dependencies)
*)letregister_librarym=letl=m.library_datainDeclaremods.register_librarym.library_namel.md_compiledl.md_objectsm.library_digestsm.library_extra_univs;register_loaded_library(mk_summarym)(* Follow the semantics of Anticipate object:
- called at module or module type closing when a Require occurs in
the module or module type
- not called from a library (i.e. a module identified with a file) *)letload_require_needed=List.iterregister_libraryneeded(* [needed] is the ordered list of libraries not already loaded *)letcache_requireo=load_require1oletdischarge_requireo=Someo(* open_function is never called from here because an Anticipate object *)typerequire_obj=library_tlistletin_require:require_obj->obj=declare_object{(default_object"REQUIRE")withcache_function=cache_require;load_function=load_require;open_function=(fun__->assertfalse);discharge_function=discharge_require;classify_function=(funo->Anticipate)}(* Require libraries, import them if [export <> None], mark them for export
if [export = Some true] *)letwarn_require_in_module=CWarnings.create~name:"require-in-module"~category:"fragile"(fun()->strbrk"Use of “Require” inside a module is fragile."++spc()++strbrk"It is not recommended to use this functionality in finished proof scripts.")letrequire_library_from_dirpath~lib_resolvermodrefl=letneeded,contents=List.fold_left(rec_intern_library~lib_resolver)([],DPmap.empty)modreflinletneeded=List.rev_map(fundir->DPmap.finddircontents)neededinifLib.is_module_or_modtype()thenwarn_require_in_module();Lib.add_leaf(in_requireneeded)(************************************************************************)(*s Initializing the compilation of a library. *)type('uid,'doc)tasks=(('uid,'doc)Stateid.request*bool)listletload_library_todof=letch=raw_intern_libraryfinlet(s0:seg_sum),_=ObjFile.marshal_in_segmentch~segment:"summary"inlet(s1:seg_lib),_=ObjFile.marshal_in_segmentch~segment:"library"inlet(s2:seg_univoption),_=ObjFile.marshal_in_segmentch~segment:"universes"inlet(tasks:(Opaqueproof.opaque_handleoption,'doc)tasksoption),_=ObjFile.marshal_in_segmentch~segment:"tasks"inlet(s4:seg_proofs),_=ObjFile.marshal_in_segmentch~segment:"opaques"inObjFile.close_inch;System.check_caml_version~caml:s0.md_ocaml~file:f;iftasks=Nonethenuser_err(str"Not a .vio file.");ifs2=Nonethenuser_err(str"Not a .vio file.");ifsnd(Option.gets2)thenuser_err(str"Not a .vio file.");s0,s1,Option.gets2,Option.gettasks,s4(************************************************************************)(*s [save_library dir] ends library [dir] and save it to the disk. *)letcurrent_deps()=letmapname=letm=try_find_librarynamein(name,m.libsum_digests)inList.mapmap!libraries_loaded_listleterror_recursively_dependent_librarydir=user_err(strbrk"Unable to use logical name "++DirPath.printdir++strbrk" to save current library because"++strbrk" it already depends on a library of this name.")type'doctodo_proofs=|ProofsTodoNone(* for .vo *)|ProofsTodoSomeEmptyofFuture.UUIDSet.t(* for .vos *)|ProofsTodoSomeofFuture.UUIDSet.t*(Future.UUID.t,'doc)tasks(* for .vio *)(* We now use two different digests in a .vo file. The first one
only covers half of the file, without the opaque table. It is
used for identifying this version of this library : this digest
is the one leading to "inconsistent assumptions" messages.
The other digest comes at the very end, and covers everything
before it. This one is used for integrity check of the whole
file when loading the opaque table. *)(* Security weakness: file might have been changed on disk between
writing the content and computing the checksum... *)letsave_library_basefsumlibunivstasksproofs=letch=raw_extern_libraryfintryObjFile.marshal_out_segmentch~segment:"summary"(sum:seg_sum);ObjFile.marshal_out_segmentch~segment:"library"(lib:seg_lib);ObjFile.marshal_out_segmentch~segment:"universes"(univs:seg_univoption);ObjFile.marshal_out_segmentch~segment:"tasks"(tasks:(Opaqueproof.opaque_handleoption,'doc)tasksoption);ObjFile.marshal_out_segmentch~segment:"opaques"(proofs:seg_proofs);ObjFile.close_outchwithreraise->letreraise=Exninfo.capturereraiseinObjFile.close_outch;Feedback.msg_warning(str"Removed file "++strf);Sys.removef;Exninfo.iraisereraiseletsave_library_totodo_proofs~output_native_objectsdirf=assert(letexpected_extension=matchtodo_proofswith|ProofsTodoNone->".vo"|ProofsTodoSomeEmpty_->".vos"|ProofsTodoSome_->".vio"inFilename.check_suffixfexpected_extension);letexcept=matchtodo_proofswith|ProofsTodoNone->Future.UUIDSet.empty|ProofsTodoSomeEmptyexcept->except|ProofsTodoSome(except,l)->exceptin(* Ensure that the call below is performed with all opaques joined *)let()=Opaques.Summary.join~except()inletopaque_table,f2t_map=Opaques.dump~except()inlet()=assert(not(Future.UUIDSet.is_emptyexcept)||Safe_typing.is_joined_environment(Global.safe_env()))inletcenv,seg,ast=Declaremods.end_library~output_native_objectsdirinlettasks,utab=matchtodo_proofswith|ProofsTodoNone->None,None|ProofsTodoSomeEmpty_except->None,Some(Univ.ContextSet.empty,false)|ProofsTodoSome(_except,tasks)->lettasks=List.mapStateid.(fun(r,b)->try{rwithuuid=Some(Future.UUIDMap.findr.uuidf2t_map)},bwithNot_found->assertb;{rwithuuid=None},b)tasksinSometasks,Some(Univ.ContextSet.empty,false)inletsd={md_name=dir;md_deps=Array.of_list(current_deps());md_ocaml=Coq_config.caml_version;}inletmd={md_compiled=cenv;md_objects=seg;}inifArray.exists(fun(d,_)->DirPath.equalddir)sd.md_depsthenerror_recursively_dependent_librarydir;(* Writing vo payload *)save_library_basefsdmdutabtasksopaque_table;(* Writing native code files *)ifoutput_native_objectsthenletfn=Filename.dirnamef^"/"^Nativecode.mod_uid_of_dirpathdirinNativelib.compile_libraryastfnletsave_library_rawfsumlibunivsproofs=save_library_basefsumlib(Someunivs)Noneproofsletget_used_load_paths()=String.Set.elements(List.fold_left(funaccm->String.Set.add(Filename.dirname(library_full_filenamem))acc)String.Set.empty!libraries_loaded_list)let_=Nativelib.get_load_paths:=get_used_load_paths