Skip to content
Yotam Barnoy edited this page Apr 29, 2015 · 8 revisions

The Typemod module is the entry point into the compiler's type system. It is in charge of converting untyped AST to typed AST for modules and everything they contain. The two main functions are type_interface and type_implementation.

type_implementation

  • Reset the delayed checks in the Typecore Module.
  • Something(?) is done with AST mapper.
  • type_structure is called to type the AST.
  • The resulting signature is fed to simplify_signature.
  • The function now checks for the existence of a matching interface.
    • If one is found in Config.load_path, the signature is read via Env.read_signature.
    • We check that the module matches its signature via Includemod.compunit.
    • We call force_delayed_checks, which also lets us know if anything is unused.
    • We save the .cmt file via Cmt_format.save_cmt.
    • We return the typed AST (Typedtree.structure) and the coerced type (Typedtree.module_coercion.
    • If we don't find a matching .cmi file, we call check_nongen_schemes on the typed structure and normalize the signature using normalize_signature.
    • We have no specific signature except for the simplified signature, so we feed that to Includemod.compunit.
    • We save the signature into a .cmi file using Env.save_signature
    • We save the .cmt file as above.
  • In case we encountered an exception, we save a partial type into a .cmt file using Cmt_format.save_cmt.

type_structure

  • A structure is the highest level entity in an OCaml file, as specified in the Parsetree Module. Modules can contain structures, which are all the inner elements of the module.
  • We create sets of type_names, module_names, and module type names, to prevent repeat name instances.
  • If we're asked to generate annotations, we save the location of every member in the structure using Stypes.record_phrase.
  • We enter and leave a new scope for warnings using Typetexp.warning_enter_scope and warning_leave_scope.
  • We add to the saved types in the Cmt_format Module using get_saved_types and set_saved_types.
  • For every member of the structure
    • We call Ctype.init_def with the current time from the Ident Module. This will be used as part of the scope limitation in the typing process.
    • We type the structure item, adding its description to the saved types in the Cmt_format Module.
    • We get the typed AST, the items signature if any, and the new environment.
    • If the item is a Pstr_eval, it's a toplevel expression. We call Typecore.type_expression on it, returning Tstr_eval of the result.
    • A Pstr_value (value) gets its scope (Location.t) determined, and is passed on to Typecore.type_binding, to return a Tstr_value. We also add the signature of the value, which consists of the let-bound identifiers as looked up in the new environment.
    • A Pstr_primitive (primitive operation) is already well known by the type system. All we need is to extract the primitive and its signature using Typedecl.transl_value_decl.
    • A Pstr_type (type declaration) is first checked to make sure it doesn't overlap with an existing type using the type_names set.
    • We call Transdecl.transl_type_decl to translate the type declaration to Tstr_type and return the new environment. To produce the signature, we use the map_rec'' function to add Trec_not, Trec_first and Trec_next markers to every part of the signature (row types are automatically not recursive).
      • To return the new environment we call enrich_type_decls giving it the Path.t, the old environment, and the new environment. This function adds to the environment the type declaration under the current path, if such a path exists.
      • If a path exists, we also call Mtype.enrich_typedecl to try and find the type manifest (approximation) of the new type(s). If no manifest exists for a type, enrich_typedecl tries to find an existing type with the same path, and to create a type variable with this path using Btype.newgenty.
    • A Pstr_typext (type extension, or t += ...) is sent to Typedecl.transl_type_extension. The signature gets type extension flags via map_ext.
    • A Pstr_exception is sent to Typedecl.transl_exception.
    • For a Pstr_module
      • We first check for duplicate names in the module_names set.
      • Calls type_module on the AST.
      • We call enrich_module_type, which, in the presence of a Path.t, calls Mtype.enrich_modtype, which recursively enriches types in each submodule, creating their paths.
      • We call Env.enter_module_declaration. This function creates an Ident.t for the module, which adds the module to the modules table.
    • For a Pstr_recmodule (recursive modules)
      • We check that each module is constrained by a module type. This is a requirement for recursive modules.
      • We check that the names of the modules don't conflict with an existing module in the module_names set.
      • We call transl_recmodule_modtypes on each module to get declarations for the modules. The declarations are used in signatures.
      • type_module is called on each module, as well as enrich_module_type.
      • We add each module declaration to the environment using Env.add_module_declaration.
      • check_recmodule_inclusion is run on the new bindings.
      • We add recursive variants (Trec_first, Trec_next) to each module binding using map_rec.

transl_recmodule_modtypes

  • We create ids using Ident.create for each module name.
  • For each id, we create a dummy module type with name "#recmod#", and add it to an approximate environment via Env.add_module, which calls add_module_declaration.
  • Call approx_modtype on each id,declaration pair, creating an approximate type.
  • Create an initial environment using these approximate types.
  • Call transition on this environment, and create a second environment from this.
  • Call check_recmod_typedecls
Clone this wiki locally