def promote_dtypes(dtypes):
    """
    Get the smallest promoted dtype, to which all scalar dtypes (provided through dtypes list argument) can be casted.
    Args:
        List [dtype (builtin)]
    Returns:
        A builtin datatype or None.

    Examples:
        >>> promote_dtypes([int32, int64, int16])
            builtin('int64')

        >>> promote_dtypes([fp16, fp32, fp64])
            builtin('fp64')

        >>> promote_dtypes([fp16, int32, int64])
            builtin('fp16')

    """
    if not isinstance(dtypes, (list, tuple)) or len(dtypes) < 1:
        raise ValueError("dtypes needs to be a list/tuple of at least 1 element")

    # Deduplicate inputs to avoid redundant calculations.
    # Without dedup, too large input will cause maximum recursion depth exceeded error.
    dtypes = list(set(dtypes))

    if len(dtypes) == 1:
        return dtypes[0]

    return promote_types(dtypes[0], promote_dtypes(dtypes[1:]))

    Get the smallest promoted dtype, to which all scalar dtypes (provided through dtypes list argument) can be casted.
    Args:
        List [dtype (builtin)]
    Returns:
        A builtin datatype or None.

    Examples:
        >>> promote_dtypes([int32, int64, int16])
            builtin('int64')

        >>> promote_dtypes([fp16, fp32, fp64])
            builtin('fp64')

        >>> promote_dtypes([fp16, int32, int64])
            builtin('fp16')

    
coremltools/converters/mil/mil/types/type_mapping.py