PreviousUpNext

15.4.679  src/lib/compiler/front/typer/types/unify-types.pkg

## unify-types.pkg 

# Compiled by:
#     src/lib/compiler/front/typer/typer.sublib

# The center of the typechecker is
#
#     src/lib/compiler/front/typer/main/type-package-language-g.pkg
#
# -- see it for a higher-level overview.
#  
# We get called from 
#
#     src/lib/compiler/front/typer/modules/api-match-g.pkg
#     src/lib/compiler/front/typer/types/unify-and-generalize-types-g.pkg
#
# The Hindley-Milner type inference algorithm on which the
# typechecker is based uses Prolog-style logical unification
# to propagate type information to syntax nodes lacking
# explicit programmer-supplied type declarations.  (Which is,
# typically, the overwhelming majority of them.)
#  
# A light overview of Hindley-Milner type inference may be found here:
#     http://en.wikipedia.org/wiki/Type_inference
#
# A more detailed treatment may be found in the
#   Types and Programming Languages
# text by Benjamin C Pierce, chapter 22.
#  
# In this file we implement the required unification operation.

# For unification, our primary analog of a logic variable is
# a type variable set to META_TYPE_VARIABLE;
# this represents a totally unconstrained type about which
# we as yet know nothing at all.
#
# Various other type variable values also admit specialization
# during unification to reflect additional knowledge gained.
# For example INCOMPLETE_RECORD_TYPE_VARIABLE; values represent
# incompletely specified records ("..." used), which can be
# updated to reflect the complete record definition if we find it.
#
# Unification thus mostly consists of propagating type knowledge
# by setting such type variables to something more specific, perhaps
# a compound type containing more META_TYPE_VARIABLE
# type variables to be set in their turn.
#
# The entrypoint into this file is unify_types().  It has a
# Void result type since all its work is done via side-effects,
# setting type variables embedded in its type arguments to
# less general (and thus more informative) values.

api Unify_Types {

    # If unification fails we raise
    # the exception UNIFY_TYPES with
    # a Unify_Fail value to detail
    # the exact reason for failure:
    #
    Unify_Fail
        = CIRCULARITY                                                                           # Cycle in type graph -- type variable loop.
        | NEED_EQUALITY_TYPE                                                                    # Equality type required. 
        | TYP_MISMATCH    (types::Typ, types::Typ)      # Type constructor mismatch.
        | TYPE_MISMATCH                (types::Type, types::Type)                               # Type mismatch.
        | LITERAL_TYPE_MISMATCH         types::Type_Variable                                    # Type of literal could not be resolved.
        | USER_TYPE_VARIABLE_MISMATCH   types::Type_Variable                                    # USER_TYPE_VARIABLE match 
        | OVERLOADED_TYPE_VARIABLE_MISMATCH                                                     # OVERLOADED_TYPE_VARIABLE, equality mismatch  
        | RECORD_FIELD_LABELS_MISMATCH                                                          # Record labels did not match.
        ;

    exception UNIFY_TYPES  Unify_Fail;

    fail_message: Unify_Fail -> String;

    unify_types:  (String, String, types::Type, types::Type, List(String)) -> Void;

    debugging:    Ref(  Bool );
};


stipulate
    package ty  =  types;                               # types                         is from   src/lib/compiler/front/typer-stuff/types/types.pkg
    package ts  =  type_junk;                           # type_junk                     is from   src/lib/compiler/front/typer-stuff/types/type-junk.pkg
                                                        # resolve_overloaded_literals   is from   src/lib/compiler/front/typer/types/resolve-overloaded-literals.pkg
                                                        # typer_debugging               is from   src/lib/compiler/front/typer/main/typer-debugging.pkg
                                                        # unparse_type                  is from   src/lib/compiler/front/typer/print/unparse-type.pkg
