diff --git a/src/MArray.jl b/src/MArray.jl index 534c350d9..970e7d211 100644 --- a/src/MArray.jl +++ b/src/MArray.jl @@ -20,42 +20,23 @@ the compiler (the element type may optionally also be specified). mutable struct MArray{S <: Tuple, T, N, L} <: StaticArray{S, T, N} data::NTuple{L,T} - function MArray{S,T,N,L}(x::NTuple{L,T}) where {S,T,N,L} + function MArray{S,T,N,L}(x::NTuple{L,T}) where {S<:Tuple,T,N,L} check_array_parameters(S, T, Val{N}, Val{L}) new{S,T,N,L}(x) end - function MArray{S,T,N,L}(x::NTuple{L,Any}) where {S,T,N,L} + function MArray{S,T,N,L}(x::NTuple{L,Any}) where {S<:Tuple,T,N,L} check_array_parameters(S, T, Val{N}, Val{L}) new{S,T,N,L}(convert_ntuple(T, x)) end - function MArray{S,T,N,L}(::UndefInitializer) where {S,T,N,L} + function MArray{S,T,N,L}(::UndefInitializer) where {S<:Tuple,T,N,L} check_array_parameters(S, T, Val{N}, Val{L}) new{S,T,N,L}() end end -@generated function (::Type{MArray{S,T,N}})(x::Tuple) where {S,T,N} - return quote - $(Expr(:meta, :inline)) - MArray{S,T,N,$(tuple_prod(S))}(x) - end -end - -@generated function (::Type{MArray{S,T}})(x::Tuple) where {S,T} - return quote - $(Expr(:meta, :inline)) - MArray{S,T,$(tuple_length(S)),$(tuple_prod(S))}(x) - end -end - -@generated function (::Type{MArray{S}})(x::T) where {S, T <: Tuple} - return quote - $(Expr(:meta, :inline)) - MArray{S,promote_tuple_eltype(T),$(tuple_length(S)),$(tuple_prod(S))}(x) - end -end +@inline MArray{S,T,N}(x::Tuple) where {S<:Tuple,T,N} = MArray{S,T,N,length(x)}(x) @generated function (::Type{MArray{S,T,N}})(::UndefInitializer) where {S,T,N} return quote @@ -71,8 +52,6 @@ end end end -@inline MArray(a::StaticArray{S,T}) where {S<:Tuple,T} = MArray{S,T}(Tuple(a)) - #################### ## MArray methods ## #################### diff --git a/src/MMatrix.jl b/src/MMatrix.jl index 9d7c1fc98..7d1100ca3 100644 --- a/src/MMatrix.jl +++ b/src/MMatrix.jl @@ -17,33 +17,6 @@ unknown to the compiler (the element type may optionally also be specified). """ const MMatrix{S1, S2, T, L} = MArray{Tuple{S1, S2}, T, 2, L} -@generated function (::Type{MMatrix{S1}})(x::NTuple{L}) where {S1,L} - S2 = div(L, S1) - if S1*S2 != L - throw(DimensionMismatch("Incorrect matrix sizes. $S1 does not divide $L elements")) - end - return quote - $(Expr(:meta, :inline)) - T = eltype(typeof(x)) - MMatrix{S1, $S2, T, L}(x) - end -end - -@generated function (::Type{MMatrix{S1,S2}})(x::NTuple{L}) where {S1,S2,L} - return quote - $(Expr(:meta, :inline)) - T = eltype(typeof(x)) - MMatrix{S1, S2, T, L}(x) - end -end - -@generated function (::Type{MMatrix{S1,S2,T}})(x::NTuple{L}) where {S1,S2,T,L} - return quote - $(Expr(:meta, :inline)) - MMatrix{S1, S2, T, L}(x) - end -end - @generated function (::Type{MMatrix{S1,S2,T}})(::UndefInitializer) where {S1,S2,T} return quote $(Expr(:meta, :inline)) @@ -51,9 +24,6 @@ end end end -@inline convert(::Type{MMatrix{S1,S2}}, a::StaticArray{<:Tuple, T}) where {S1,S2,T} = MMatrix{S1,S2,T}(Tuple(a)) -@inline MMatrix(a::StaticMatrix{N,M,T}) where {N,M,T} = MMatrix{N,M,T}(Tuple(a)) - # Some more advanced constructor-like functions @inline one(::Type{MMatrix{N}}) where {N} = one(MMatrix{N,N}) diff --git a/src/MVector.jl b/src/MVector.jl index 9eb303b99..f605564f3 100644 --- a/src/MVector.jl +++ b/src/MVector.jl @@ -16,11 +16,6 @@ compiler (the element type may optionally also be specified). """ const MVector{S, T} = MArray{Tuple{S}, T, 1, S} -@inline MVector(a::StaticVector{N,T}) where {N,T} = MVector{N,T}(a) -@inline MVector(x::NTuple{S,Any}) where {S} = MVector{S}(x) -@inline MVector{S}(x::NTuple{S,T}) where {S, T} = MVector{S, T}(x) -@inline MVector{S}(x::NTuple{S,Any}) where {S} = MVector{S, promote_tuple_eltype(typeof(x))}(x) - # Some more advanced constructor-like functions @inline zeros(::Type{MVector{N}}) where {N} = zeros(MVector{N,Float64}) @inline ones(::Type{MVector{N}}) where {N} = ones(MVector{N,Float64}) diff --git a/src/SArray.jl b/src/SArray.jl index 58986aabb..36d5226b7 100644 --- a/src/SArray.jl +++ b/src/SArray.jl @@ -18,38 +18,18 @@ compiler (the element type may optionally also be specified). struct SArray{S <: Tuple, T, N, L} <: StaticArray{S, T, N} data::NTuple{L,T} - function SArray{S, T, N, L}(x::NTuple{L,T}) where {S, T, N, L} + function SArray{S, T, N, L}(x::NTuple{L,T}) where {S<:Tuple, T, N, L} check_array_parameters(S, T, Val{N}, Val{L}) new{S, T, N, L}(x) end - function SArray{S, T, N, L}(x::NTuple{L,Any}) where {S, T, N, L} + function SArray{S, T, N, L}(x::NTuple{L,Any}) where {S<:Tuple, T, N, L} check_array_parameters(S, T, Val{N}, Val{L}) new{S, T, N, L}(convert_ntuple(T, x)) end end -@generated function (::Type{SArray{S, T, N}})(x::Tuple) where {S <: Tuple, T, N} - return quote - @_inline_meta - SArray{S, T, N, $(tuple_prod(S))}(x) - end -end - -@generated function (::Type{SArray{S, T}})(x::Tuple) where {S <: Tuple, T} - return quote - @_inline_meta - SArray{S, T, $(tuple_length(S)), $(tuple_prod(S))}(x) - end -end - -@generated function (::Type{SArray{S}})(x::T) where {S <: Tuple, T <: Tuple} - return quote - @_inline_meta - SArray{S, promote_tuple_eltype(T), $(tuple_length(S)), $(tuple_prod(S))}(x) - end -end - +@inline SArray{S,T,N}(x::Tuple) where {S<:Tuple,T,N} = SArray{S,T,N,length(x)}(x) @noinline function generator_too_short_error(inds::CartesianIndices, i::CartesianIndex) error("Generator produced too few elements: Expected exactly $(shape_string(inds)) elements, but generator stopped at $(shape_string(i))") @@ -106,8 +86,6 @@ sacollect @inline (::Type{SA})(gen::Base.Generator) where {SA <: StaticArray} = sacollect(SA, gen) -@inline SArray(a::StaticArray{S,T}) where {S<:Tuple,T} = SArray{S,T}(Tuple(a)) - #################### ## SArray methods ## #################### diff --git a/src/SHermitianCompact.jl b/src/SHermitianCompact.jl index a6a8a9d00..ab68ab21b 100644 --- a/src/SHermitianCompact.jl +++ b/src/SHermitianCompact.jl @@ -60,6 +60,7 @@ lowertriangletype(::Type{SHermitianCompact{N}}) where {N} = SVector{triangularnu end @generated function SHermitianCompact{N, T, L}(a::Tuple) where {N, T, L} + _check_hermitian_parameters(Val(N), Val(L)) expr = Vector{Expr}(undef, L) i = 0 for col = 1 : N, row = col : N @@ -77,15 +78,13 @@ end SHermitianCompact{N, T, L}(a) end -@inline SHermitianCompact{N}(a::Tuple) where {N} = SHermitianCompact{N, promote_tuple_eltype(a)}(a) -@inline SHermitianCompact{N}(a::NTuple{M, T}) where {N, T, M} = SHermitianCompact{N, T}(a) -@inline SHermitianCompact(a::StaticMatrix{N, N, T}) where {N, T} = SHermitianCompact{N, T}(a) - @inline (::Type{SSC})(a::SHermitianCompact) where {SSC <: SHermitianCompact} = SSC(a.lowertriangle) -@inline (::Type{SSC})(a::SSC) where {SSC <: SHermitianCompact} = a @inline (::Type{SSC})(a::AbstractVector) where {SSC <: SHermitianCompact} = SSC(convert(lowertriangletype(SSC), a)) +# disambiguation +@inline (::Type{SSC})(a::StaticArray{<:Tuple,<:Any,1}) where {SSC <: SHermitianCompact} = SSC(convert(SVector, a)) + @generated function _hermitian_compact_indices(::Val{N}) where N # Returns a Tuple{Pair{Int, Bool}} I such that for linear index i, # * I[i][1] is the index into the lowertriangle field of an SHermitianCompact{N}; diff --git a/src/SMatrix.jl b/src/SMatrix.jl index 39abab61d..f18c70888 100644 --- a/src/SMatrix.jl +++ b/src/SMatrix.jl @@ -16,50 +16,11 @@ unknown to the compiler (the element type may optionally also be specified). """ const SMatrix{S1, S2, T, L} = SArray{Tuple{S1, S2}, T, 2, L} -@generated function SMatrix{S1}(x::NTuple{L,Any}) where {S1,L} - S2 = div(L, S1) - if S1*S2 != L - throw(DimensionMismatch("Incorrect matrix sizes. $S1 does not divide $L elements")) - end - - return quote - $(Expr(:meta, :inline)) - T = promote_tuple_eltype(typeof(x)) - SMatrix{S1, $S2, T, L}(x) - end -end - -@generated function SMatrix{S1,S2}(x::NTuple{L,Any}) where {S1,S2,L} - return quote - $(Expr(:meta, :inline)) - T = promote_tuple_eltype(typeof(x)) - SMatrix{S1, S2, T, L}(x) - end -end -SMatrixNoType{S1, S2, L, T} = SMatrix{S1, S2, T, L} -@generated function SMatrixNoType{S1, S2, L}(x::NTuple{L,Any}) where {S1,S2,L} - return quote - $(Expr(:meta, :inline)) - T = promote_tuple_eltype(typeof(x)) - SMatrix{S1, S2, T, L}(x) - end -end - -@generated function SMatrix{S1,S2,T}(x::NTuple{L,Any}) where {S1,S2,T,L} - return quote - $(Expr(:meta, :inline)) - SMatrix{S1, S2, T, L}(x) - end -end - @inline SMatrix{M, N, T}(gen::Base.Generator) where {M, N, T} = sacollect(SMatrix{M, N, T}, gen) @inline SMatrix{M, N}(gen::Base.Generator) where {M, N} = sacollect(SMatrix{M, N}, gen) -@inline convert(::Type{SMatrix{S1,S2}}, a::StaticArray{<:Tuple, T}) where {S1,S2,T} = SMatrix{S1,S2,T}(Tuple(a)) -@inline SMatrix(a::StaticMatrix{S1, S2, T}) where {S1, S2, T} = SMatrix{S1, S2, T}(Tuple(a)) - # Some more advanced constructor-like functions @inline one(::Type{SMatrix{N}}) where {N} = one(SMatrix{N,N}) diff --git a/src/SVector.jl b/src/SVector.jl index bb039e135..ffb19b6cf 100644 --- a/src/SVector.jl +++ b/src/SVector.jl @@ -15,11 +15,6 @@ compiler (the element type may optionally also be specified). """ const SVector{S, T} = SArray{Tuple{S}, T, 1, S} -@inline SVector(a::StaticVector{N,T}) where {N,T} = SVector{N,T}(a) -@inline SVector(x::NTuple{S,Any}) where {S} = SVector{S}(x) -@inline SVector{S}(x::NTuple{S,T}) where {S, T} = SVector{S,T}(x) -@inline SVector{S}(x::T) where {S, T <: Tuple} = SVector{S,promote_tuple_eltype(T)}(x) - @inline SVector{N, T}(gen::Base.Generator) where {N, T} = sacollect(SVector{N, T}, gen) @inline SVector{N}(gen::Base.Generator) where {N} = diff --git a/src/Scalar.jl b/src/Scalar.jl index dc85da614..98016b22c 100644 --- a/src/Scalar.jl +++ b/src/Scalar.jl @@ -6,13 +6,11 @@ Construct a statically-sized 0-dimensional array that contains a single element, """ const Scalar{T} = SArray{Tuple{},T,0,1} -@inline Scalar(x::Tuple{T}) where {T} = Scalar{T}(x[1]) @inline Scalar(a::AbstractArray) = Scalar{typeof(a)}((a,)) +@inline Scalar(a::StaticArray) = Scalar{typeof(a)}((a,)) # disambiguation + @inline Scalar(a::AbstractScalar) = Scalar{eltype(a)}((a[],)) # Do we want this to convert or wrap? -@inline function convert(::Type{SA}, a::AbstractArray) where {SA <: Scalar} - return SA((a[],)) -end -@inline convert(::Type{SA}, sa::SA) where {SA <: Scalar} = sa +@inline Scalar(a::StaticScalar) = Scalar{eltype(a)}((a[],)) # disambiguation @propagate_inbounds function getindex(v::Scalar, i::Int) @boundscheck if i != 1 diff --git a/src/SizedArray.jl b/src/SizedArray.jl index 695c0a7f7..e4251c0cb 100644 --- a/src/SizedArray.jl +++ b/src/SizedArray.jl @@ -16,7 +16,7 @@ wrap a 2x3 array `a` in a `SizedArray`, use `SizedMatrix{2,3}(a)`. struct SizedArray{S<:Tuple,T,N,M,TData<:AbstractArray{T,M}} <: StaticArray{S,T,N} data::TData - function SizedArray{S,T,N,M,TData}(a::TData) where {S,T,N,M,TData<:AbstractArray{T,M}} + function SizedArray{S,T,N,M,TData}(a::TData) where {S<:Tuple,T,N,M,TData<:AbstractArray{T,M}} require_one_based_indexing(a) if size(a) != size_to_tuple(S) && size(a) != (tuple_prod(S),) throw(DimensionMismatch("Dimensions $(size(a)) don't match static size $S")) @@ -24,40 +24,49 @@ struct SizedArray{S<:Tuple,T,N,M,TData<:AbstractArray{T,M}} <: StaticArray{S,T,N return new{S,T,N,M,TData}(a) end - function SizedArray{S,T,N,1,TData}(::UndefInitializer) where {S,T,N,TData<:AbstractArray{T,1}} + function SizedArray{S,T,N,1,TData}(::UndefInitializer) where {S<:Tuple,T,N,TData<:AbstractArray{T,1}} return new{S,T,N,1,TData}(TData(undef, tuple_prod(S))) end - function SizedArray{S,T,N,N,TData}(::UndefInitializer) where {S,T,N,TData<:AbstractArray{T,N}} + function SizedArray{S,T,N,N,TData}(::UndefInitializer) where {S<:Tuple,T,N,TData<:AbstractArray{T,N}} return new{S,T,N,N,TData}(TData(undef, size_to_tuple(S)...)) end end -# Julia v1.0 has some weird bug that prevents this from working -@inline SizedArray(a::StaticArray{S,T,N}) where {S<:Tuple,T,N} = SizedArray{S,T,N}(a) -@inline function SizedArray{S,T,N}( - a::TData, -) where {S,T,N,M,TData<:AbstractArray{T,M}} - return SizedArray{S,T,N,M,TData}(a) -end -@inline function SizedArray{S,T}(a::TData) where {S,T,M,TData<:AbstractArray{T,M}} - return SizedArray{S,T,tuple_length(S),M,TData}(a) +@inline function SizedArray{S,T,N,M}(a::AbstractArray) where {S<:Tuple,T,N,M} + if eltype(a) == T && (M == 1 || M == ndims(a)) + a′ = M == 1 ? vec(a) : a + return SizedArray{S,T,N,M,typeof(a′)}(a′) + end + return convert(SizedArray{S,T,N,M}, a) end -@inline function SizedArray{S}(a::TData) where {S,T,M,TData<:AbstractArray{T,M}} - return SizedArray{S,T,tuple_length(S),M,TData}(a) + +@inline function SizedArray{S,T,N}(a::AbstractArray) where {S<:Tuple,T,N} + M = ndims(a) == N ? N : 1 + return SizedArray{S,T,N,M}(a) end -function SizedArray{S,T,N,N}(::UndefInitializer) where {S,T,N} + +@inline (::Type{SZA})(a::AbstractArray) where {SZA<:SizedArray} = construct_type(SZA, a)(a) + +# disambiguation +@inline SizedArray{S,T,N,M}(a::StaticArray) where {S<:Tuple,T,N,M} = construct_type(SizedArray{S,T,N,M}, a)(a.data) +@inline SizedArray{S,T,N}(a::StaticArray) where {S<:Tuple,T,N} = construct_type(SizedArray{S,T,N}, a)(a.data) +@inline (::Type{SZA})(a::StaticArray) where {SZA<:SizedArray} = construct_type(SZA, a)(a.data) +# TODO: Should we respect `TData`? +SizedArray{S,T,N,M,TData}(a::TData) where {S<:Tuple,T,N,M,TData<:StaticArray{<:Tuple,T,M}} = SizedArray{S,T,N,M}(a.data) + +function SizedArray{S,T,N,N}(::UndefInitializer) where {S<:Tuple,T,N} return SizedArray{S,T,N,N,Array{T,N}}(undef) end -function SizedArray{S,T,N,1}(::UndefInitializer) where {S,T,N} +function SizedArray{S,T,N,1}(::UndefInitializer) where {S<:Tuple,T,N} return SizedArray{S,T,N,1,Vector{T}}(undef) end -@inline function SizedArray{S,T,N}(::UndefInitializer) where {S,T,N} +@inline function SizedArray{S,T,N}(::UndefInitializer) where {S<:Tuple,T,N} return SizedArray{S,T,N,N}(undef) end -@inline function SizedArray{S,T}(::UndefInitializer) where {S,T} +@inline function SizedArray{S,T}(::UndefInitializer) where {S<:Tuple,T} return SizedArray{S,T,tuple_length(S)}(undef) end -@generated function (::Type{SizedArray{S,T,N,M,TData}})(x::NTuple{L,Any}) where {S,T,N,M,TData<:AbstractArray{T,M},L} +@generated function (::Type{SizedArray{S,T,N,M,TData}})(x::NTuple{L,Any}) where {S<:Tuple,T,N,M,TData<:AbstractArray{T,M},L} if L != tuple_prod(S) error("Dimension mismatch") end @@ -69,18 +78,12 @@ end return a end end -@inline function SizedArray{S,T,N,M}(x::Tuple) where {S,T,N,M} +@inline function SizedArray{S,T,N,M}(x::Tuple) where {S<:Tuple,T,N,M} return SizedArray{S,T,N,M,Array{T,M}}(x) end -@inline function SizedArray{S,T,N}(x::Tuple) where {S,T,N} +@inline function SizedArray{S,T,N}(x::Tuple) where {S<:Tuple,T,N} return SizedArray{S,T,N,N,Array{T,N}}(x) end -@inline function SizedArray{S,T}(x::Tuple) where {S,T} - return SizedArray{S,T,tuple_length(S)}(x) -end -@inline function SizedArray{S}(x::NTuple{L,T}) where {S,T,L} - return SizedArray{S,T}(x) -end # Overide some problematic default behaviour @inline convert(::Type{SA}, sa::SizedArray) where {SA<:SizedArray} = SA(sa.data) @@ -129,43 +132,8 @@ Base.elsize(::Type{SizedArray{S,T,M,N,A}}) where {S,T,M,N,A} = Base.elsize(A) const SizedVector{S,T} = SizedArray{Tuple{S},T,1,1} -SizedVector(a::StaticVector{N,T}) where {N,T} = SizedVector{N,T}(a) -@inline function SizedVector{S}(a::TData) where {S,T,TData<:AbstractVector{T}} - return SizedArray{Tuple{S},T,1,1,TData}(a) -end -@inline function SizedVector(x::NTuple{S,T}) where {S,T} - return SizedArray{Tuple{S},T,1,1,Vector{T}}(x) -end -@inline function SizedVector{S}(x::NTuple{S,T}) where {S,T} - return SizedArray{Tuple{S},T,1,1,Vector{T}}(x) -end -@inline function SizedVector{S,T}(x::NTuple{S}) where {S,T} - return SizedArray{Tuple{S},T,1,1,Vector{T}}(x) -end -# disambiguation -@inline function SizedVector{S}(a::StaticVector{S,T}) where {S,T} - return SizedVector{S,T}(a.data) -end - const SizedMatrix{S1,S2,T} = SizedArray{Tuple{S1,S2},T,2} -SizedMatrix(a::StaticMatrix{N,M,T}) where {N,M,T} = SizedMatrix{N,M,T}(a) -@inline function SizedMatrix{S1,S2}( - a::TData, -) where {S1,S2,T,M,TData<:AbstractArray{T,M}} - return SizedArray{Tuple{S1,S2},T,2,M,TData}(a) -end -@inline function SizedMatrix{S1,S2}(x::NTuple{L,T}) where {S1,S2,T,L} - return SizedArray{Tuple{S1,S2},T,2,2,Matrix{T}}(x) -end -@inline function SizedMatrix{S1,S2,T}(x::NTuple{L}) where {S1,S2,T,L} - return SizedArray{Tuple{S1,S2},T,2,2,Matrix{T}}(x) -end -# disambiguation -@inline function SizedMatrix{S1,S2}(a::StaticMatrix{S1,S2,T}) where {S1,S2,T} - return SizedMatrix{S1,S2,T}(a.data) -end - Base.dataids(sa::SizedArray) = Base.dataids(sa.data) function promote_rule( diff --git a/src/convert.jl b/src/convert.jl index cadb44044..ba277d8e8 100644 --- a/src/convert.jl +++ b/src/convert.jl @@ -1,14 +1,113 @@ -(::Type{SA})(x::Tuple{Tuple{Tuple{<:Tuple}}}) where {SA <: StaticArray} = - throw(DimensionMismatch("No precise constructor for $SA found. Length of input was $(length(x[1][1][1])).")) +# A help wrapper to distinguish `SA(x...)` and `SA((x...,))` +struct Args{T<:Tuple} + args::T +end + +# Some help functions. +@pure has_eltype(::Type{<:StaticArray{<:Tuple,T}}) where {T} = @isdefined T +@pure has_eltype(::Type{<:StaticArray}) = false +@pure has_size(::Type{<:StaticArray{S}}) where {S<:Tuple} = @isdefined S +@pure has_size(::Type{<:StaticArray}) = false +@pure has_size1(::Type{<:StaticMatrix{M}}) where {M} = @isdefined M +@pure has_size1(::Type{<:StaticMatrix}) = false +_size1(::Type{<:StaticMatrix{M}}) where {M} = M +StaticSquareMatrix{N,T} = StaticMatrix{N,N,T} +@generated function _sqrt(::Length{L}) where {L} + N = round(Int, sqrt(L)) + N^2 == L || throw(DimensionMismatch("Input's length must be perfect square")) + return :($N) +end + +# Here we define `construct_type(SA, x)` to pick the "proper" constructor. +# Different `x` has different rules, to summarize: +# 1. Tuple +# We try to fill `SA` with elements in `x` if `SA` is static-sized. +# If `SA <: StaticMatrix{M}`, the output `Size` is derived based on `length(x)÷M`. +# If `SA <: StaticMatrix{M,M} where M`, the output `Size` is derived based on `sqrt(length(x))`. +# If `length(SA) == 1 && length(x) > 1`, then we tries to fill `SA` with `x` itself. (rewrapping) +construct_type(SA, x::Tuple) = construct_type(SA, x, Tuple{length(x)}, promote_tuple_eltype(x)) +# 2. Args (`SA(x...)`) +# Similar to `Tuple`, but re-wrapping is not allowed. +construct_type(SA, x::Args) = construct_type(SA, x, Tuple{length(x.args)}, promote_tuple_eltype(x.args)) +# 3. StaticArray +# Treat `x` as `Tuple` whenever possible. If failed, then try to inherit `x`'s `Size`. +construct_type(SA, x::StaticArray) = construct_type(SA, x, Tuple{size(x)...}, eltype(x)) +# 4. AbstractArray +# `x` is used to provide eltype. Thus `SA` must be static sized. +function construct_type(SA, x::AbstractArray) + ET = has_eltype(SA) ? eltype(SA) : eltype(x) + has_size(SA) || _no_precise_constructor(SA) + construct_type(Tuple{SA, AbstractArray}, ET, Tuple{size(SA)...}) +end -@inline (::Type{SA})(x...) where {SA <: StaticArray} = SA(x) -@inline (::Type{SA})(a::StaticArray) where {SA<:StaticArray} = SA(Tuple(a)) -@inline (::Type{SA})(a::StaticArray) where {SA<:SizedArray} = SA(a.data) +function construct_type(::Type{SA}, x::A, ::Type{SZ}, ::Type{T}) where {SA <: StaticArray, A <: Union{Tuple, Args, StaticArray}, SZ<:Tuple, T} + len = tuple_prod(SZ) + ET = has_eltype(SA) ? eltype(SA) : T + if has_size(SA) # SA has size defined, just check its validity. + if length(SA) == len + return construct_type(Tuple{SA, A}, ET, Tuple{size(SA)...}) + elseif !(A <: Args) && length(SA) == 1 + has_eltype(SA) || (ET = typeof(x)) + return construct_type(Tuple{SA, Args{Tuple{typeof(x)}}}, ET, Tuple{size(SA)...}) + end + elseif SA <: StaticVector + # For vector, use input length directly + return construct_type(Tuple{SA, A}, ET, Tuple{len}) + elseif SA <: StaticMatrix && has_size1(SA) + # For `SMatrix{N}(...)` + # TODO: is there a better way to extend this branch to, e.g. "?*n" "m*?*n"? + N = _size1(SA) + M = len ÷ N + M * N == len || throw(DimensionMismatch("Incorrect matrix sizes. $len does not divide $N elements")) + return construct_type(Tuple{SA, A}, ET, Tuple{N, M}) + elseif SA <: StaticSquareMatrix + # For `SHermitianCompact(...)`, `Rotations.jl` also needs this branch. + N = _sqrt(Length(len)) + return construct_type(Tuple{SA, A}, ET, Tuple{N, N}) + elseif A <: StaticArray + # Here we just try with src's shape. + return construct_type(Tuple{SA, A}, ET, SZ) + end + _no_precise_constructor(SA, SZ) +end + +function construct_type(::Type{Tuple{SA, A}}, ::Type{ET}, ::Type{SZ}) where {SA <: StaticArray, A <: Union{Tuple, Args, StaticArray, AbstractArray}, ET, SZ <: Tuple} + # Here we use Base.typeintersect to get the most concrete dest type (if valid) + # It's similar to `similar_type`, but not fallback to SArray by default. + T = Base.typeintersect(SA, StaticArray{SZ,ET,tuple_length(SZ)}) + check_parameters(T) || _no_precise_constructor(SA, SZ) + A <: Tuple && T === SA && error("Constructor is missing for $(SA)(::Tuple), please file a bug.") + return T +end + +check_parameters(::Type{<:StaticArray{S,T,N}}) where {S,T,N} = @isdefined(S) && @isdefined(T) && @isdefined(N) +check_parameters(::Type{SArray{S,T,N,L}}) where {S,T,N,L} = (check_array_parameters(S,T,Val{N},Val{L}); true) +check_parameters(::Type{MArray{S,T,N,L}}) where {S,T,N,L} = (check_array_parameters(S,T,Val{N},Val{L}); true) +check_parameters(::Type{SHermitianCompact{N,T,L}}) where {N,T,L} = (_check_hermitian_parameters(Val(N), Val(L));true) +check_parameters(::Type{Union{}}) = false + +_no_precise_constructor(SA, ::Type{Tuple{N}}) where {N} = throw(DimensionMismatch("No precise constructor for $SA found. Length of input was $N.")) +_no_precise_constructor(SA, SZ::Type{<:Tuple}) = throw(DimensionMismatch("No precise constructor for $SA found. Size of input was $SZ.")) +_no_precise_constructor(SA) = throw(DimensionMismatch("No precise constructor for $SA found. Input is not static sized.")) + +@inline (::Type{SA})(x...) where {SA <: StaticArray} = construct_type(SA, Args(x))(x) +@inline function (::Type{SA})(x::Tuple) where {SA <: Union{SArray, MArray, SHermitianCompact, SizedArray}} + SA′ = construct_type(SA, x) + x isa eltype(SA′) ? SA′((x,)) : SA′(x) +end +@inline function (::Type{SA})(sa::StaticArray) where {SA <: StaticArray} + SA′ = construct_type(SA, sa) + sa isa eltype(SA′) ? SA′((sa,)) : SA′(Tuple(sa)) +end @propagate_inbounds (::Type{SA})(a::AbstractArray) where {SA <: StaticArray} = convert(SA, a) # this covers most conversions and "statically-sized reshapes" -@inline convert(::Type{SA}, sa::StaticArray) where {SA<:StaticArray} = SA(Tuple(sa)) -@inline convert(::Type{SA}, sa::StaticArray) where {SA<:Scalar} = SA((sa[],)) # disambiguation +@inline function convert(::Type{SA}, sa::StaticArray{S}) where {SA<:StaticArray,S<:Tuple} + SA′ = construct_type(SA, sa) + # `SA′((sa,))` is not valid. As we want `SA′(sa...)` + sa isa eltype(SA′) && throw_convert(SA, S) + return SA′(Tuple(sa)) +end @inline convert(::Type{SA}, sa::SA) where {SA<:StaticArray} = sa @inline convert(::Type{SA}, x::Tuple) where {SA<:StaticArray} = SA(x) # convert -> constructor. Hopefully no loops... @@ -29,21 +128,20 @@ end @boundscheck if length(a) != length(SA) dimension_mismatch_fail(SA, a) end - - return _convert(SA, a, Length(SA)) + SA′ = construct_type(SA, a) + return SA′(unroll_tuple(a, Length(SA′))) end -@inline _convert(SA, a, l::Length) = SA(unroll_tuple(a, l)) -@inline _convert(SA::Type{<:StaticArray{<:Tuple,T}}, a, ::Length{0}) where T = similar_type(SA, T)(()) -@inline _convert(SA, a, ::Length{0}) = similar_type(SA, eltype(a))(()) - length_val(a::T) where {T <: StaticArrayLike} = length_val(Size(T)) length_val(a::Type{T}) where {T<:StaticArrayLike} = length_val(Size(T)) +unroll_tuple(a::AbstractArray, ::Length{0}) = () +unroll_tuple(a::AbstractArray, ::Length{1}) = @inbounds (a[],) @generated function unroll_tuple(a::AbstractArray, ::Length{L}) where {L} - exprs = [:(a[$j]) for j = 1:L] + exprs = (:(a[$j+Δj]) for j = 0:L-1) quote @_inline_meta + Δj = firstindex(a) @inbounds return $(Expr(:tuple, exprs...)) end end diff --git a/src/util.jl b/src/util.jl index 727c81a0e..a59576d15 100644 --- a/src/util.jl +++ b/src/util.jl @@ -19,6 +19,8 @@ TupleN{T,N} = NTuple{N,T} end # Base gives up on tuples for promote_eltype... (TODO can we improve Base?) +_NTuple{T} = Tuple{T,Vararg{T}} +promote_tuple_eltype(::Union{_NTuple{T}, Type{<:_NTuple{T}}}) where {T} = T @generated function promote_tuple_eltype(::Union{T,Type{T}}) where T <: Tuple t = Union{} for i = 1:length(T.parameters) diff --git a/test/MArray.jl b/test/MArray.jl index 3a5cf42d9..8fb36211e 100644 --- a/test/MArray.jl +++ b/test/MArray.jl @@ -19,6 +19,8 @@ end @testset "Outer constructors and macro" begin + @test_throws Exception MArray(1,2,3,4) # unknown constructor + @test MArray{Tuple{1},Int,1}((1,)).data === (1,) @test MArray{Tuple{1},Int}((1,)).data === (1,) @test MArray{Tuple{1}}((1,)).data === (1,) @@ -189,3 +191,19 @@ @test v[] == 2 end end + +@testset "some special case" begin + @test_throws Exception MVector{1}(1, 2) + @test (@inferred(MVector{1}((1, 2)))::MVector{1,NTuple{2,Int}}).data === ((1,2),) + @test (@inferred(MVector{2}((1, 2)))::MVector{2,Int}).data === (1,2) + @test (@inferred(MVector(1, 2))::MVector{2,Int}).data === (1,2) + @test (@inferred(MVector((1, 2)))::MVector{2,Int}).data === (1,2) + + @test_throws Exception MMatrix{1,1}(1, 2) + @test (@inferred(MMatrix{1,1}((1, 2)))::MMatrix{1,1,NTuple{2,Int}}).data === ((1,2),) + @test (@inferred(MMatrix{1,2}((1, 2)))::MMatrix{1,2,Int}).data === (1,2) + @test (@inferred(MMatrix{1}((1, 2)))::MMatrix{1,2,Int}).data === (1,2) + @test (@inferred(MMatrix{1}(1, 2))::MMatrix{1,2,Int}).data === (1,2) + @test (@inferred(MMatrix{2}((1, 2)))::MMatrix{2,1,Int}).data === (1,2) + @test (@inferred(MMatrix{2}(1, 2))::MMatrix{2,1,Int}).data === (1,2) +end diff --git a/test/MMatrix.jl b/test/MMatrix.jl index 105fec397..623285751 100644 --- a/test/MMatrix.jl +++ b/test/MMatrix.jl @@ -18,6 +18,8 @@ end @testset "Outer constructors and macro" begin + @test_throws Exception MMatrix(1,2,3,4) # unknown constructor + @test MMatrix{1,1,Int}((1,)).data === (1,) @test MMatrix{1,1}((1,)).data === (1,) @test MMatrix{1}((1,)).data === (1,) @@ -52,6 +54,7 @@ test_expand_error(:(@MMatrix sin(1:5))) test_expand_error(:(@MMatrix [1; 2; 3; 4]...)) + @test ((@MMatrix [1 2.;3 4])::MMatrix{2, 2, Float64}).data === (1., 3., 2., 4.) #issue #911 @test ((@MMatrix zeros(2,2))::MMatrix{2, 2, Float64}).data === (0.0, 0.0, 0.0, 0.0) @test ((@MMatrix fill(3.4, 2,2))::MMatrix{2, 2, Float64}).data === (3.4, 3.4, 3.4, 3.4) @test ((@MMatrix ones(2,2))::MMatrix{2, 2, Float64}).data === (1.0, 1.0, 1.0, 1.0) diff --git a/test/SArray.jl b/test/SArray.jl index d9b494fc4..019b7f86b 100644 --- a/test/SArray.jl +++ b/test/SArray.jl @@ -17,6 +17,8 @@ end @testset "Outer constructors and macro" begin + @test_throws Exception SArray(1,2,3,4) # unknown constructor + @test SArray{Tuple{1},Int,1}((1,)).data === (1,) @test SArray{Tuple{1},Int}((1,)).data === (1,) @test SArray{Tuple{1}}((1,)).data === (1,) @@ -175,3 +177,21 @@ @test @inferred(promote_type(SMatrix{2,3,Float32,6}, SMatrix{2,3,Complex{Float64},6})) === SMatrix{2,3,Complex{Float64},6} end end + +@testset "some special case" begin + @test_throws Exception (SArray{Tuple{2,M,N}} where {M,N})(SArray{Tuple{3,2,1}}(1,2,3,4,5,6)) + + @test_throws Exception SVector{1}(1, 2) + @test (@inferred(SVector{1}((1, 2)))::SVector{1,NTuple{2,Int}}).data === ((1,2),) + @test (@inferred(SVector{2}((1, 2)))::SVector{2,Int}).data === (1,2) + @test (@inferred(SVector(1, 2))::SVector{2,Int}).data === (1,2) + @test (@inferred(SVector((1, 2)))::SVector{2,Int}).data === (1,2) + + @test_throws Exception SMatrix{1,1}(1, 2) + @test (@inferred(SMatrix{1,1}((1, 2)))::SMatrix{1,1,NTuple{2,Int}}).data === ((1,2),) + @test (@inferred(SMatrix{1,2}((1, 2)))::SMatrix{1,2,Int}).data === (1,2) + @test (@inferred(SMatrix{1}((1, 2)))::SMatrix{1,2,Int}).data === (1,2) + @test (@inferred(SMatrix{1}(1, 2))::SMatrix{1,2,Int}).data === (1,2) + @test (@inferred(SMatrix{2}((1, 2)))::SMatrix{2,1,Int}).data === (1,2) + @test (@inferred(SMatrix{2}(1, 2))::SMatrix{2,1,Int}).data === (1,2) +end diff --git a/test/SHermitianCompact.jl b/test/SHermitianCompact.jl index c710fb541..0b4f38849 100644 --- a/test/SHermitianCompact.jl +++ b/test/SHermitianCompact.jl @@ -58,6 +58,11 @@ fill3(x) = fill(3, x) end @testset "Outer Constructors" begin + @test @inferred(SHermitianCompact(MVector(1,2,3))) === SHermitianCompact(SVector(1,2,3)) + @test_throws Exception SHermitianCompact(1,2,3) + @test_throws Exception SHermitianCompact(1,2,3,4,5) + @test @inferred(SHermitianCompact(1,2,3,4)) === SHermitianCompact(SVector(1,2,4)) + for (N, L) in ((3, 6), (4, 10), (6, 21)) for T in (Int32, Int64) @eval begin diff --git a/test/SMatrix.jl b/test/SMatrix.jl index 01dcc652c..fd5f9c597 100644 --- a/test/SMatrix.jl +++ b/test/SMatrix.jl @@ -17,6 +17,8 @@ end @testset "Outer constructors and macro" begin + @test_throws Exception SMatrix(1,2,3,4) # unknown constructor + @test SMatrix{1,1,Int}((1,)).data === (1,) @test SMatrix{1,1}((1,)).data === (1,) @test SMatrix{1}((1,)).data === (1,) diff --git a/test/Scalar.jl b/test/Scalar.jl index e50aba33e..55c72e83d 100644 --- a/test/Scalar.jl +++ b/test/Scalar.jl @@ -11,4 +11,10 @@ @test Scalar(a)[] == 2 s = Scalar(a) @test convert(typeof(s), s) === s + @test Scalar(SVector(1,2,3))[] === SVector(1,2,3) + @test Scalar(MArray{Tuple{}}(1)) === Scalar(1) +end + +@testset "issue #809" begin + @test_throws DimensionMismatch Scalar(1, 2) end diff --git a/test/SizedArray.jl b/test/SizedArray.jl index 2ae8a5f61..845867661 100644 --- a/test/SizedArray.jl +++ b/test/SizedArray.jl @@ -19,6 +19,7 @@ end @testset "Outer Constructors" begin + @test_throws DimensionMismatch SizedArray([3, 4]) @test SizedArray{Tuple{2},Int,1}([3, 4]).data == [3, 4] @test SizedArray{Tuple{2},Int,1,1}([3, 4]).data == [3, 4] @@ -30,7 +31,7 @@ @test @inferred(SizedArray{Tuple{2,2}}([1 2;3 4]))::SizedArray{Tuple{2,2},Int,2,2} == [1 2; 3 4] # From Array, reshaped @test @inferred(SizedArray{Tuple{2,2}}([1,2,3,4]))::SizedArray{Tuple{2,2},Int,2,1} == [1 3; 2 4] - @test_throws DimensionMismatch SizedArray{Tuple{4}}([1 2; 3 4]) + @test_throws DimensionMismatch SizedArray{Tuple{1,4}}([1 2; 3 4]) # Uninitialized @test @inferred(SizedArray{Tuple{2,2},Int,2,2,Matrix{Int}}(undef)) isa SizedArray{Tuple{2,2},Int,2,2,Matrix{Int}} @test @inferred(SizedArray{Tuple{2,2},Int,2,2}(undef)) isa SizedArray{Tuple{2,2},Int,2,2,Matrix{Int}} @@ -252,3 +253,21 @@ struct OVector <: AbstractVector{Int} end Base.length(::OVector) = 10 Base.axes(::OVector) = (0:9,) @test_throws ArgumentError SizedVector{10}(OVector()) + +@testset "some special case" begin + @test_throws Exception SizedVector{1}(1, 2) + @test (@inferred(SizedVector{1}((1, 2)))::SizedVector{1,NTuple{2,Int}}) == [(1, 2)] + @test (@inferred(SizedVector{2}((1, 2)))::SizedVector{2,Int}) == [1, 2] + @test (@inferred(SizedVector(1, 2))::SizedVector{2,Int}) == [1, 2] + @test (@inferred(SizedVector((1, 2)))::SizedVector{2,Int}) == [1, 2] + + @test_throws Exception SizedMatrix{1,1}(1, 2) + @test (@inferred(SizedMatrix{1,1}((1, 2)))::SizedMatrix{1,1,NTuple{2,Int}}) == fill((1, 2),1,1) + @test (@inferred(SizedMatrix{1,2}((1, 2)))::SizedMatrix{1,2,Int}) == reshape(1:2, 1, 2) + @test (@inferred(SizedMatrix{1}((1, 2)))::SizedMatrix{1,2,Int}) == reshape(1:2, 1, 2) + @test (@inferred(SizedMatrix{1}(1, 2))::SizedMatrix{1,2,Int}) == reshape(1:2, 1, 2) + @test (@inferred(SizedMatrix{2}((1, 2)))::SizedMatrix{2,1,Int}) == reshape(1:2, 2, 1) + @test (@inferred(SizedMatrix{2}(1, 2))::SizedMatrix{2,1,Int}) == reshape(1:2, 2, 1) + + +end diff --git a/test/ambiguities.jl b/test/ambiguities.jl index 66b13970a..6cc4708a8 100644 --- a/test/ambiguities.jl +++ b/test/ambiguities.jl @@ -1,7 +1,7 @@ # Allow no new ambiguities (see #18), unless you fix some old ones first! -const allowable_ambiguities = VERSION ≥ v"1.7" ? 60 : - VERSION ≥ v"1.6" ? 61 : error("version must be ≥1.6") +const allowable_ambiguities = VERSION ≥ v"1.7" ? 10 : + VERSION ≥ v"1.6" ? 11 : error("version must be ≥1.6") # TODO: Revisit and fix. See # https://github.com/JuliaLang/julia/pull/36962 diff --git a/test/convert.jl b/test/convert.jl index 83acc415e..63487d1ba 100644 --- a/test/convert.jl +++ b/test/convert.jl @@ -41,4 +41,22 @@ end end mInt = SA[Int16(1) Int16(2) Int16(3); Int16(4) Int16(5) Int16(6)] # SMatrix{3,2,Int16} @test float(typeof(mInt)) === SMatrix{2, 3, float(Int16), 6} -end \ No newline at end of file +end + +struct BugSArray{S<:Tuple,T,N,L} <: StaticArray{S,T,N} + data::NTuple{L,T} + BugSArray{S,T,N,L}(x::NTuple{L,Any}) where {S<:Tuple,T,N,L} = new{S,T,N,L}(map(T,x)) +end +(::Type{BS})(x::Tuple) where {BS<:BugSArray} = StaticArrays.construct_type(BS, x)(x) +BugSVector{N,T,L} = BugSArray{Tuple{N},T,1,L} +@testset "missing constructor" begin + @test_throws DimensionMismatch BugSArray(1,2,3) + @test_throws ErrorException BugSVector(1,2,3) # we catch a missing constructor here. + @test BugSVector{<:Any,<:Any,3}(1,2,3) isa BugSVector{3,Int,3} +end + +@testset "convert with missing/wrong size" begin + @test convert(SVector, MVector(1,2,3)) === SVector(1,2,3) + @test convert(SMatrix{3}, MVector(1,2,3)) === SMatrix{3,1}(1,2,3) + @test_throws Exception convert(SVector{1}, MVector(1,2,3)) +end