123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568(************************************************************************)(* * 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_library?locf=System.with_magic_number_check?loc(funfile->ObjFile.open_in~file)f(************************************************************************)(** Serialized objects loaded on-the-fly *)exceptionFaultyofstringmoduleDelayed:sigtype'adelayedvalin_delayed:string->ObjFile.in_handle->segment:'aObjFile.id->'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_syntax_objects:Declaremods.library_objects;md_objects:Declaremods.library_objects;}typesummary_disk={md_name:compilation_unit_name;md_deps:(compilation_unit_name*Safe_typing.vodigest)array;md_ocaml:string;md_info:Library_info.t;}(*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_info:Library_info.t;library_vm:Vmlibrary.on_disk;}typelibrary_summary={libsum_name:compilation_unit_name;libsum_digests:Safe_typing.vodigest;libsum_info:Library_info.t;}(* This is a map from names to loaded libraries *)letlibraries_table:library_summaryDPmap.tref=Summary.refDPmap.empty~stage:Summary.Stage.Synterp~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[]~stage:Summary.Stage.Synterp~name:"LIBRARY-LOAD"letloaded_native_libraries=Summary.refDPset.empty~stage:Summary.Stage.Interp~name:"NATIVE-LIBRARY-LOAD"(* Opaque proof tables *)(* various requests to the tables *)letfind_librarydir=DPmap.find_optdir!libraries_tablelettry_find_librarydir=matchfind_librarydirwith|Somelib->lib|None->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_library~rootm=letlibname=m.libsum_nameinletrecaux=function|[]->[root,libname]|(_,m')::_aslwhenDirPath.equalm'libname->l|m'::l'->m'::auxl'inlibraries_loaded_list:=aux!libraries_loaded_list;libraries_table:=DPmap.addlibnamem!libraries_tableletregister_native_librarylibname=if(Global.typing_flags()).enable_native_compiler&¬(DPset.memlibname!loaded_native_libraries)thenbeginletdirname=Filename.dirname(library_full_filenamelibname)inloaded_native_libraries:=DPset.addlibname!loaded_native_libraries;Nativelib.enable_librarydirnamelibnameendletloaded_libraries()=List.mapsnd!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.subst_mps_listsubcinSome(c,ctx)letindirect_accessor={Global.access_proof=access_opaque_table;}(************************************************************************)(* Internalise libraries *)typeseg_sum=summary_disktypeseg_lib=library_disktypeseg_proofs=Opaques.opaque_disktypeseg_vm=Vmlibrary.compiled_libraryletmk_librarysdmddigestsvm={library_name=sd.md_name;library_data=md;library_deps=sd.md_deps;library_digests=digests;library_info=sd.md_info;library_vm=vm;}letmk_summarym={libsum_name=m.library_name;libsum_digests=m.library_digests;libsum_info=m.library_info;}letmk_intern_librarysumlibdigest_libproofsvm=add_opaque_tablesum.md_name(ToFetchproofs);letopenSafe_typinginmk_librarysumlib(Dvo_or_vidigest_lib)vmletsummary_seg:seg_sumObjFile.id=ObjFile.make_id"summary"letlibrary_seg:seg_libObjFile.id=ObjFile.make_id"library"letopaques_seg:seg_proofsObjFile.id=ObjFile.make_id"opaques"letvm_seg:seg_vmObjFile.id=Vmlibrary.vm_segmentmoduleIntern=structmoduleProvenance=structtypet=string*string(** A pair of [kind, object], for example ["file",
"/usr/local/foo.vo"], used for error messages. *)endtypet=DirPath.t->(library_t,Exninfo.iexn)Result.t*Provenance.tendletintern_from_filefile=letch=raw_intern_libraryfileinletlsd,digest_lsd=ObjFile.marshal_in_segmentch~segment:summary_seginletlmd,digest_lmd=ObjFile.marshal_in_segmentch~segment:library_seginletdel_opaque,_=in_delayedfilech~segment:opaques_seginletvmlib=Vmlibrary.loadlsd.md_name~filechinObjFile.close_inch;System.check_caml_version~caml:lsd.md_ocaml~file;register_library_filenamelsd.md_namefile;Library_info.warn_library_info~transitive:truelsd.md_namelsd.md_info;mk_intern_librarylsdlmddigest_lmddel_opaquevmlibletintern_from_filefile=letprovenance=("file",file)in(* This is a barrier to catch IO / Marshal exceptions in a more
structured way, as to provide better error messages. *)(matchCErrors.to_result~f:intern_from_filefilewith|Okres->Okres|Erroriexn->Erroriexn),provenanceletcheck_library_expected_name~provenancedirlibrary_name=ifnot(DirPath.equaldirlibrary_name)thenletkind,obj=provenanceinuser_err(str"The "++strkind++str" "++strobj++str" contains library"++spc()++DirPath.printlibrary_name++spc()++str"and not library"++spc()++DirPath.printdir++str".")exceptionInternErrorof{exn:exn;provenance:Intern.Provenance.t;dir:DirPath.t}let()=CErrors.register_handler(function|InternError{exn;provenance;dir}->leterr=CErrors.printexninSome(Pp.(str"Error when parsing .vo (from "++str(fstprovenance)++str" "++str(sndprovenance)++str") for library "++Names.DirPath.printdir++str": "++err))|_->None)leterror_in_internprovenancedir(exn,info)=Exninfo.iraise(InternError{exn;provenance;dir},info)(* Returns the digest of a library, checks both caches to see what is loaded *)letrecintern_library~root~intern(needed,contentsasacc)dir=(* Look if in the current logical environment *)matchfind_librarydirwith|Someloaded_lib->loaded_lib,acc|None->(* Look if already listed in the accumulator *)matchDPmap.find_optdircontentswith|Someinterned_lib->mk_summaryinterned_lib,acc|None->(* We intern the library, and then intern the deps *)matchinterndirwith|Okm,provenance->check_library_expected_name~provenancedirm.library_name;mk_summarym,intern_library_deps~root~internaccdirm|Erroriexn,provenance->error_in_internprovenancediriexnandintern_library_deps~root~internlibsdirm=letneeded,contents=Array.fold_left(intern_mandatory_library~interndir)libsm.library_depsin((root,dir)::needed,DPmap.adddirmcontents)andintern_mandatory_library~interncallerlibs(dir,d)=letm,libs=intern_library~root:false~internlibsdirinletdigest=m.libsum_digestsinlet()=ifnot(Safe_typing.digest_match~actual:digest~required:d)thenletfrom=library_full_filenamecallerinuser_err(str"Compiled library "++DirPath.printcaller++str" (in file "++strfrom++str") makes inconsistent assumptions over library "++DirPath.printdir)inlibsletrec_intern_library~internlibs(loc,dir)=letm,libs=intern_library~root:true~internlibsdirinLibrary_info.warn_library_infom.libsum_namem.libsum_info;libsletnative_name_from_filenamef=letch=raw_intern_libraryfinletlmd,digest_lmd=ObjFile.marshal_in_segmentch~segment:summary_seginNativecode.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.Interp.register_librarym.library_namel.md_compiledl.md_objectsm.library_digestsm.library_vm;register_native_librarym.library_nameletregister_library_syntax(root,m)=letl=m.library_datainDeclaremods.Synterp.register_librarym.library_namel.md_syntax_objects;register_loaded_library~root(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)}letload_require_syntax_needed=List.iterregister_library_syntaxneededletcache_require_syntaxo=load_require_syntax1oletdischarge_require_syntaxo=Someo(* open_function is never called from here because an Anticipate object *)typerequire_obj_syntax=(bool*library_t)listletin_require_syntax:require_obj_syntax->obj=declare_object{(default_object"REQUIRE-SYNTAX")withobject_stage=Summary.Stage.Synterp;cache_function=cache_require_syntax;load_function=load_require_syntax;open_function=(fun__->assertfalse);discharge_function=discharge_require_syntax;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:CWarnings.CoreCategories.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_dirpathneeded=ifLib.is_module_or_modtype()thenwarn_require_in_module();Lib.add_leaf(in_requireneeded)letrequire_library_syntax_from_dirpath~internmodrefl=letneeded,contents=List.fold_left(rec_intern_library~intern)([],DPmap.empty)modreflinletneeded=List.rev_map(fun(root,dir)->root,DPmap.finddircontents)neededinLib.add_leaf(in_require_syntaxneeded);List.mapsndneeded(************************************************************************)(*s [save_library dir] ends library [dir] and save it to the disk. *)letcurrent_deps()=(* Only keep the roots of the dependency DAG *)letmap(root,m)=ifrootthenletm=try_find_libraryminSome(m.libsum_name,m.libsum_digests)elseNoneinList.map_filtermap!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 *)(* 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_basefsumlibproofsvmlib=letch=raw_extern_libraryfintryObjFile.marshal_out_segmentch~segment:summary_segsum;ObjFile.marshal_out_segmentch~segment:library_seglib;ObjFile.marshal_out_segmentch~segment:opaques_segproofs;ObjFile.marshal_out_segmentch~segment:vm_segvmlib;ObjFile.close_outchwithreraise->letreraise=Exninfo.capturereraiseinObjFile.close_outch;Feedback.msg_warning(str"Removed file "++strf);Sys.removef;Exninfo.iraisereraise(* This is the basic vo save structure *)letsave_library_struct~output_native_objectsdir=letmd_compiled,md_objects,md_syntax_objects,vmlib,ast,info=Declaremods.end_library~output_native_objectsdirinletsd={md_name=dir;md_deps=Array.of_list(current_deps());md_ocaml=Coq_config.caml_version;md_info=info}inletmd={md_compiled;md_syntax_objects;md_objects}inifArray.exists(fun(d,_)->DirPath.equalddir)sd.md_depsthenerror_recursively_dependent_librarydir;sd,md,vmlib,astletsave_librarydir:library_t=letsd,md,vmlib,_ast=save_library_struct~output_native_objects:falsedirin(* Digest for .vo files is on the md part, for now we also play it
safe when we work on-memory and compute the digest for the lib
part, even if that's slow. Better safe than sorry. *)letdigest=Marshal.to_stringmd[]|>Digest.stringinmk_librarysdmd(Dvo_or_vidigest)(Vmlibrary.injectvmlib)letsave_library_totodo_proofs~output_native_objectsdirf=assert(letexpected_extension=matchtodo_proofswith|ProofsTodoNone->".vo"|ProofsTodoSomeEmpty_->".vos"inFilename.check_suffixfexpected_extension);letexcept=matchtodo_proofswith|ProofsTodoNone->Future.UUIDSet.empty|ProofsTodoSomeEmptyexcept->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()))inletsd,md,vmlib,ast=save_library_struct~output_native_objectsdirin(* Writing vo payload *)save_library_basefsdmdopaque_tablevmlib;(* Writing native code files *)ifoutput_native_objectsthenletfn=Filename.dirnamef^"/"^Nativecode.mod_uid_of_dirpathdirinNativelib.compile_libraryastfnletget_used_load_paths()=String.Set.elements(List.fold_left(funacc(root,m)->String.Set.add(Filename.dirname(library_full_filenamem))acc)String.Set.empty!libraries_loaded_list)let_=Nativelib.get_load_paths:=get_used_load_paths