herein

    package   unify_types
    : (weak)  Unify_Types                               # Unify_Types                   is from   src/lib/compiler/front/typer/types/unify-types.pkg
    {
        # Type unification.

        debugging =   typer_control::unify_types_debugging;             #  REF FALSE 

        stipulate
#           include types; 

            #  Debugging 
            say = control_print::say;

            fun if_debugging_say (msg: String)
                =
                if *debugging
                    say msg;
                    say "\n";
                fi;

            fun bug msg
                =
                error_message::impossible("unify_types: " + msg);


            unparse_type
                =
                unparse_type::unparse_type  symbolmapstack::empty;

            fun debug_unparse_type (msg, type)
                =
                typer_debugging::debug_print  debugging  (msg, unparse_type, type);

            fun debug_unparse_typevar_ref  typevar_ref
                =
                if *debugging           # Without this 'if' (and the matching one in unify_and_generalize_types_g), compiling the compiler takes 5X as long! :-)
                    typer_debugging::with_internals
                        (fn () =  if_debugging_say (unparse_type::typevar_ref_printname typevar_ref));
                fi;


            prettyprint_type
                =
                prettyprint_type::prettyprint_type  symbolmapstack::empty;

            fun debug_pptype (msg, type)
                =
                typer_debugging::debug_print  debugging  (msg, prettyprint_type, type);

        herein

             Unify_Fail
                 = CIRCULARITY                                                                  # Cycle in type graph -- type variable loop.
                 | NEED_EQUALITY_TYPE                                                           # Equality type required.
                 | TYP_MISMATCH    (types::Typ, types::Typ)     # Type constructor mismatch.
                 | TYPE_MISMATCH                (types::Type, types::Type)                              # Type mismatch.
                 | LITERAL_TYPE_MISMATCH         types::Type_Variable                           # Type of literal could not be resolved.
                 | USER_TYPE_VARIABLE_MISMATCH   types::Type_Variable                           # USER_TYPE_VARIABLE match.
                 | OVERLOADED_TYPE_VARIABLE_MISMATCH                                            # OVERLOADED_TYPE_VARIABLE, equality mismatch.
                 | RECORD_FIELD_LABELS_MISMATCH                                                 # Record labels did not match.
                 ;

            fun fail_message failure
                =
                case failure
                     CIRCULARITY                   => "circularity";
                     NEED_EQUALITY_TYPE                => "equality type required";

                     TYP_MISMATCH       _ => "typ mismatch";
                     TYPE_MISMATCH                   _ => "type mismatch";

                     LITERAL_TYPE_MISMATCH           _ => "literal";
                     USER_TYPE_VARIABLE_MISMATCH     _ => "USER_TYPE_VARIABLE match";

                     OVERLOADED_TYPE_VARIABLE_MISMATCH => "OVERLOADED_TYPE_VARIABLE, equality mismatch";
                     RECORD_FIELD_LABELS_MISMATCH      => "record labels";
                esac;


            exception UNIFY_TYPES  Unify_Fail;


            ########################################################
            # Miscellaneous functions:

            eq_label = symbol::eq;

            fun literal_is_equality_kind (lk:  ty::Literal_Kind)
                =
                case lk
                     (ty::INT | ty::UNT | ty::CHAR | ty::STRING)   =>   TRUE;
                     ty::FLOAT                                     =>   FALSE;
                esac;

            # equality_property_of_typ typ:
            #
            # This function returns the equality_property
            # of typ for use in determining
            # when a TYPCON_TYPE is an equality type.
            #
            # Note: Calling this function on ERRONEOUS_TYP produces an impossible
            # because an ERRONEOUS_TYP should never occur in a TYPCON_TYPE
            # and hence an equality_property of one of them should never be needed.
            #
            # Calling this function on a DEFINED_TYP also produces an impossible because
            # the current equality_property scheme is insufficiently expressive to describe
            # the possibilities.  (Ex: first argument must be an eq type but not
            # necessarily the second)  Because of this, it is currently necessary to
            # expand a DEFINED_TYP before computing its equality type.
            #
            fun equality_property_of_typ (ty::PLAIN_TYP { eqtype_info, ... } )
                    =>
                    case *eqtype_info
                        #       
                        ty::eq_type::EQ_ABSTRACT =>  ty::eq_type::NO;
                        ep             =>  ep;
                    esac;

                equality_property_of_typ (ty::RECORD_TYP _)  =>  ty::eq_type::YES;
                equality_property_of_typ (ty::DEFINED_TYP _) =>  bug "equality_property_of_typ: DEFINED_TYP";
                equality_property_of_typ (ty::ERRONEOUS_TYP) =>  bug "equality_property_of_typ: ERRONEOUS_TYP";
                equality_property_of_typ _                   =>  bug "equality_property_of_typ: unexpected typ";
            end;

            # fieldwise (just1, just2, combine, fields1, fields2):
            #
            # This function merges two sorted lists of (label, type) pairs
            # (sorted by label) into a single sorted list of (label, type) pairs.
            # If (l1, t1) occurs in fields1 but l1 doesn't occur in fields2 then
            # (l1, just1 t1) occurs in the output.  Similarly with just2.
            # If (l, t1) occurs in fields1 and (l, t2) in fields2, then 
            # (l, combine t1 t2) occurs in the output.
            #
            fun fieldwise (_, just2, _, [], fields2) =>   map  (fn (n, t) = (n, just2 t))  fields2;
                fieldwise (just1, _, _, fields1, []) =>   map  (fn (n, t) = (n, just1 t))  fields1;

                fieldwise (just1, just2, combine, ((n1, t1) ! r1), ((n2, t2) ! r2))
                    =>
                    if   (eq_label (n1, n2))

                         (n1, combine (t1, t2)) ! (fieldwise (just1, just2, combine, r1, r2));
                    else
                         if   (ts::label_is_greater_than (n2, n1))

                              (n1, just1 t1) ! (fieldwise (just1, just2, combine, r1, ((n2, t2) ! r2)));
                         else
                              (n2, just2 t2) ! (fieldwise (just1, just2, combine, ((n1, t1) ! r1), r2));
                         fi;
                    fi;
            end;


            # ************** adjust function ****************************************

            # We are about to do
            #
            #     given_type_var := ty::RESOLVED_TYPE_VARIABLE given_type;
            #
            # or something very similar, and before doing
            # so we want to propagate any relevant type
            # information from the current value of *given_type_var
            # into 'given_type'.
            #
            # We also want to incorporate into 'given_type'
            # our given_fn_nesting and 'given_eq' values,
            # by setting
            #     given_type_var.fn_nesting  to minimum of current and given value;
            #     given_type_var.eq          to 'or' of current value and 'given_eq'.
            #
            # Raise CIRCULARITY if there is a type variable loop.
            #
            fun adjust_type
                ( given_type:          types::Type,
                  given_typevar_ref:   types::Typevar_Ref,
                  given_fn_nesting:    Int,                     # Count of enclosing fun/fn lexical scopes.
                  given_eq:            Bool                     # TRUE if type variable must resolve to an equality type.

                )
                : Void
                =
                adjust_type'  given_eq  given_type
                where
                    if_debugging_say "\n\nadjust_type: variable ";
                    debug_unparse_typevar_ref  given_typevar_ref;
                    debug_unparse_type (" ==> ty::RESOLVED_TYPE_VARIABLE ", given_type);

                    fun adjust_type' this_eq (ty::TYPE_VARIABLE_REF (tv as { id, ref_typevar as REF type_variable } ))
                            =>
                            case type_variable
                                #
                                ty::USER_TYPE_VARIABLE { fn_nesting, eq, name }
                                    =>
                                    # Check if eq is compatible and propagate fn_nesting:
                                    # 
                                    if   (this_eq and not eq)               raise exception UNIFY_TYPES  NEED_EQUALITY_TYPE;
                                    elif (given_fn_nesting < fn_nesting)    ref_typevar := ty::USER_TYPE_VARIABLE { fn_nesting => given_fn_nesting, eq, name };
                                    fi;


                                ty::META_TYPE_VARIABLE { eq, fn_nesting }
                                    =>
                                    # Check for circularity,
                                    # propagate eq and fn_nesting
                                    #
                                    if (ts::typevar_refs_are_equal (given_typevar_ref, tv))

                                         raise exception UNIFY_TYPES CIRCULARITY;
                                    else

                                         ref_typevar := ty::META_TYPE_VARIABLE
                                                   {
                                                     fn_nesting =>  int::min (given_fn_nesting, fn_nesting),
                                                     eq         =>  this_eq or eq
                                                   };
                                    fi;


                                ty::INCOMPLETE_RECORD_TYPE_VARIABLE { known_fields, eq, fn_nesting }
                                    =>
                                    # Check for circularity,
                                    # propagate eq and fn_nesting
                                    #
                                    if (ts::typevar_refs_are_equal (given_typevar_ref, tv))

                                         raise exception UNIFY_TYPES CIRCULARITY;
                                    else
                                         # Do field types recursively:
                                         #      
                                         apply  (fn (l, t) =  adjust_type (t, given_typevar_ref, given_fn_nesting, eq))
                                                known_fields;

                                         ref_typevar := ty::INCOMPLETE_RECORD_TYPE_VARIABLE
                                                   {
                                                     fn_nesting =>  int::min (given_fn_nesting, fn_nesting),
                                                     eq         =>  this_eq or eq,
                                                     known_fields
                                                   };
                                    fi;


                                ty::RESOLVED_TYPE_VARIABLE  type
                                    =>
                                    adjust_type'  this_eq  type;


                                ty::LITERAL_TYPE_VARIABLE { kind, ... }
                                    =>
                                    #  Check if eq is compatible 
                                    #
                                    if (this_eq and not (literal_is_equality_kind  kind))
                                         raise exception UNIFY_TYPES  NEED_EQUALITY_TYPE;
                                    fi;


                                ty::OVERLOADED_TYPE_VARIABLE eq'
                                    =>
                                    if   (ts::typevar_refs_are_equal (given_typevar_ref, tv))   raise exception UNIFY_TYPES CIRCULARITY;
                                    elif (this_eq and not eq')                              ref_typevar := ty::OVERLOADED_TYPE_VARIABLE this_eq;
                                    fi;


                                ty::TYPE_VARIABLE_MARK _
                                    =>
                                    bug "unify: adjust_type: ty::TYPE_VARIABLE_MARK";
                            esac;


                        adjust_type'  this_eq  (type as ty::TYPCON_TYPE (ty::DEFINED_TYP _, args))
                            =>
                            adjust_type'  this_eq  (ts::head_reduce_type  type);


                        adjust_type'  this_eq  (ty::TYPCON_TYPE (typ, args))
                            =>
                            case (equality_property_of_typ  typ)
                                #
                                ty::eq_type::CHUNK =>  apply (adjust_type' FALSE  ) args;
                                ty::eq_type::YES   =>  apply (adjust_type' this_eq) args;

                                _        =>  if this_eq   raise exception UNIFY_TYPES  NEED_EQUALITY_TYPE;
                                             else         apply (adjust_type' FALSE) args;
                                             fi;
                            esac;


                        adjust_type' _ ty::WILDCARD_TYPE
                            =>
                            ();


                        # BUG? why don't these cases blow up
                        # (in equality_property_of_typ)
                        # when adjust_type' is applied to arguments that
                        # are unreduced applications of DEFINED_TYP?
                        # XXX BUGGO FIXME
                        # (Is this answered by the previous comment that
                        # a DEFINED_TYP must always be expanded
                        # before calling equality_property_of_typ?)

                        adjust_type' _ (ty::TYPE_SCHEME_TYPE _)      =>  bug "adjust_type 1";
                        adjust_type' _ (ty::TYPE_SCHEME_ARG_I     _) =>  bug "adjust_type 2";
                        adjust_type' _ _                             =>  bug "adjust_type 3";
                    end;
                end;


            # Reorder two type_variables in descending order according to the ordering
            # ty::LITERAL_TYPE_VARIABLE > ty::USER_TYPE_VARIABLE > ty::OVERLOADED_TYPE_VARIABLE > ty::INCOMPLETE_RECORD_TYPE_VARIABLE > ty::META_TYPE_VARIABLE
            #
            fun sort_vars (  typevar_ref1 as { id => id1, ref_typevar => REF typevar1 },
                             typevar_ref2 as { id => id2, ref_typevar => REF typevar2 }
                          )
                =
                case (typevar1, typevar2)
                     (ty::LITERAL_TYPE_VARIABLE _, _)    => (typevar_ref1, typevar_ref2);
                     (_, ty::LITERAL_TYPE_VARIABLE _)    => (typevar_ref2, typevar_ref1);

                     (ty::USER_TYPE_VARIABLE _, _)       => (typevar_ref1, typevar_ref2);
                     (_, ty::USER_TYPE_VARIABLE _)       => (typevar_ref2, typevar_ref1);

                     (ty::OVERLOADED_TYPE_VARIABLE _, _) => (typevar_ref1, typevar_ref2);
                     (_, ty::OVERLOADED_TYPE_VARIABLE _) => (typevar_ref2, typevar_ref1);

                     (ty::INCOMPLETE_RECORD_TYPE_VARIABLE _, _) => (typevar_ref1, typevar_ref2);
                     (_, ty::INCOMPLETE_RECORD_TYPE_VARIABLE _) => (typevar_ref2, typevar_ref1);

                     _ => (typevar_ref1, typevar_ref2);  #  Both ty::META_TYPE_VARIABLE
                esac;


            # Here is the externally visible entrypoint.
            # It is just a wrapper for unify_types':
            #
            fun unify_types
                ( name1, name2,
                  type1, type2,
                  stack
                )
                =
                {   type1 = ts::prune type1;    # Reduce ty::TYPE_VARIABLE_REF (REF (ty::RESOLVED_TYPE_VARIABLE type))  to just  type.
                    type2 = ts::prune type2;    # "                                                                              ".

                    if (not *debugging)

                        unify_types' (stack, type1, type2);

                    else
                        verbose =   case (type1, type2)
                                        #
                                        (ty::TYPE_VARIABLE_REF _, ty::TYPE_VARIABLE_REF _) => FALSE;
                                        _                                                  => TRUE;
                                    esac;

                        if verbose
                            if_debugging_say "\n\n============= unify_types/TOP ===============";
                            if_debugging_say     "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n";
                            if_debugging_say ("\nCalled by:  " + (string::join " " (reverse stack)) + "\n");

                            debug_unparse_type("    " + name1 + ": ", type1);
                            debug_unparse_type(         name2 + ": ", type2);

    #                           if_debugging_say "\n\n----------prettyprinting unify_types args------------\n";
    #                           debug_pptype(">>unify_types: type1: ", type1);
    #                           debug_pptype(">>unify_types: type2: ", type2);
                        fi;


                        unify_types' (stack, type1, type2);


                        if verbose
                            if_debugging_say "\nRESULTS:\n";
                            debug_unparse_type("    " + name1 + ": ", type1);
                            debug_unparse_type(         name2 + ": ", type2);

    #                   if_debugging_say "\n\n----------prettyprinting unify_types results------------\n";
    #                   debug_pptype(">>unify_types: type1: ", type1);
    #                   debug_pptype(">>unify_types: type2: ", type2);

                            if_debugging_say "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^";
                            if_debugging_say   "============= unify_types/BOTTOM ===============\n";
                        fi;
                    fi;
                }


            # The real work is unifying type variables.
            # Here we handle interactions at the next
            # level up, the various settings in which
            # type variables are embedded:
            #
            also
            fun unify_types' (stack, type1, type2)
                =
                {   case ( ts::head_reduce_type type1,                  # Mainly evaluates DEFINED_TYP type functions.
                           ts::head_reduce_type type2                   # "                                                      ".
                         )

                         ( ty::TYPE_VARIABLE_REF var1,
                           ty::TYPE_VARIABLE_REF var2
                         )
                             =>
                             unify_type_variables (var1, var2);

                         (ty::TYPE_VARIABLE_REF (var1 as { id, ref_typevar }),  etype2)
                             =>
                             resolve_type_variable (var1, type2, etype2);       # E.g. if var1 is ty::META_TYPE_VARIABLE it becomes ty::RESOLVED_TYPE_VARIABLE(type).

                         (etype1,  ty::TYPE_VARIABLE_REF (var2 as { id, ref_typevar }))
                             =>
                             resolve_type_variable (var2, type1, etype1);       # "                                                                                    ".

                         ( ty::TYPCON_TYPE (typ1, args1),
                           ty::TYPCON_TYPE (typ2, args2)
                         )
                             =>
                             if (ts::typs_are_equal (typ1, typ2) )
    if_debugging_say "--------- unify_types'/CONSTRUCTOR recursive calls TOP\n";
                                 paired_lists::apply unify (args1, args2)
                                 where
                                     fun unify (type1, type2) = unify_types ("1", "2", type1, type2, stack);
                                 end;
    if_debugging_say "--------- unify_types'/CONSTRUCTOR recursive calls BOTTOM\n";
                             else
                                 raise exception UNIFY_TYPES (TYP_MISMATCH (typ1, typ2));
                             fi;

                         # If one of the types is ty::WILDCARD_TYPE, propagate it down into the
                         # other type to eliminate type_variables that might otherwise cause
                         # generalize_type to complain.
                         #      
                         (ty::WILDCARD_TYPE, ty::TYPCON_TYPE(_, args2))
                             => 
                             {   
    if_debugging_say "--------- unify_types'/WILD+CONSTRUCTOR recursive calls TOP\n";
                                 apply  (fn x =  unify_types ("1", "2", x, ty::WILDCARD_TYPE, stack))
                                        args2;
    if_debugging_say "--------- unify_types'/WILD+CONSTRUCTOR recursive calls BOTTOM\n";
                             };

                         (ty::TYPCON_TYPE(_, args1), ty::WILDCARD_TYPE)
                             =>
                             {   
    if_debugging_say "--------- unify_types'/CONSTRUCTOR+WILD recursive calls TOP\n";
                                 apply  (fn x =  unify_types ("1", "2", x,  ty::WILDCARD_TYPE, stack))
                                        args1;
    if_debugging_say "--------- unify_types'/CONSTRUCTOR+WILD recursive calls BOTTOM\n";
                             };

                         (ty::WILDCARD_TYPE, _) => ();
                         (_, ty::WILDCARD_TYPE) => ();

                         other =>  raise exception UNIFY_TYPES (TYPE_MISMATCH other);
                    esac;
                }

            also
            fun unify_type_variables (var1, var2)
                =
                {   if_debugging_say ">>unify_type_variables";

                    if  (not (ts::typevar_refs_are_equal (var1, var2)))
                        unify_type_variables'  (sort_vars  (var1, var2));
                    fi;
                }
                where

                    # Here is the beating heart of the unification logic.
                    # The essential tranforms are:
                    #
                    #     ty::LITERAL_TYPE_VARIABLE    can resolve to a compatible
                    #                ty::LITERAL_TYPE_VARIABLE or a monotype of its ty::LITERAL_TYPE_VARIABLE ilk.
                    #
                    #     ty::USER_TYPE_VARIABLE cannot be changed,
                    #                but its fn_nesting can be reduced.
                    #
                    #     FLEX       can merge with another FLEX or resolve to a META.
                    #
                    #     META       can resolve to anything.
                    #
                    # Note that our (typevar_ref1, typevar_ref2) arguments are run
                    # through sort_vars() before we are called.  This reduces the
                    # number of cases we must consider.
                    # For example, we can have (USER, META)
                    # but we can never have    (META, USER).
                    #
                    fun unify_type_variables'
                        (
                          typevar_ref1 as { id => id1, ref_typevar => ref_tv1 as REF type1 },
                          typevar_ref2 as { id => id2, ref_typevar => ref_tv2 as REF type2 }
                        )
                        =
                        #  ASSERT: ref_tv1 != ref_tv2 
                        case type1
                            #
                            ty::META_TYPE_VARIABLE
                                {
                                  fn_nesting => fn_nesting1,
                                  eq => eq1
                                }
                                =>
                                case type2
                                    #
                                    ty::META_TYPE_VARIABLE { fn_nesting=>fn_nesting2, eq=>eq2 }
                                        =>
                                        {   fn_nesting = int::min (fn_nesting1, fn_nesting2);
                                            eq         = eq1 or eq2;
                                            #
                                            ref_tv1 := ty::META_TYPE_VARIABLE { fn_nesting, eq };
                                            ref_tv2 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref1 );
                                        };

                                    _ => bug "unify_Type_variables 3";
                                esac;

                            ty::USER_TYPE_VARIABLE { fn_nesting=>fn_nesting1, eq=>eq1, name }
                                =>
                                case type2
                                    #
                                    ty::META_TYPE_VARIABLE { eq=>eq2, fn_nesting=>fn_nesting2 }
                                        =>
                                        if (eq1 or (not eq2))
                                             if (fn_nesting2 < fn_nesting1)
                                                 ref_tv1 := ty::USER_TYPE_VARIABLE { fn_nesting=>fn_nesting2, eq=>eq1, name };
                                             fi;
                                             ref_tv2 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref1);
                                        else
                                             raise exception UNIFY_TYPES (USER_TYPE_VARIABLE_MISMATCH type1);
                                        fi;

                                    _ => {
                                             raise exception UNIFY_TYPES (USER_TYPE_VARIABLE_MISMATCH type1);
                                         };
                                esac;

                            ty::INCOMPLETE_RECORD_TYPE_VARIABLE {   known_fields => known_fields1,   fn_nesting => fn_nesting1,   eq => eq1   }
                                =>
                                case type2
                                    #
                                    ty::META_TYPE_VARIABLE { eq=>eq2, fn_nesting=>fn_nesting2 }
                                        =>
                                        {   fn_nesting = int::min (fn_nesting1, fn_nesting2);
                                            eq         = eq1 or eq2;

                                            apply (fn (l, t) = adjust_type (t, typevar_ref2, fn_nesting, eq))
                                                  known_fields1;

                                            ref_tv1 := ty::INCOMPLETE_RECORD_TYPE_VARIABLE { known_fields=>known_fields1, fn_nesting, eq };
                                            ref_tv2 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref1);


                                        };

                                    ty::INCOMPLETE_RECORD_TYPE_VARIABLE { known_fields=>known_fields2, eq=>eq2, fn_nesting=>fn_nesting2 }
                                        =>
                                        {   fn_nesting = int::min (fn_nesting1, fn_nesting2);
                                            eq         = eq1 or eq2;

                                            apply   (fn (l, t) =  adjust_type (t, typevar_ref1, fn_nesting, eq))   known_fields2;
                                            apply   (fn (l, t) =  adjust_type (t, typevar_ref2, fn_nesting, eq))   known_fields1;

                                            ref_tv1 := ty::INCOMPLETE_RECORD_TYPE_VARIABLE
                                                      { fn_nesting,
                                                        eq,
                                                        known_fields => (merge_fields (TRUE, TRUE, known_fields1, known_fields2))
                                                      };

                                            ref_tv2 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref1);
                                        };
                                   _ => bug "unify_type_variables 2";
                                esac;

                            ty::LITERAL_TYPE_VARIABLE { kind, source_code_region }
                                =>
                                case type2
                                    #
                                    ty::LITERAL_TYPE_VARIABLE { kind=>kind', ... }
                                        =>
                                        if (kind == kind')

                                             ref_tv2 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref1);
                                        else
                                             raise exception UNIFY_TYPES (LITERAL_TYPE_MISMATCH type1);
                                        fi;

                                   (ty::META_TYPE_VARIABLE { eq=>e2, ... } | ty::OVERLOADED_TYPE_VARIABLE e2)
                                        =>
                                        #  Check eq compatibility 
                                        if (not e2 or literal_is_equality_kind kind)

                                             ref_tv2 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref1);
                                        else
                                             raise exception UNIFY_TYPES (LITERAL_TYPE_MISMATCH type1);
                                        fi;

                                   _ => raise exception UNIFY_TYPES (LITERAL_TYPE_MISMATCH type1);
                               esac;

                            ty::OVERLOADED_TYPE_VARIABLE eq1
                                =>
                                case type2
                                    #
                                    ty::OVERLOADED_TYPE_VARIABLE eq2
                                        =>
                                        if (eq1 or not eq2)    ref_tv2 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref1);
                                        else                   ref_tv1 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref2);
                                        fi;

                                    ty::META_TYPE_VARIABLE { eq=>eq2, fn_nesting=>fn_nesting2 }
                                        =>
                                        if (eq1 or (not eq2))
                                              ref_tv2 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref1);
                                        else  ref_tv1 := ty::OVERLOADED_TYPE_VARIABLE eq2;
                                              ref_tv2 := ty::RESOLVED_TYPE_VARIABLE (ty::TYPE_VARIABLE_REF typevar_ref1);
                                        fi;

                                    _ => raise exception UNIFY_TYPES  OVERLOADED_TYPE_VARIABLE_MISMATCH;
                                esac;


                            _ =>  bug "unify_type_variables 4";
                        esac;                                           # fun unify_type_variables
                end                                                     # where

            also
            fun resolve_type_variable
                    ( var as { id, ref_typevar as REF (ty::META_TYPE_VARIABLE { fn_nesting, eq } ) },
                      type,
                      ety
                    )
                    =>
                    {   case ety
                             ty::WILDCARD_TYPE => ();
                             _             => adjust_type (ety, var, fn_nesting, eq);
                        esac;

                        ref_typevar := ty::RESOLVED_TYPE_VARIABLE type;
                    };

                resolve_type_variable (var as { id, ref_typevar as REF (ty::INCOMPLETE_RECORD_TYPE_VARIABLE { known_fields, fn_nesting, eq } ) }, type, ety)
                    =>
                    case ety
                        #
                        ty::TYPCON_TYPE (ty::RECORD_TYP field_names, field_types)
                            =>
                            {   record_fields = paired_lists::zip (field_names, field_types);
                                #
                                apply  (fn t = adjust_type (t, var, fn_nesting, eq))
                                       field_types;

                                merge_fields (FALSE, TRUE, known_fields, record_fields);

                                ref_typevar := ty::RESOLVED_TYPE_VARIABLE type;
                            };

                        ty::WILDCARD_TYPE       #  propagate ty::WILDCARD_TYPE to the fields 
                            =>
                            apply  (fn (lab, type) =  unify_types ("1", "2", ty::WILDCARD_TYPE, type, ["resolve_type_variable"]))
                                   known_fields;

                        _ => raise exception UNIFY_TYPES (TYPE_MISMATCH (ty::TYPE_VARIABLE_REF (var), ety));
                    esac;


                resolve_type_variable (var as { id, ref_typevar as REF (i as ty::OVERLOADED_TYPE_VARIABLE eq) }, type, ety)
                    =>
                    {  adjust_type (ety, var, ty::infinity, eq);
                       ref_typevar := ty::RESOLVED_TYPE_VARIABLE type;
                    };

                resolve_type_variable (var as { id, ref_typevar as REF (i as ty::LITERAL_TYPE_VARIABLE { kind, ... } ) }, type, ety)
                    =>
                    case ety
                         ty::WILDCARD_TYPE => ();
                        _ => if   (resolve_overloaded_literals::is_literal_type (kind, ety))

                                  ref_typevar := ty::RESOLVED_TYPE_VARIABLE type;
                             else
                                  raise exception UNIFY_TYPES (LITERAL_TYPE_MISMATCH i);                # Should return the type for error msg. XXX BUGGO FIXME
                             fi;
                    esac;   

                resolve_type_variable ({ id, ref_typevar as REF (i as ty::USER_TYPE_VARIABLE _) }, _, ety)
                    =>
                    case ety
                         ty::WILDCARD_TYPE => ();
                         _ => {
                                  raise exception UNIFY_TYPES (USER_TYPE_VARIABLE_MISMATCH i);  # Should return the type for error msg. XXX BUGGO FIXME.
                              };
                    esac;

                resolve_type_variable ({ id, ref_typevar as REF (ty::RESOLVED_TYPE_VARIABLE _) }, _, _) =>  bug "resolve_type_variable: ty::RESOLVED_TYPE_VARIABLE";
                resolve_type_variable ({ id, ref_typevar as REF (ty::TYPE_VARIABLE_MARK     _) }, _, _) =>  bug "resolve_type_variable: ty::TYPE_VARIABLE_MARK";
            end 

            # merge_fields (extra1, extra2, fields1, fields2):
            #
            #    This function merges the 2 sorted field lists.  Fields occurring
            # in both lists have their types unified.  If a field occurs in only
            # one list, say fields { i } then if extra { i } is TRUE, a unify_types
            # error is raised.

            also
            fun merge_fields (extra1, extra2, fields1, fields2)
                =
                {   fun extra allowed t
                        =
                        if   (not allowed)
                             raise exception UNIFY_TYPES  RECORD_FIELD_LABELS_MISMATCH;
                        else t;
                        fi;

                    fieldwise (   extra extra1,
                                  extra extra2, 
                                  (fn (t1, t2) =  { unify_types ("1", "2", t1, t2, ["unify_types::merge_fields"]); t1;}),
                                  fields1,
                                  fields2
                              );
                };

        end;    #  local 
    };          #  package unify_types 
end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext