PreviousUpNext

15.3.46  src/lib/c-glue-lib/c.api

#
# Encoding the C type system in Mythryl.
#
#   (C) 2001, Lucent Technologies, Bell Laboratories
#
# author: Matthias Blume

# Compiled by:
#     src/lib/c-glue-lib/internals/c-internals.lib

api Ctypes {


    exception OUT_OF_MEMORY;



    # Chunks of type T, constness C.
    #
    # The T type variable will be the chunk's "witness" type.
    # The intention is that there be an isomorphism between such
    # witness types and corresponding C types.
    #
    # Witness types are often the same as the (abstract) type of the value
    # stored in the chunk.  However, this is merely a coincidence.  For
    # example, a constant chunk holding a pointer to a read-write integer
    # would have type Chunk (Ptr (Sint, Rw), Ro)  and the value itself has
    # type Ptr (Sint, Rw).
    # However, in the case of the "light" version of this chunk (see below),
    # the chunk type is Chunk' (Ptr (Sint, Rw), Ro)  while fetching
    # from this chunk gives a value of type Ptr' (Sint, Rw).
    # (In other words, we use the "heavy" versions of value types as witness
    # types -- even in the "light" case.)

    Chunk (T, C);


    # An alternative "light-weight" version that does not carry
    # run-time type information (RTTI) at the cost of requiring
    # explicit passing of RTTI for certain operations:

    eqtype Chunk' (T, C);



    #  Constness property, to be substituted for C 

    Ro also Rw;

    # Pointers come in two varieties in C:  Pointers to things we
    # know and pointers to "incomplete" types.  The "ptr" type constructor
    # below encodes both kinds using the following convention:
    #
    #   - In the case of complete target types, O will be
    #     some Chunk(T, C). 
    #
    #   - In the case of incomplete target types, O will
    #     some fresh (abstract) type. (See iptr.api for what this will
    #     look like in practice)

    Ptr( O );                           #  pointer to O 
    Arr( T, N );                        #  N-sized Rw_Vector with T elements 

    # Light-weight alternative (no implicit RTTI):
    eqtype Ptr'( O );

    eqtype Void;                        #  no values, admits equality 

    # void* is really a base type, but it happens to take the
    # form of a light-weight pointer type (with an abstract target).
    # This design makes it possible to use those ptr-related
    # functions that "make sense" for void*.
    Voidptr =  Ptr'( Void );    #  C's void* 

    # Function pointers 
    Fptr( F );                  #  A function pointer 

    # Lightweight function pointers:
    eqtype Fptr'( F );

    # Structures and unions:
    Su( A_tag );                        # struct/union named A_tag;
                                        # A_tag is drawn from the types
                                        # defined in the Tag module

    # Enumerations:
    eqtype An_Enum( A_tag );


    # Primtypes (signed/unsigned char, int, short, long; float, double) 
    eqtype Schar     also Uchar;
    eqtype Sint      also Uint;
    eqtype Sshort    also Ushort;
    eqtype Slong     also Ulong;
    eqtype Slonglong also Ulonglong;
    Float            also Double;

    # Going from abstract to concrete and vice versa.
    # (This shouldn't be needed except when calling
    # functions through function pointers.)
    package convert:
        api {
            #  Mythryl -> C 

            c_schar:      mlrep::signed::Int              -> Schar;
            c_uchar:      mlrep::unsigned::Unt           -> Uchar;

            c_sint:       mlrep::signed::Int              -> Sint;
            c_uint:       mlrep::unsigned::Unt           -> Uint;

            c_sshort:     mlrep::signed::Int              -> Sshort;
            c_ushort:     mlrep::unsigned::Unt           -> Ushort;

            c_slong:      mlrep::signed::Int              -> Slong;
            c_ulong:      mlrep::unsigned::Unt           -> Ulong;

            c_slonglong:  mlrep::long_long_signed::Int    -> Slonglong;
            c_ulonglong:  mlrep::long_long_unsigned::Unt -> Ulonglong;

            c_float:      mlrep::float::Float              -> Float;
            c_double:     mlrep::float::Float              -> Double;

            i2c_enum:     mlrep::signed::Int              -> An_Enum( E );



            #  C -> Mythryl

            ml_schar:     Schar     -> mlrep::signed::Int;
            ml_uchar:     Uchar     -> mlrep::unsigned::Unt;

            ml_sint:      Sint      -> mlrep::signed::Int;
            ml_uint:      Uint      -> mlrep::unsigned::Unt;

            ml_sshort:    Sshort    -> mlrep::signed::Int;
            ml_ushort:    Ushort    -> mlrep::unsigned::Unt;

            ml_slong:     Slong     -> mlrep::signed::Int;
            ml_ulong:     Ulong     -> mlrep::unsigned::Unt;

            ml_slonglong: Slonglong -> mlrep::long_long_signed::Int;
            ml_ulonglong: Ulonglong -> mlrep::long_long_unsigned::Unt;

            ml_float:     Float     -> mlrep::float::Float;
            ml_double:    Double    -> mlrep::float::Float;

            c2i_enum:     An_Enum( E )   -> mlrep::signed::Int;
        };


    # Type-abbreviations for a bit more convenience. 

    Schar_Chunk(     C ) =   Chunk( Schar, C ); 
    Uchar_Chunk(     C ) =   Chunk( Uchar, C ); 

    Sint_Chunk(      C ) =   Chunk( Sint, C ); 
    Uint_Chunk(      C ) =   Chunk( Uint, C ); 

    Sshort_Chunk(    C ) =   Chunk( Sshort, C ); 
    Ushort_Chunk(    C ) =   Chunk( Ushort, C ); 

    Slong_Chunk(     C ) =   Chunk( Slong, C ); 
    Ulong_Chunk(     C ) =   Chunk( Ulong, C ); 

    Slonglong_Chunk( C ) =   Chunk( Slonglong, C ); 
    Ulonglong_Chunk( C ) =   Chunk( Ulonglong, C ); 

    Float_Chunk(     C ) =   Chunk( Float, C ); 
    Double_Chunk(    C ) =   Chunk( Double, C ); 

    Voidptr_Chunk(   C ) =   Chunk( Voidptr, C ); 

    Enum_Chunk(   E, C ) =   Chunk( An_Enum( E ), C );
    Fptr_Chunk(   F, C ) =   Chunk( Fptr( F ), C ); 

    Su_Chunk(     S, C ) =   Chunk( Su( S ), C ); 



    #  Alternate lightweight versions (no implicit RTTI):

    Schar_Chunk'(     C ) =   Chunk'( Schar, C ); 
    Uchar_Chunk'(     C ) =   Chunk'( Uchar, C ); 

    Sint_Chunk'(      C ) =   Chunk'( Sint, C );
    Uint_Chunk'(      C ) =   Chunk'( Uint, C ); 

    Sshort_Chunk'(    C ) =   Chunk'( Sshort, C ); 
    Ushort_Chunk'(    C ) =   Chunk'( Ushort, C ); 

    Slong_Chunk'(     C ) =   Chunk'( Slong, C ); 
    Ulong_Chunk'(     C ) =   Chunk'( Ulong, C ); 

    Slonglong_Chunk'( C ) =   Chunk'( Slonglong, C );
    Ulonglong_Chunk'( C ) =   Chunk'( Ulonglong, C );

    Float_Chunk'(     C ) =   Chunk'( Float, C ); 
    Double_Chunk'(    C ) =   Chunk'( Double, C ); 

    Voidptr_Chunk'(   C ) =   Chunk'( Voidptr, C ); 

    Enum_Chunk'   (E, C ) =   Chunk'( An_Enum( E ), C ); 
    Fptr_Chunk'   (F, C ) =   Chunk'( Fptr( F ), C); 
    Su_Chunk'     (S, C ) =   Chunk'( Su( S ), C ); 



    # Bitfields aren't "ordinary chunks",
    # so they have their own type:
    eqtype Sbf C                                # "Sbf" ==   "signed bitfield"
    also   Ubf C;                               # "Ubf" == "unsigned bitfield"

    package w:
        api {
            #  Conversion "witness" values 
            Witness( A_from, A_to );

            # A small calculus for generating new witnesses.
            # Since the only witness constructors that do anything real are
            # rw and ro, all this calculus gives you is a way of changing
            # "const" attributes at any level within a bigger type.
            #
            # (The calculus can express nonsensical witnesses -- which
            # fortunately are harmless because they can't be applied to any
            # values.)
            trivial:  Witness( T, T );

            pointer:  Witness( A_from, A_to ) ->   Witness( Ptr( A_from ), Ptr( A_to ) );

            chunk:    Witness( A_st,   A_tt ) ->   Witness (Chunk( A_st, C ), Chunk( A_tt, C ));
            arr:      Witness( A_st,   A_tt ) ->   Witness (Arr( A_st, N ),   Arr( A_tt, N ));

            ro:       Witness( A_st,   A_tt ) ->   Witness (Chunk( A_st, C ),    Chunk( A_tt, Ro ));
            rw:       Witness( A_st,   A_tt ) ->   Witness (Chunk( A_st, A_sc ), Chunk( A_tt, A_tc ));
        };

    #  Chunk conversions that rely on witnesses: 
    convert:   w::Witness (Chunk( A_st, A_sc ), Chunk( A_tt, A_tc )) -> Chunk( A_st,  A_sc ) -> Chunk ( A_tt, A_tc );
    convert':  w::Witness (Chunk( A_st, A_sc ), Chunk( A_tt, A_tc )) -> Chunk'( A_st, A_sc ) -> Chunk'( A_tt, A_tc );



    # A family of types and corresponding values representing natural numbers.
    # (An encoding in Mythryl without using dependent types.)

    package dim:
        api {

            # Internally, a value of type Dim0 (X, Z) is just a number.
            # The trick here is to give each of these numbers a different unique
            # type. X will be a decimal encoding of the number's value in
            # "type digits". Z keeps track of whether the number is zero or not.

            Dim0( X, Z );

            #  We can always get the internal number back... 
            to_int:  Dim0( X, Z ) -> Int;

            # These two types act as "flags". They will be substituted for Z
            # and remember whether the value is zero or not.
            Zero;
            Nonzero;

            Dim(X) = Dim0( X, Nonzero ); 

            # These are the "type digits".  Type "Dec" acts as a "terminator".
            # We chose its name so to remind us that the encoding is decimal.
            # If a nonzero value's decimal representation is "<Dn>...<D0>", then
            # its type will be "Dim0 ( dg<D0> ... Dg<Dn> Dec, Nonzero) ", which
            # is the same as "Dim Dg<D0> ... Dg<Dn> Dec".  The type of the zero
            # value is "Dim0( Dec, Zero )".
            Dec;
            Dg0(X) also Dg1(X) also Dg2(X) also Dg3(X) also Dg4(X);
            Dg5(X) also Dg6(X) also Dg7(X) also Dg8(X) also Dg9(X);

            # Here are the corresponding constructors for Dim0 (X, Z) values.
            # The type for dg0 ensures that there will be no "leading zeros" in
            # any encoding.  This guarantees a 1-1 correspondence of constructable
            # values and their types.
            # To construct the value corresponding to a nonzero number whose
            # decimal representation is "<Dn>...<D0>", one must invoke
            # "dg<D0>' (... (dg<Dn>' dec')...)", i.e., the least significant
            # digit appears leftmost.
            dec' : Dim0( Dec, Zero );
            dg0' : Dim(X)     -> Dim( Dg0(X) );
            dg1' : Dim0( X, Z ) -> Dim( Dg1(X) );
            dg2' : Dim0( X, Z ) -> Dim( Dg2(X) );
            dg3' : Dim0( X, Z ) -> Dim( Dg3(X) );
            dg4' : Dim0( X, Z ) -> Dim( Dg4(X) );
            dg5' : Dim0( X, Z ) -> Dim( Dg5(X) );
            dg6' : Dim0( X, Z ) -> Dim( Dg6(X) );
            dg7' : Dim0( X, Z ) -> Dim( Dg7(X) );
            dg8' : Dim0( X, Z ) -> Dim( Dg8(X) );
            dg9' : Dim0( X, Z ) -> Dim( Dg9(X) );

            # Since having to reverse the sequence of digits seems unnatural,
            # here is another set of constructors for dim values.  These
            # constructors use fate-passing style and themselves
            # have more complicated types.  But their use is easier:
            # To construct the value corresponding to a nonzero number whose
            # decimal representation is "<Dn>...<D0>", one must invoke
            # "dec dg<Dn> ... dg<D0> dim"; i.e., the least significant
            # digit appears rightmost -- just like in the usual decimal
            # notation for numbers that we are all familiar with.
            #
            # Moreover, for any dim(X) value we have the neat property that
            # it can be constructed by taking its type (expressed using "dim")
            # and interpreting it as an expression.  For example, the dim
            # value representing 312 is "dec dg3 dg1 dg2 dim" and it can
            # be constructed by evaluating "dec dg3 dg1 dg2 dim".
            dec:  (Dim0( Dec, Zero ) -> Y) -> Y;

            dg0:  Dim(X)     -> (Dim( Dg0(X) ) -> Y) -> Y;
            dg1:  Dim0( X, Z ) -> (Dim( Dg1(X) ) -> Y) -> Y;
            dg2:  Dim0( X, Z ) -> (Dim( Dg2(X) ) -> Y) -> Y;
            dg3:  Dim0( X, Z ) -> (Dim( Dg3(X) ) -> Y) -> Y;
            dg4:  Dim0( X, Z ) -> (Dim( Dg4(X) ) -> Y) -> Y;
            dg5:  Dim0( X, Z ) -> (Dim( Dg5(X) ) -> Y) -> Y;
            dg6:  Dim0( X, Z ) -> (Dim( Dg6(X) ) -> Y) -> Y;
            dg7:  Dim0( X, Z ) -> (Dim( Dg7(X) ) -> Y) -> Y;
            dg8:  Dim0( X, Z ) -> (Dim( Dg8(X) ) -> Y) -> Y;
            dg9:  Dim0( X, Z ) -> (Dim( Dg9(X) ) -> Y) -> Y;

            dim:  Dim0( X, Z ) -> Dim0( X, Z );
        };

    # A sub-package for dealing with run-time type info (sizes only): 
    package s:
        api {
            # Our size info itself is statically typed!
            # The size info for a value stored in chunk( T, C ) has
            # the following type:
            Size( T );

            # Get a number out: 
            to_word:  Size( T ) -> Unt;


            # Sizes for simple things: 

            schar:      Size(  Schar );
            uchar:      Size(  Uchar );

            sint:       Size(  Sint );
            uint:       Size(  Uint );

            sshort:     Size(  Sshort );
            ushort:     Size(  Ushort );

            slong:      Size(  Slong );
            ulong:      Size(  Ulong );

            slonglong:  Size(  Slonglong );
            ulonglong:  Size(  Ulonglong );

            float:      Size(  Float );
            double:     Size(  Double );

            voidptr:    Size(  Voidptr );

            ptr:        Size(  Ptr(  O ) );
            fptr:       Size(  Fptr( F ) );

            an_enum:    Size(  An_Enum(  A_tag ) );
        };

    # Sub-package for dealing with run-time type info: 
    package t:
        api {

            # Our RTTI itself is statically typed!
            # The RTTI for a value stored in chunk( T, C ) has
            # the following type:
            Type( T );

            # Get the RTTI from an actual chunk:
            typeof:  Chunk( T, C ) -> Type( T );

            #  Construct a new RTTI from existing RTTI:
            pointer:  Type( T ) -> Type( Ptr( Chunk( T, Rw ) ) );
            target:   Type( Ptr( Chunk( T, C ) ) ) -> Type( T );
            arr:      (Type( T ), dim::Dim( N )) -> Type( Arr( T, N ) );
            element:  Type( Arr( T, N ) ) -> Type( T );
            ro:       Type( Ptr( Chunk( T, C ) ) ) -> Type( Ptr( Chunk( T, Ro ) ) );

            # Calculate the size of a chunk given its RTTI:
            sizeof:  Type( T ) -> s::Size( T );

            # Dimension of rw_vector type 
            dim:  Type( Arr( T, N ) ) -> dim::Dim( N );


            # RTTI for simple things:
            schar:      Type(  Schar );
            uchar:      Type(  Uchar );

            sint:       Type(  Sint );
            uint:       Type(  Uint );

            sshort:     Type(  Sshort );
            ushort:     Type(  Ushort );

            slong:      Type(  Slong );
            ulong:      Type(  Ulong );

            slonglong:  Type(  Slonglong );
            ulonglong:  Type(  Ulonglong );

            float:      Type(  Float );
            double:     Type(  Double );

            voidptr:    Type(  Voidptr );

            an_enum:    Type(  An_Enum(  A_tag ) );
        };



    # Convert from regular (heavy)
    # to alternative (light) versions: 

    package light:
        api {
            chunk:  Chunk( T, C ) ->   Chunk'( T, C );
            ptr:    Ptr(  O )     ->   Ptr'( O );
            fptr:   Fptr( F )     ->   Fptr'( F );
        };



    # And vice versa:

    package heavy:
        api {
            chunk:  t::Type( T )         ->   Chunk'( T, C ) -> Chunk( T, C );
            ptr:    t::Type( Ptr( O ) )  ->   Ptr'(  O )     -> Ptr( O );
            fptr:   t::Type( Fptr( F ) ) ->   Fptr'( F )     -> Fptr( F );
        };

    #  Calculate size of an chunk 
    sizeof:  Chunk( T, C ) -> s::Size( T );

    # "fetch" methods for various types;
    # fetching does not care about constness
    package get:
        api {
            #  Primitive types.  The results are concrete here:

            schar:      Schar_Chunk(     C ) ->   mlrep::signed::Int;
            uchar:      Uchar_Chunk(     C ) ->   mlrep::unsigned::Unt;

            sint:       Sint_Chunk(      C ) ->   mlrep::signed::Int;
            uint:       Uint_Chunk(      C ) ->   mlrep::unsigned::Unt;

            sshort:     Sshort_Chunk(    C ) ->   mlrep::signed::Int;
            ushort:     Ushort_Chunk(    C ) ->   mlrep::unsigned::Unt;

            slong:      Slong_Chunk(     C ) ->   mlrep::signed::Int;
            ulong:      Ulong_Chunk(     C ) ->   mlrep::unsigned::Unt;

            slonglong:  Slonglong_Chunk( C ) ->   mlrep::long_long_signed::Int;
            ulonglong:  Ulonglong_Chunk( C ) ->   mlrep::long_long_unsigned::Unt;

            float:      Float_Chunk(     C ) ->   mlrep::float::Float;
            double:     Double_Chunk(    C ) ->   mlrep::float::Float;

            an_enum:    Enum_Chunk(   E, C ) ->   mlrep::signed::Int;



            #  Alt 

            schar'     : Schar_Chunk'(     C ) -> mlrep::signed::Int;
            uchar'     : Uchar_Chunk'(     C ) -> mlrep::unsigned::Unt;

            sint'      : Sint_Chunk'(      C ) -> mlrep::signed::Int;
            uint'      : Uint_Chunk'(      C ) -> mlrep::unsigned::Unt;

            sshort'    : Sshort_Chunk'(    C ) -> mlrep::signed::Int;
            ushort'    : Ushort_Chunk'(    C ) -> mlrep::unsigned::Unt;

            slong'     : Slong_Chunk'(     C ) -> mlrep::signed::Int;
            ulong'     : Ulong_Chunk'(     C ) -> mlrep::unsigned::Unt;

            slonglong' : Slonglong_Chunk'( C ) -> mlrep::long_long_signed::Int;
            ulonglong' : Ulonglong_Chunk'( C ) -> mlrep::long_long_unsigned::Unt;

            float'     : Float_Chunk'(     C ) -> mlrep::float::Float;
            double'    : Double_Chunk'(    C ) -> mlrep::float::Float;

            an_enum'   : Enum_Chunk'(   E, C ) -> mlrep::signed::Int;

            # Fetching pointers.  Results have to be abstract:
            ptr:      Chunk( Ptr( O ), C ) ->   Ptr( O );
            fptr:     Fptr_Chunk(   F, C ) ->   Fptr( F );
            voidptr:  Voidptr_Chunk(   C ) ->   Voidptr;

            #  Alt 
            ptr' :     Chunk'( Ptr( O ), C ) ->   Ptr'( O );
            fptr' :    Fptr_Chunk'(   F, C ) ->   Fptr'( F );
            voidptr' : Voidptr_Chunk'(   C ) ->   Voidptr;

            #  Bitfields.  Concrete again:
            sbf:  Sbf( C ) ->   mlrep::signed::Int;
            ubf:  Ubf( C ) ->   mlrep::unsigned::Unt;
        };

    #  "store" methods.  These require rw chunks:
    package set:
        api {
            # Primitive types.  Use concrete values:
            schar:      (Schar_Chunk(     Rw ),  mlrep::signed::Int)              -> Void;
            uchar:      (Uchar_Chunk(     Rw ),  mlrep::unsigned::Unt)           -> Void;
            sint:       (Sint_Chunk(      Rw ),  mlrep::signed::Int)              -> Void;
            uint:       (Uint_Chunk(      Rw ),  mlrep::unsigned::Unt)           -> Void;
            sshort:     (Sshort_Chunk(    Rw ),  mlrep::signed::Int)              -> Void;
            ushort:     (Ushort_Chunk(    Rw ),  mlrep::unsigned::Unt)           -> Void;
            slong:      (Slong_Chunk(     Rw ),  mlrep::signed::Int)              -> Void;
            ulong:      (Ulong_Chunk(     Rw ),  mlrep::unsigned::Unt)           -> Void;
            slonglong:  (Slonglong_Chunk( Rw ),  mlrep::long_long_signed::Int)    -> Void;
            ulonglong:  (Ulonglong_Chunk( Rw ),  mlrep::long_long_unsigned::Unt) -> Void;
            float:      (Float_Chunk(     Rw ),  mlrep::float::Float)              -> Void;
            double:     (Double_Chunk(    Rw ),  mlrep::float::Float)              -> Void;
            an_enum:    (Enum_Chunk( E,   Rw ),  mlrep::signed::Int)              -> Void;

            #  Alternative lightweight versions (no implicit RTTI):
            schar'     : (Schar_Chunk'(     Rw ),  mlrep::signed::Int)              -> Void;
            uchar'     : (Uchar_Chunk'(     Rw ),  mlrep::unsigned::Unt)           -> Void;
            sint'      : (Sint_Chunk'(      Rw ),  mlrep::signed::Int)              -> Void;
            uint'      : (Uint_Chunk'(      Rw ),  mlrep::unsigned::Unt)           -> Void;
            sshort'    : (Sshort_Chunk'(    Rw ),  mlrep::signed::Int)              -> Void;
            ushort'    : (Ushort_Chunk'(    Rw ),  mlrep::unsigned::Unt)           -> Void;
            slong'     : (Slong_Chunk'(     Rw ),  mlrep::signed::Int)              -> Void;
            ulong'     : (Ulong_Chunk'(     Rw ),  mlrep::unsigned::Unt)           -> Void;
            slonglong' : (Slonglong_Chunk'( Rw ),  mlrep::long_long_signed::Int)    -> Void;
            ulonglong' : (Ulonglong_Chunk'( Rw ),  mlrep::long_long_unsigned::Unt) -> Void;
            float'     : (Float_Chunk'(     Rw ),  mlrep::float::Float)              -> Void;
            double'    : (Double_Chunk'(    Rw ),  mlrep::float::Float)              -> Void;
            an_enum'   : (Enum_Chunk'(   E, Rw ),  mlrep::signed::Int)              -> Void;

            # Storing pointers.   Abstract:
            ptr:   (Chunk( Ptr( O ), Rw ), Ptr( O )) -> Void;
            fptr:   (Fptr_Chunk( F, Rw ), Fptr( F )) -> Void;
            voidptr:  (Voidptr_Chunk( Rw ), Voidptr) -> Void;

            #  Alternative lightweight variants:
            ptr' : (Chunk'( Ptr( O ), Rw ), Ptr'( O )) -> Void;
            fptr' : (Fptr_Chunk'( F, Rw ), Fptr'( F )) -> Void;
            voidptr' : (Voidptr_Chunk'( Rw ), Voidptr) -> Void;

            # When storing, voidptr is compatible
            # with any ptr type -- just as in C.
            #
            # This should eliminate most need for
            # run-time type information in practice:
            ptr_voidptr:  (Chunk( Ptr( O ), Rw ), Voidptr) -> Void;

            #  Alt 
            ptr_voidptr' : (Chunk'( Ptr( O ), Rw ), Voidptr) -> Void;

            #  Bitfields; concrete 
            sbf:  (Sbf( Rw ),  mlrep::signed::Int)    -> Void;
            ubf:  (Ubf( Rw ),  mlrep::unsigned::Unt) -> Void;
        };

    # Copy contents of an arbitrary chunk:
    copy:  { from: Chunk( T, C ),
             to:   Chunk( T, Rw ) } -> Void;

    #  Alt 
    copy' : s::Size( T ) -> { from: Chunk'( T, C ),
                              to:   Chunk'( T, Rw ) } -> Void;

    # Manipulating chunk constness:
    # rw -> ro:  This direction just accounts for the fact that
    #            rw is conceptually a subtype of ro
    # ro -> rw:  This is not safe, but C makes it so easy that we
    #            might as well directly support it;
    # Concretely, we make both operations typeagnostic in the argument
    # constness.  Moreover, the second (unsafe) direction is also
    # typeagnostic in the result.  This can be used to effectively
    # implement a conversion to "whatever the context wants".
    #
    # Note: fun ro x = convert (w::ro w::trivial) x
    #       etc.

    ro:  Chunk( T, C )    -> Chunk( T, Ro );
    rw:  Chunk( T, A_sc ) -> Chunk( T, A_tc );

    #  Alt 
    ro' : Chunk'( T, C )  -> Chunk'( T, Ro );
    rw' : Chunk'( T, A_sc ) -> Chunk'( T, A_tc );



    # Operations on (mostly) pointers:

    package ptr:
        api {
            # Going from chunk to pointer and vice versa, like C &val and *ptr:
            enref:      Chunk( T, C )    ->   Ptr( Chunk( T, C ) );
            deref: Ptr( Chunk( T, C ) )  ->        Chunk( T, C );

            # Alternate "lightweight" versions (no implicit run-time type info):
            enref' :       Chunk'( T, C )   ->   Ptr'( Chunk ( T, C ) );
            deref' : Ptr'( Chunk ( T, C ) )  ->        Chunk'( T, C );

            #  Comparing pointers 
            compare:  (Ptr( O ), Ptr( O ))  ->  Order;

            #  Alt 
            compare' : (Ptr'( O ), Ptr'( O ))  ->  Order;

            # Going from pointer to void*;  this also accounts for a conceptual
            # subtyping relation and is safe
            inject:  Ptr( O ) -> Voidptr;

            #  Alt 
            inject' : Ptr'( O ) -> Voidptr;


            # The opposite is not safe, but C makes it
            # not only easy but also almost necessary.
            #
            # We use our RTTI interface to specify
            # the pointer type (not the element type!)

            cast:  t::Type( Ptr( O ) ) -> Voidptr -> Ptr( O );



            #  Alt, needs explicit type constraint on result! 

            cast' : Voidptr -> Ptr'( O );


            #  NULL as void* 
            v_null:  Voidptr;

            #  Projecting v_null to given pointer type:
            null:  t::Type( Ptr( O ) ) -> Ptr( O );

            # The "light" NULL pointer is simply a typeagnostic constant 
            null' : Ptr'( O );

            #  fptr version of NULL 
            fnull:  t::Type( Fptr( F ) ) -> Fptr( F );

            #  Again, "light" version is simply a typeagnostic constant 
            fnull' : Fptr'( F );

            #  Checking for NULL pointer 
            v_is_null:  Voidptr -> Bool;

            #  Combining inject and v_is_null for convenience:
            is_null:  Ptr( O ) -> Bool;

            #  Alt 
            is_null' : Ptr'( O ) -> Bool;

            #  Checking a function pointer for NULL 
            is_fnull:  Fptr( F ) -> Bool;

            #  Alt 
            is_fnull' : Fptr'( F ) -> Bool;

            #  pointer arithmetic 
            plus: (Ptr( Chunk( T, C ) ), Int) -> Ptr( Chunk( T, C ) );
            diff: (Ptr( Chunk( T, C ) ), Ptr( Chunk( T, C ) )) -> Int;

            #  Alt; needs explicit size (for element) 
            plus' : s::Size( T ) -> (Ptr'( Chunk( T, C ) ), Int) -> Ptr'( Chunk( T, C ) );
            diff' : s::Size( T ) -> (Ptr'( Chunk( T, C ) ), Ptr'( Chunk( T, C ) )) -> Int;

            # Subscript through a pointer.  This is unchecked:
            sub:  (Ptr( Chunk( T, C ) ), Int) -> Chunk( T, C );

            #  Alt; needs explicit size (for element) 
            sub' : s::Size( T ) -> (Ptr'( Chunk( T, C ) ), Int) -> Chunk'( T, C );

            #  Conversions that rely on witnesses 
            convert:   w::Witness (Ptr( Chunk( A_st, A_sc ) ), Ptr( Chunk( A_tt, A_tc ) ))
                       ->
                       Ptr( Chunk( A_st, A_sc ) )
                       ->
                       Ptr( Chunk( A_tt, A_tc ) );

            convert' : w::Witness (Ptr( Chunk( A_st, A_sc ) ), Ptr( Chunk( A_tt, A_tc ) ))
                       ->
                       Ptr'( Chunk( A_st, A_sc ) )
                       ->
                       Ptr'( Chunk( A_tt, A_tc ) );


            # Constness manipulation for pointers.
            # Note: fun ro x = convert (w::pointer (w::ro w::trivial)) x
            #       etc.

            ro:  Ptr ( Chunk( T, C    ) ) ->   Ptr ( Chunk( T, Ro   ) );
            rw:  Ptr ( Chunk( T, A_sc ) ) ->   Ptr ( Chunk( T, A_tc ) );

            ro': Ptr'( Chunk( T, C    ) ) ->   Ptr'( Chunk( T, Ro   ) );
            rw': Ptr'( Chunk( T, A_sc ) ) ->   Ptr'( Chunk( T, A_tc ) );
        };



    # Operations on (mostly) arrays 

    package arr:
        api {
            # Array subscript.
            # Since we have RTTI, we can actually make this safe:
            # We raise exceptions::INDEX_OUT_OF_BOUNDS for out-of-bounds access;
            # for unchecked access, go through arr_decay and ptr_sub
            sub:   (Chunk( Arr( T, N ), C), Int) -> Chunk( T, C );

            #  Alt; needs element size and rw_vector dimension 
            sub' : (s::Size( T ), dim::Dim( N )) ->
                        (Chunk'( Arr( T, N ), C), Int) -> Chunk'( T, C );

            # Let a rw_vector chunk decay, yielding pointer to first element:
            decay:   Chunk ( Arr( T, N ), C) ->   Ptr ( Chunk( T, C ) );
            decay':  Chunk'( Arr( T, N ), C) ->   Ptr'( Chunk( T, C ) );

            # Reconstruct an rw_vector chunk from the pointer to its first element 
            reconstruct : (Ptr ( Chunk( T, C ) ), dim::Dim( N )) ->   Chunk ( Arr( T, N ), C);
            reconstruct': (Ptr'( Chunk( T, C ) ), dim::Dim( N )) ->   Chunk'( Arr( T, N ), C);

            #  Dimension of rw_vector chunk 
            dim:   Chunk( Arr( T, N ), C) -> dim::Dim( N );
        };

    #  Allocating new chunks 
    new:  t::Type( T ) -> Chunk( T, C );

    #  Alt 
    new' : s::Size( T ) -> Chunk'( T, C );

    #  freeing chunks that were allocated earlier 
    discard:  Chunk( T, C ) -> Void;

    #  Alt 
    discard' : Chunk'( T, C ) -> Void;

    #  Allocating a dynamically-sized rw_vector 
    allot:  t::Type( T ) -> Unt -> Ptr( Chunk( T, C ) );

    #  Alt 
    allot' : s::Size( T ) -> Unt -> Ptr'( Chunk( T, C ) );

    # Freeing through pointers:
    free:  Ptr( O ) -> Void;

    # Alt 
    free' : Ptr'( O ) -> Void;

    # Perform a function call through a function-pointer:
    call:  (Fptr( X -> Y ), X) -> Y;

    # Alt:  Needs explicit type for the function pointer: 
    call' : t::Type( Fptr( X -> Y ) ) -> (Fptr'( X -> Y ), X) -> Y;


    # Completely unsafe stuff, but common in C code:
    package u:
        api {
            fcast:  Fptr'(X) -> Fptr'(Y);
            p2i:    Ptr' ( O ) -> Ulong;
            i2p:    Ulong      -> Ptr'( O );
        };
};


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext