def promote_types(dtype1, dtype2):
    """
    Get the smallest type to which the given scalar types can be cast.

    Args:
        dtype1 (builtin):
        dtype2 (builtin):

    Returns:
        A builtin datatype or None.

    Examples:
        >>> promote_types(int32, int64)
            builtin('int64')

        >>> promote_types(fp16, fp32)
            builtin('fp32')

        >>> promote_types(fp16, int32)
            builtin('fp16')
    """
    nptype1 = nptype_from_builtin(dtype1)
    nptype2 = nptype_from_builtin(dtype2)
    # Circumvent the undesirable np type promotion:
    # >> np.promote_types(np.float32, np.int32)
    # dtype('float64')
    if np.issubdtype(nptype1, np.floating) and np.issubdtype(nptype2, np.signedinteger):
        nppromoted = nptype1
    elif np.issubdtype(nptype2, np.floating) and np.issubdtype(
        nptype1, np.signedinteger
    ):
        nppromoted = nptype2
    else:
        nppromoted = np.promote_types(nptype1, nptype2)
    return numpy_type_to_builtin_type(nppromoted)

    Get the smallest type to which the given scalar types can be cast.

    Args:
        dtype1 (builtin):
        dtype2 (builtin):

    Returns:
        A builtin datatype or None.

    Examples:
        >>> promote_types(int32, int64)
            builtin('int64')

        >>> promote_types(fp16, fp32)
            builtin('fp32')

        >>> promote_types(fp16, int32)
            builtin('fp16')
    
coremltools/converters/mil/mil/types/type_mapping.py