Module Shape
Shapes are an abstract representation of modules' implementations which allow the tracking of definitions through functor applications and other module-level operations.
The Shape of a compilation unit is elaborated during typing, partially reduced (without loading external shapes) and written to the cmt file.
External tools can retrieve the definition of any value (or type, or module, etc) by following this procedure:
- Build the Shape corresponding to the value's path:
let shape = Env.shape_of_path ~namespace env path
- Instantiate the
Shape_reduce.Makefunctor with a way to load shapes from external units and to looks for shapes in the environment (usually usingEnv.shape_of_path).
- Completely reduce the shape:
let shape = My_reduce.(weak_)reduce env shape
- The
Uid.tstored in the reduced shape should be the one of the definition. However, if theapproximatefield of the reduced shape istruethen theUid.twill not correspond to the definition, but to the closest parent module's uid. This happens when Shape reduction gets stuck, for example when hitting first-class modules.
- The location of the definition can be easily found with the
cmt_format.cmt_uid_to_decltable of the corresponding compilation unit.
See:
module Layout = Jkind_types.Sort.Consttype base_layout = Jkind_types.Sort.basemodule Uid : sig ... endA Uid.t is associated to every declaration in signatures and implementations. They uniquely identify bindings in the program. When associated with these bindings' locations they are useful to external tools when trying to jump to an identifier's declaration or definition. They are stored to that effect in the uid_to_decl table of cmt files.
module DeBruijn_index : sig ... endWe use de Bruijn indices for some binders in Shape.t below to increase sharing. That is, de Bruijn indices ensure that alpha-equivalent terms are actually equal. This reduces redundancy when we emit shape information into the debug information in later stages of the compiler (see dwarf_type.ml), since equal shapes produce the same debug information.
module Sig_component_kind : sig ... endmodule Item : sig ... endShape's items are elements of a structure or, in the case of constructors and labels, elements of a record or variants definition seen as a structure. These structures model module components and nested types' constructors and labels.
module Predef : sig ... endtype var = Ident.tand desc = | Var of Shape.var| Abs of Shape.var * Shape.t| App of Shape.t * Shape.t| Struct of Shape.t Shape.Item.Map.t| Alias of Shape.t| Leaf| Proj of Shape.t * Shape.Item.t| Comp_unit of string| Error of string| Constr of Ident.t * Shape.t list| Tuple of Shape.t list| Unboxed_tuple of Shape.t list| Predef of Shape.Predef.t * Shape.t list| Arrow| Poly_variant of Shape.t Shape.poly_variant_constructors| Mu of Shape.t(*
*)Mu trepresents a binder for a recursive type with bodyt. Its variables areRec_var nbelow, wherenis a DeBruijn-index to maximize sharing between alpha-equivalent shapes.| Rec_var of Shape.DeBruijn_index.t| Variant of (Shape.t * Layout.t) Shape.complex_constructors| Variant_unboxed of {name : string;variant_uid : Shape.Uid.t option;arg_name : string option;(*if this is
*)None, we are looking at a singleton tuple; otherwise, it is a singleton record.arg_uid : Shape.Uid.t option;arg_shape : Shape.t;arg_layout : Layout.t;
}(*An unboxed variant corresponds to the
*)@@unboxedannotation. It must have a single, complex constructor.| Record of {fields : (string * Shape.Uid.t option * Shape.t * Layout.t) list;kind : Shape.record_kind;
}| Mutrec of Shape.t Ident.Map.t(*
*)Mutrec mrepresents a map of (potentially mutually-recursive) declarations. Declarations with type variables are represented as abstractions inside. To project out a declaration,Proj_declcan be used.| Proj_decl of Shape.t * Ident.t
For DWARF type emission to work as expected, we store the layouts in the declaration alongside the shapes in those cases where the layout "expands" again such as variant constructors, which themselves are values but point to blocks in memory. Here, layouts are stored for the individual fields.
and 'a poly_variant_constructors = 'a Shape.poly_variant_constructor listand record_kind = | Record_unboxed(*
*)Record_unboxedis the case for single-field records declared with@@unboxed, whose runtime representation is simply its contents without any indirection.| Record_unboxed_product(*
*)Record_unboxed_productis the truly unboxed record that corresponds to#{ ... }.| Record_boxed| Record_mixed of Shape.mixed_product_shape| Record_floats(*Basically the same as
*)Record_mixed, but we don't reorder the fields.
and 'a complex_constructors = 'a Shape.complex_constructor listand 'a complex_constructor = {name : string;constr_uid : Shape.Uid.t option;kind : Shape.constructor_representation;args : 'a Shape.complex_constructor_argument list;
}and 'a complex_constructor_argument = {field_name : string option;field_uid : Shape.Uid.t option;field_value : 'a;
}and constructor_representation = Shape.mixed_product_shapeand mixed_product_shape = Layout.t arrayval print : Stdlib.Format.formatter -> Shape.t -> unitval equal_record_kind : Shape.record_kind -> Shape.record_kind -> boolval equal_complex_constructor :
('a -> 'a -> bool) ->
'a Shape.complex_constructor ->
'a Shape.complex_constructor ->
boolval for_unnamed_functor_param : Shape.varval fresh_var : ?name:string -> Shape.Uid.t -> Shape.var * Shape.tval var : Shape.Uid.t -> Ident.t -> Shape.tval var' : Shape.Uid.t option -> Ident.t -> Shape.tval abs : ?uid:Shape.Uid.t -> Shape.var -> Shape.t -> Shape.tval app : ?uid:Shape.Uid.t -> Shape.t -> arg:Shape.t -> Shape.tval str : ?uid:Shape.Uid.t -> Shape.t Shape.Item.Map.t -> Shape.tval alias : ?uid:Shape.Uid.t -> Shape.t -> Shape.tval error : ?uid:Shape.Uid.t -> string -> Shape.tval proj : ?uid:Shape.Uid.t -> Shape.t -> Shape.Item.t -> Shape.tval leaf : Shape.Uid.t -> Shape.tval leaf' : Shape.Uid.t option -> Shape.tval comp_unit : ?uid:Shape.Uid.t -> string -> Shape.tval constr : ?uid:Shape.Uid.t -> Ident.t -> Shape.t list -> Shape.tval tuple : ?uid:Shape.Uid.t -> Shape.t list -> Shape.tval unboxed_tuple : ?uid:Shape.Uid.t -> Shape.t list -> Shape.tval predef : ?uid:Shape.Uid.t -> Shape.Predef.t -> Shape.t list -> Shape.tval arrow : ?uid:Shape.Uid.t -> unit -> Shape.tval poly_variant :
?uid:Shape.Uid.t ->
Shape.t Shape.poly_variant_constructors ->
Shape.tval mu : ?uid:Shape.Uid.t -> Shape.t -> Shape.tval rec_var : ?uid:Shape.Uid.t -> Shape.DeBruijn_index.t -> Shape.tval variant :
?uid:Shape.Uid.t ->
(Shape.t * Layout.t) Shape.complex_constructors ->
Shape.tval variant_unboxed :
?uid:Shape.Uid.t ->
variant_uid:Shape.Uid.t option ->
arg_uid:Shape.Uid.t option ->
string ->
string option ->
Shape.t ->
Layout.t ->
Shape.tval record :
?uid:Shape.Uid.t ->
Shape.record_kind ->
(string * Shape.Uid.t option * Shape.t * Layout.t) list ->
Shape.tval mutrec : ?uid:Shape.Uid.t -> Shape.t Ident.Map.t -> Shape.tval proj_decl : ?uid:Shape.Uid.t -> Shape.t -> Ident.t -> Shape.tval for_persistent_unit : string -> Shape.tval leaf_for_unpack : Shape.tval poly_variant_constructors_map :
('a -> 'b) ->
'a Shape.poly_variant_constructors ->
'b Shape.poly_variant_constructorsval complex_constructor_map :
('a -> 'b) ->
'a Shape.complex_constructor ->
'b Shape.complex_constructorval complex_constructors_map :
('a -> 'b) ->
'a Shape.complex_constructors ->
'b Shape.complex_constructorsmodule Map : sig ... endval dummy_mod : Shape.tval of_path :
find_shape:(Shape.Sig_component_kind.t -> Ident.t -> Shape.t) ->
namespace:Shape.Sig_component_kind.t ->
Path.t ->
Shape.tThis function returns the shape corresponding to a given path. It requires a callback to find shapes in the environment. It is generally more useful to rely directly on the Env.shape_of_path function to get the shape associated with a given path.
val set_uid_if_none : Shape.t -> Shape.Uid.t -> Shape.tmodule Cache : Stdlib.Hashtbl.S with type key = Shape.tmodule DeBruijn_env : sig ... endDeBruijn Environment for working with the recursive binders.