|
| 1 | +# This file is a part of Julia. License is MIT: https://julialang.org/license |
| 2 | + |
| 3 | +module TupleFromIterator |
| 4 | + |
| 5 | +import ..TypeIntersectExact as TIE |
| 6 | + |
| 7 | +incremented(n::Int) = Core.Intrinsics.add_int(n, 1) |
| 8 | +decremented(n::Int) = Core.Intrinsics.sub_int(n, 1) |
| 9 | + |
| 10 | +function _totuple_err(@nospecialize T) |
| 11 | + @noinline |
| 12 | + throw(ArgumentError("too few or too many elements for tuple type $T")) |
| 13 | +end |
| 14 | + |
| 15 | +function iterator_to_ntuple_recur(::Type{Tuple{}}, iter, state::Union{Tuple{},Tuple{Any}}) |
| 16 | + i = iterate(iter, state...)::Union{Nothing,Tuple{Any,Any}} |
| 17 | + isnothing(i) || |
| 18 | + throw(ArgumentError("iterator has too many elements for the tuple type")) |
| 19 | + () |
| 20 | +end |
| 21 | +function iterator_to_ntuple_recur( |
| 22 | + ::Type{Tuple{E,Vararg{E,lenm1}}}, iter, state::Union{Tuple{},Tuple{Any}} |
| 23 | +) where {E,lenm1} |
| 24 | + T2 = Tuple{E,Any} |
| 25 | + ItUn = Union{Nothing,T2} |
| 26 | + i = iterate(iter, state...)::ItUn |
| 27 | + T = Tuple{E,Vararg{E,lenm1}} |
| 28 | + isnothing(i) && |
| 29 | + throw(ArgumentError("iterator has too few elements for the tuple type")) |
| 30 | + (e, s) = i::T2 |
| 31 | + Next = NTuple{lenm1,E} |
| 32 | + t = iterator_to_ntuple_recur(Next, iter, (s,))::Next |
| 33 | + (e, t...)::T |
| 34 | +end |
| 35 | + |
| 36 | +function iterator_to_ntuple(::Type{T}, iter) where {len,T<:NTuple{len,Any}} |
| 37 | + if len < 100 |
| 38 | + iterator_to_ntuple_recur(T, iter, ())::T |
| 39 | + else |
| 40 | + # prevent stack overflow during type inference |
| 41 | + let |
| 42 | + f(i) = (collect(i)...,) |
| 43 | + f(i::Tuple) = i |
| 44 | + f(i::Union{NamedTuple,Core.SimpleVector,Array,Pair}) = (i...,) |
| 45 | + f(i::Array{<:Any,0}) = (first(i),) |
| 46 | + f(i::Union{AbstractArray{<:Any,0},Number,Ref}) = (first(i),) |
| 47 | + f(i::Pair) = (first(i), last(i)) |
| 48 | + f(iter)::T |
| 49 | + end |
| 50 | + end::T |
| 51 | +end |
| 52 | + |
| 53 | +function iterator_to_tuple(::Type{R}, iter) where {R<:Tuple} |
| 54 | + f(i) = collect(i) |
| 55 | + f(i::Union{Tuple,NamedTuple,Core.SimpleVector,Array,AbstractArray{<:Any,0},Number,Ref,Pair}) = i |
| 56 | + |
| 57 | + c = f(iter) |
| 58 | + len = length(c)::Int |
| 59 | + E = eltype(c)::Type |
| 60 | + T = NTuple{len,E} |
| 61 | + r = iterator_to_ntuple(T, c)::Tuple{Vararg{E}}::T |
| 62 | + (r isa R) || _totuple_err(R) |
| 63 | + r::R |
| 64 | +end |
| 65 | + |
| 66 | +""" |
| 67 | + ntuple_any(::Type{<:NTuple{len,Any}}) where {len} |
| 68 | +
|
| 69 | +Like `ntuple(Returns(Any), Val(len))`. |
| 70 | +""" |
| 71 | +ntuple_any(::Type{Tuple{}}) = () |
| 72 | +function ntuple_any(::Type{<:Tuple{Any,Vararg{Any,lenm1}}}) where {lenm1} |
| 73 | + T = NTuple{lenm1,DataType} |
| 74 | + t = ntuple_any(T)::T |
| 75 | + (Any, t...)::NTuple{len,DataType} |
| 76 | +end |
| 77 | + |
| 78 | +""" |
| 79 | + tuple_va_type_length(::Val) |
| 80 | +
|
| 81 | +Creates a `Vararg` `<:Tuple` type of specified minimal length. |
| 82 | +""" |
| 83 | +function tuple_va_type_length(::Val{len}) where {len} |
| 84 | + Tuple{ntuple_any(NTuple{len,DataType})...,Vararg} |
| 85 | +end |
| 86 | +tuple_va_type_length(n::Int) = tuple_va_type_length(Val(n))::Type{<:Tuple} |
| 87 | + |
| 88 | +tuple_length_type_va(::Type{T}, ::Val{n}, ::Type{S}) where {T<:Tuple,n,S<:Tuple} = Val(decremented(n)) |
| 89 | +function tuple_length_type_va(::Type{T}, ::Val{n}, ::Type{S}) where {n,S<:Tuple,T<:S} |
| 90 | + v = Val(incremented(n)) |
| 91 | + tuple_length_type_va(T, v, tuple_va_type_length(v)::Type{<:Tuple})::Val |
| 92 | +end |
| 93 | + |
| 94 | +""" |
| 95 | + tuple_length_type(::Type{<:Tuple}) |
| 96 | +
|
| 97 | +Strips the element type information from a tuple type while keeping |
| 98 | +the information about the length. |
| 99 | +""" |
| 100 | +function tuple_length_type(::Type{T}) where {T<:Tuple} |
| 101 | + v = Val(1) |
| 102 | + r = tuple_length_type_va(T, v, tuple_va_type_length(v)::Type{<:Tuple})::Val |
| 103 | + tuple_va_type_length(r)::Type{<:Tuple} |
| 104 | +end |
| 105 | +tuple_length_type(::Type{T}) where {len,T<:NTuple{len,Any}} = NTuple{len,Any} |
| 106 | + |
| 107 | +""" |
| 108 | + fieldtype_typeintersect_ntuple(::Type{T}, ::Type{<:NTuple{len,Any}}, ::Val{ind}) where {T<:Tuple, len, ind} |
| 109 | +
|
| 110 | +Returns the type of the field `ind` in the type intersection of `T` with |
| 111 | +`NTuple{len,Any}`. Failing to compute the exact intersection, the field `ind` of |
| 112 | +`T` is returned. |
| 113 | +""" |
| 114 | +function fieldtype_typeintersect_ntuple( |
| 115 | + (@nospecialize T::Type{<:Tuple}), ::Type{<:NTuple{len,Any}}, ::Val{ind} |
| 116 | +) where {len,ind} |
| 117 | + Core.@_foldable_meta |
| 118 | + (1 ≤ (ind::Int) ≤ len) || throw(ArgumentError("`ind` out of bounds")) |
| 119 | + S = NTuple{len,Any} |
| 120 | + ST = typeintersect(S, T)::Type |
| 121 | + TS = typeintersect(T, S)::Type |
| 122 | + X = fieldtype(T, ind)::Type |
| 123 | + Y = fieldtype(ST, ind)::Type |
| 124 | + Z = fieldtype(TS, ind)::Type |
| 125 | + type_intrs = TIE.type_intersect_exact(X, Y, Z) |
| 126 | + TIE.get_result(X, type_intrs)::Type |
| 127 | +end |
| 128 | + |
| 129 | +function tuple_converted_elem(::Type{T}, t::NTuple{len,Any}, ::Val{ind}) where {T<:Tuple,len,ind} |
| 130 | + (1 ≤ (ind::Int) ≤ len) || throw(ArgumentError("`ind` out of bounds")) |
| 131 | + S = fieldtype_typeintersect_ntuple(T, NTuple{len,Any}, Val(ind))::Type |
| 132 | + e = t[ind] |
| 133 | + ((e isa S) ? e : convert(S, e))::S |
| 134 | +end |
| 135 | + |
| 136 | +function tuple_with_converted_elems_recur(::Type{T}, ::NTuple{len,Any}, r::NTuple{len,Any}) where {T<:Tuple,len} |
| 137 | + r::T |
| 138 | +end |
| 139 | +function tuple_with_converted_elems_recur(::Type{T}, t::NTuple{len,Any}, r::NTuple{n,Any}) where {T<:Tuple,len,n} |
| 140 | + (n < len) || throw(ArgumentError("`n` out of bounds")) |
| 141 | + m = incremented(n) |
| 142 | + s = (r..., tuple_converted_elem(T, t, Val(m)))::NTuple{m,Any} |
| 143 | + tuple_with_converted_elems_recur(T, t, s)::NTuple{len,Any} |
| 144 | +end |
| 145 | + |
| 146 | +function tuple_with_converted_elems(::Type{T}, t::NTuple{len,Any}) where {T<:Tuple,len} |
| 147 | + NT = NTuple{len,Any} |
| 148 | + type_intrs = TIE.type_intersect_exact(NT, T) |
| 149 | + S = TIE.get_result(T, type_intrs)::Type{<:Tuple} |
| 150 | + (S <: Union{}) && _totuple_err(T) |
| 151 | + tuple_with_converted_elems_recur(S, t, ())::T::NT::S |
| 152 | +end |
| 153 | + |
| 154 | +function iterator_to_tuple_with_element_types(::Type{T}, iter) where {T<:Tuple} |
| 155 | + R = tuple_length_type(T)::Type{<:Tuple} |
| 156 | + t = iterator_to_tuple(R, iter)::R |
| 157 | + tuple_with_converted_elems(T, t)::T |
| 158 | +end |
| 159 | + |
| 160 | +# As an optimization, special case some tuple types with no constraints on the |
| 161 | +# types of the elements. |
| 162 | +iterator_to_tuple_with_element_types(T::Type{NTuple{len,Any}}, i) where {len} = iterator_to_tuple(T, i)::T |
| 163 | +iterator_to_tuple_with_element_types(T::Type{tuple_va_type_length(0)}, i) = iterator_to_tuple(T, i)::T |
| 164 | +iterator_to_tuple_with_element_types(T::Type{tuple_va_type_length(1)}, i) = iterator_to_tuple(T, i)::T |
| 165 | +iterator_to_tuple_with_element_types(T::Type{tuple_va_type_length(2)}, i) = iterator_to_tuple(T, i)::T |
| 166 | +iterator_to_tuple_with_element_types(T::Type{tuple_va_type_length(3)}, i) = iterator_to_tuple(T, i)::T |
| 167 | +iterator_to_tuple_with_element_types(T::Type{tuple_va_type_length(4)}, i) = iterator_to_tuple(T, i)::T |
| 168 | + |
| 169 | +end |
0 commit comments