diff --git a/NEWS.md b/NEWS.md index b03a933f33eee..a899202425e93 100644 --- a/NEWS.md +++ b/NEWS.md @@ -224,6 +224,13 @@ Compiler/Runtime improvements Deprecated or removed --------------------- + * Linear indexing is now only supported when there is exactly one + non-cartesian index provided. Allowing a trailing index at dimension `d` to + linearly access the higher dimensions from array `A` (beyond `size(A, d)`) + has been deprecated as a stricter constraint during bounds checking. + Instead, `reshape` the array such that its dimensionality matches the + number of indices ([#20079]). + * `isdefined(a::Array, i::Int)` has been deprecated in favor of `isassigned` ([#18346]). * `is` has been deprecated in favor of `===` (which used to be an alias for `is`) ([#17758]). diff --git a/base/abstractarray.jl b/base/abstractarray.jl index ca4e20115094f..fbdf2f51133a0 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -120,10 +120,10 @@ julia> length(A) 60 ``` """ -length(t::AbstractArray) = prod(size(t)) -_length(A::AbstractArray) = prod(map(unsafe_length, indices(A))) # circumvent missing size -_length(A) = length(A) -endof(a::AbstractArray) = length(a) +length(t::AbstractArray) = (@_inline_meta; prod(size(t))) +_length(A::AbstractArray) = (@_inline_meta; prod(map(unsafe_length, indices(A)))) # circumvent missing size +_length(A) = (@_inline_meta; length(A)) +endof(a::AbstractArray) = (@_inline_meta; length(a)) first(a::AbstractArray) = a[first(eachindex(a))] """ @@ -306,6 +306,11 @@ function checkbounds(::Type{Bool}, A::AbstractArray, I...) @_inline_meta checkbounds_indices(Bool, indices(A), I) end +# Linear indexing is explicitly allowed when there is only one (non-cartesian) index +function checkbounds(::Type{Bool}, A::AbstractArray, i) + @_inline_meta + checkindex(Bool, linearindices(A), i) +end # As a special extension, allow using logical arrays that match the source array exactly function checkbounds{_,N}(::Type{Bool}, A::AbstractArray{_,N}, I::AbstractArray{Bool,N}) @_inline_meta @@ -358,7 +363,21 @@ function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{Any}) end function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{Any}) @_inline_meta - checkindex(Bool, OneTo(trailingsize(IA)), I[1]) # linear indexing + checkbounds_linear_indices(Bool, IA, I[1]) +end +function checkbounds_linear_indices(::Type{Bool}, IA::Tuple, i) + @_inline_meta + if checkindex(Bool, IA[1], i) + return true + elseif checkindex(Bool, OneTo(trailingsize(IA)), i) # partial linear indexing + partial_linear_indexing_warning_lookup(length(IA)) + return true # TODO: Return false after the above function is removed in deprecated.jl + end + return false +end +function checkbounds_linear_indices(::Type{Bool}, IA::Tuple, i::Union{Slice,Colon}) + partial_linear_indexing_warning_lookup(length(IA)) + true end checkbounds_indices(::Type{Bool}, ::Tuple, ::Tuple{}) = true diff --git a/base/deprecated.jl b/base/deprecated.jl index 7b78e1ada0278..78eed7327b74c 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -59,40 +59,40 @@ end function depwarn(msg, funcsym) opts = JLOptions() if opts.depwarn > 0 - ln = Int(unsafe_load(cglobal(:jl_lineno, Cint))) - fn = unsafe_string(unsafe_load(cglobal(:jl_filename, Ptr{Cchar}))) bt = backtrace() - caller = firstcaller(bt, funcsym) - if opts.depwarn == 1 # raise a warning - warn(msg, once=(caller != C_NULL), key=caller, bt=bt, - filename=fn, lineno=ln) - elseif opts.depwarn == 2 # raise an error - throw(ErrorException(msg)) - end + _depwarn(msg, opts, bt, firstcaller(bt, funcsym)) end nothing end +function _depwarn(msg, opts, bt, caller) + ln = Int(unsafe_load(cglobal(:jl_lineno, Cint))) + fn = unsafe_string(unsafe_load(cglobal(:jl_filename, Ptr{Cchar}))) + if opts.depwarn == 1 # raise a warning + warn(msg, once=(caller != StackTraces.UNKNOWN), key=(caller,fn,ln), bt=bt, + filename=fn, lineno=ln) + elseif opts.depwarn == 2 # raise an error + throw(ErrorException(msg)) + end +end -function firstcaller(bt::Array{Ptr{Void},1}, funcsym::Symbol) +firstcaller(bt::Array{Ptr{Void},1}, funcsym::Symbol) = firstcaller(bt, (funcsym,)) +function firstcaller(bt::Array{Ptr{Void},1}, funcsyms) # Identify the calling line - i = 1 - while i <= length(bt) - lkups = StackTraces.lookup(bt[i]) - i += 1 + found = false + lkup = StackTraces.UNKNOWN + for frame in bt + lkups = StackTraces.lookup(frame) for lkup in lkups if lkup === StackTraces.UNKNOWN continue end - if lkup.func == funcsym - @goto found - end + found && @goto found + found = lkup.func in funcsyms end end + return StackTraces.UNKNOWN @label found - if i <= length(bt) - return bt[i] - end - return C_NULL + return lkup end deprecate(s::Symbol) = deprecate(current_module(), s) @@ -1739,6 +1739,46 @@ eval(Base.Test, quote export @test_approx_eq end) +# Deprecate partial linear indexing +function partial_linear_indexing_warning_lookup(nidxs_remaining) + # We need to figure out how many indices were passed for a sensible deprecation warning + opts = JLOptions() + if opts.depwarn > 0 + # Find the caller -- this is very expensive so we don't want to do it twice + bt = backtrace() + found = false + call = StackTraces.UNKNOWN + caller = StackTraces.UNKNOWN + for frame in bt + lkups = StackTraces.lookup(frame) + for caller in lkups + if caller === StackTraces.UNKNOWN + continue + end + found && @goto found + if caller.func in (:getindex, :setindex!, :view) + found = true + call = caller + end + end + end + @label found + fn = "`reshape`" + if call != StackTraces.UNKNOWN && !isnull(call.linfo) + # Try to grab the number of dimensions in the parent array + mi = get(call.linfo) + args = mi.specTypes.parameters + if length(args) >= 2 && args[2] <: AbstractArray + fn = "`reshape(A, Val{$(ndims(args[2]) - nidxs_remaining + 1)})`" + end + end + _depwarn("Partial linear indexing is deprecated. Use $fn to make the dimensionality of the array match the number of indices.", opts, bt, caller) + end +end +function partial_linear_indexing_warning(n) + depwarn("Partial linear indexing is deprecated. Use `reshape(A, Val{$n})` to make the dimensionality of the array match the number of indices.", (:getindex, :setindex!, :view)) +end + # Deprecate Array(T, dims...) in favor of proper type constructors @deprecate Array{T,N}(::Type{T}, d::NTuple{N,Int}) Array{T,N}(d) @deprecate Array{T}(::Type{T}, d::Int...) Array{T,length(d)}(d...) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 4465e3435d00c..ffe1b10d7a7af 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -181,6 +181,12 @@ end # IteratorsMD using .IteratorsMD ## Bounds-checking with CartesianIndex +# Disallow linear indexing with CartesianIndex +function checkbounds(::Type{Bool}, A::AbstractArray, i::Union{CartesianIndex, AbstractArray{C} where C <: CartesianIndex}) + @_inline_meta + checkbounds_indices(Bool, indices(A), (i,)) +end + @inline checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) = checkbounds_indices(Bool, (), (I[1].I..., tail(I)...)) @inline checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) = diff --git a/src/array.c b/src/array.c index 55063aa23ceb4..109d79d6c3be0 100644 --- a/src/array.c +++ b/src/array.c @@ -530,7 +530,7 @@ int jl_array_isdefined(jl_value_t **args0, int nargs) { assert(jl_is_array(args0[0])); jl_depwarn("`isdefined(a::Array, i::Int)` is deprecated, " - "use `isassigned(a, i)` instead", jl_symbol("isdefined")); + "use `isassigned(a, i)` instead", (jl_value_t*)jl_symbol("isdefined")); jl_array_t *a = (jl_array_t*)args0[0]; jl_value_t **args = &args0[1]; diff --git a/src/builtins.c b/src/builtins.c index d1a9acd6d4e27..3a31bfafebaa6 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1045,7 +1045,7 @@ JL_CALLABLE(jl_f_invoke) if (jl_is_tuple(args[1])) { jl_depwarn("`invoke(f, (types...), ...)` is deprecated, " "use `invoke(f, Tuple{types...}, ...)` instead", - jl_symbol("invoke")); + (jl_value_t*)jl_symbol("invoke")); argtypes = (jl_value_t*)jl_apply_tuple_type_v((jl_value_t**)jl_data_ptr(argtypes), jl_nfields(argtypes)); } @@ -1735,7 +1735,7 @@ JL_DLLEXPORT void jl_breakpoint(jl_value_t *v) // put a breakpoint in your debugger here } -void jl_depwarn(const char *msg, jl_sym_t *sym) +void jl_depwarn(const char *msg, jl_value_t *sym) { static jl_value_t *depwarn_func = NULL; if (!depwarn_func && jl_base_module) { @@ -1749,11 +1749,31 @@ void jl_depwarn(const char *msg, jl_sym_t *sym) JL_GC_PUSHARGS(depwarn_args, 3); depwarn_args[0] = depwarn_func; depwarn_args[1] = jl_cstr_to_string(msg); - depwarn_args[2] = (jl_value_t*)sym; + depwarn_args[2] = sym; jl_apply(depwarn_args, 3); JL_GC_POP(); } +void jl_depwarn_partial_indexing(size_t n) +{ + static jl_value_t *depwarn_func = NULL; + if (!depwarn_func && jl_base_module) { + depwarn_func = jl_get_global(jl_base_module, jl_symbol("partial_linear_indexing_warning")); + } + if (!depwarn_func) { + jl_safe_printf("WARNING: Partial linear indexing is deprecated. Use " + "`reshape(A, Val{%zd})` to make the dimensionality of the array match " + "the number of indices\n", n); + return; + } + jl_value_t **depwarn_args; + JL_GC_PUSHARGS(depwarn_args, 2); + depwarn_args[0] = depwarn_func; + depwarn_args[1] = jl_box_long(n); + jl_apply(depwarn_args, 2); + JL_GC_POP(); +} + #ifdef __cplusplus } #endif diff --git a/src/cgutils.cpp b/src/cgutils.cpp index af7f0efc56091..c91fecaa02524 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1396,8 +1396,30 @@ static Value *emit_array_nd_index(const jl_cgval_t &ainfo, jl_value_t *ex, ssize if (linear_indexing) { // Compare the linearized index `i` against the linearized size of // the accessed array, i.e. `if !(i < alen) goto error`. - Value *alen = emit_arraylen(ainfo, ex, ctx); - builder.CreateCondBr(builder.CreateICmpULT(i, alen), endBB, failBB); + if (nidxs > 1) { + // TODO: REMOVE DEPWARN AND RETURN FALSE AFTER 0.6. + // We need to check if this is inside the non-linearized size + BasicBlock *partidx = BasicBlock::Create(jl_LLVMContext, "partlinidx"); + BasicBlock *partidxwarn = BasicBlock::Create(jl_LLVMContext, "partlinidxwarn"); + Value *d = emit_arraysize_for_unsafe_dim(ainfo, ex, nidxs, nd, ctx); + builder.CreateCondBr(builder.CreateICmpULT(ii, d), endBB, partidx); + + // We failed the normal bounds check; check to see if we're + // inside the linearized size (partial linear indexing): + ctx->f->getBasicBlockList().push_back(partidx); + builder.SetInsertPoint(partidx); + Value *alen = emit_arraylen(ainfo, ex, ctx); + builder.CreateCondBr(builder.CreateICmpULT(i, alen), partidxwarn, failBB); + + // We passed the linearized bounds check; now throw the depwarn: + ctx->f->getBasicBlockList().push_back(partidxwarn); + builder.SetInsertPoint(partidxwarn); + builder.CreateCall(prepare_call(jldepwarnpi_func), ConstantInt::get(T_size, nidxs)); + builder.CreateBr(endBB); + } else { + Value *alen = emit_arraylen(ainfo, ex, ctx); + builder.CreateCondBr(builder.CreateICmpULT(i, alen), endBB, failBB); + } } else { // Compare the last index of the access against the last dimension of // the accessed array, i.e. `if !(last_index < last_dimension) goto error`. diff --git a/src/codegen.cpp b/src/codegen.cpp index 55eba653380e1..76d8bdd511f38 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -394,6 +394,7 @@ static Function *expect_func; static Function *jldlsym_func; static Function *jlnewbits_func; static Function *jltypeassert_func; +static Function *jldepwarnpi_func; #if JL_LLVM_VERSION < 30600 static Function *jlpow_func; static Function *jlpowf_func; @@ -5777,6 +5778,13 @@ static void init_julia_llvm_env(Module *m) "jl_typeassert", m); add_named_global(jltypeassert_func, &jl_typeassert); + std::vector argsdepwarnpi(0); + argsdepwarnpi.push_back(T_size); + jldepwarnpi_func = Function::Create(FunctionType::get(T_void, argsdepwarnpi, false), + Function::ExternalLinkage, + "jl_depwarn_partial_indexing", m); + add_named_global(jldepwarnpi_func, &jl_depwarn_partial_indexing); + queuerootfun = Function::Create(FunctionType::get(T_void, args_1ptr, false), Function::ExternalLinkage, "jl_gc_queue_root", m); diff --git a/src/julia_internal.h b/src/julia_internal.h index 60b931afedddc..e0e2c8282799b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -817,7 +817,8 @@ STATIC_INLINE void *jl_get_frame_addr(void) } JL_DLLEXPORT jl_array_t *jl_array_cconvert_cstring(jl_array_t *a); -void jl_depwarn(const char *msg, jl_sym_t *sym); +void jl_depwarn(const char *msg, jl_value_t *sym); +void jl_depwarn_partial_indexing(size_t n); int isabspath(const char *in); diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 299c2a66e82b2..9cf12eca837c6 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -16,10 +16,10 @@ A = rand(5,4,3) @test checkbounds(Bool, A, 2, 2, 2, 1) == true # extra indices @test checkbounds(Bool, A, 2, 2, 2, 2) == false @test checkbounds(Bool, A, 1, 1) == true # partial linear indexing (PLI) -@test checkbounds(Bool, A, 1, 12) == true # PLI -@test checkbounds(Bool, A, 5, 12) == true # PLI +# @test checkbounds(Bool, A, 1, 12) == false # PLI TODO: Re-enable after partial linear indexing deprecation +# @test checkbounds(Bool, A, 5, 12) == false # PLI TODO: Re-enable after partial linear indexing deprecation @test checkbounds(Bool, A, 1, 13) == false # PLI -@test checkbounds(Bool, A, 6, 12) == false # PLI +# @test checkbounds(Bool, A, 6, 12) == false # PLI TODO: Re-enable after partial linear indexing deprecation # single CartesianIndex @test checkbounds(Bool, A, CartesianIndex((1, 1, 1))) == true @@ -31,15 +31,15 @@ A = rand(5,4,3) @test checkbounds(Bool, A, CartesianIndex((5, 5, 3))) == false @test checkbounds(Bool, A, CartesianIndex((5, 4, 4))) == false @test checkbounds(Bool, A, CartesianIndex((1,))) == true -@test checkbounds(Bool, A, CartesianIndex((60,))) == true +# @test checkbounds(Bool, A, CartesianIndex((60,))) == false # TODO: Re-enable after partial linear indexing deprecation @test checkbounds(Bool, A, CartesianIndex((61,))) == false @test checkbounds(Bool, A, CartesianIndex((2, 2, 2, 1,))) == true @test checkbounds(Bool, A, CartesianIndex((2, 2, 2, 2,))) == false @test checkbounds(Bool, A, CartesianIndex((1, 1,))) == true -@test checkbounds(Bool, A, CartesianIndex((1, 12,))) == true -@test checkbounds(Bool, A, CartesianIndex((5, 12,))) == true +# @test checkbounds(Bool, A, CartesianIndex((1, 12,))) == false # TODO: Re-enable after partial linear indexing deprecation +# @test checkbounds(Bool, A, CartesianIndex((5, 12,))) == false # TODO: Re-enable after partial linear indexing deprecation @test checkbounds(Bool, A, CartesianIndex((1, 13,))) == false -@test checkbounds(Bool, A, CartesianIndex((6, 12,))) == false +# @test checkbounds(Bool, A, CartesianIndex((6, 12,))) == false # TODO: Re-enable after partial linear indexing deprecation # mix of CartesianIndex and Int @test checkbounds(Bool, A, CartesianIndex((1,)), 1, CartesianIndex((1,))) == true @@ -63,9 +63,10 @@ A = rand(5,4,3) @test checkbounds(Bool, A, 1:61) == false @test checkbounds(Bool, A, 2, 2, 2, 1:1) == true # extra indices @test checkbounds(Bool, A, 2, 2, 2, 1:2) == false -@test checkbounds(Bool, A, 1:5, 1:12) == true +@test checkbounds(Bool, A, 1:5, 1:4) == true +# @test checkbounds(Bool, A, 1:5, 1:12) == false # TODO: Re-enable after partial linear indexing deprecation @test checkbounds(Bool, A, 1:5, 1:13) == false -@test checkbounds(Bool, A, 1:6, 1:12) == false +# @test checkbounds(Bool, A, 1:6, 1:12) == false # TODO: Re-enable after partial linear indexing deprecation # logical @test checkbounds(Bool, A, trues(5), trues(4), trues(3)) == true @@ -76,9 +77,9 @@ A = rand(5,4,3) @test checkbounds(Bool, A, trues(61)) == false @test checkbounds(Bool, A, 2, 2, 2, trues(1)) == true # extra indices @test checkbounds(Bool, A, 2, 2, 2, trues(2)) == false -@test checkbounds(Bool, A, trues(5), trues(12)) == true +# @test checkbounds(Bool, A, trues(5), trues(12)) == false # TODO: Re-enable after partial linear indexing deprecation @test checkbounds(Bool, A, trues(5), trues(13)) == false -@test checkbounds(Bool, A, trues(6), trues(12)) == false +# @test checkbounds(Bool, A, trues(6), trues(12)) == false # TODO: Re-enable after partial linear indexing deprecation @test checkbounds(Bool, A, trues(5, 4, 3)) == true @test checkbounds(Bool, A, trues(5, 4, 2)) == false @test checkbounds(Bool, A, trues(5, 12)) == false @@ -201,6 +202,7 @@ end TSlow{T}(::Type{T}, dims::Int...) = TSlow(T, dims) TSlow{T,N}(::Type{T}, dims::NTuple{N,Int}) = TSlow{T,N}(Dict{NTuple{N,Int}, T}(), dims) +Base.convert{T,N }(::Type{TSlow{T,N}}, X::TSlow{T,N}) = X Base.convert{T,N }(::Type{TSlow }, X::AbstractArray{T,N}) = convert(TSlow{T,N}, X) Base.convert{T,N,_}(::Type{TSlow{T }}, X::AbstractArray{_,N}) = convert(TSlow{T,N}, X) Base.convert{T,N }(::Type{TSlow{T,N}}, X::AbstractArray ) = begin @@ -236,7 +238,6 @@ Base.setindex!{T}(A::TSlow{T,4}, v, i1::Int, i2::Int, i3::Int, i4::Int) = Base.setindex!{T}(A::TSlow{T,5}, v, i1::Int, i2::Int, i3::Int, i4::Int, i5::Int) = (A.data[(i1,i2,i3,i4,i5)] = v) -import Base: trailingsize const can_inline = Base.JLOptions().can_inline != 0 function test_scalar_indexing{T}(::Type{T}, shape, ::Type{TestAbstractArray}) N = prod(shape) @@ -245,7 +246,7 @@ function test_scalar_indexing{T}(::Type{T}, shape, ::Type{TestAbstractArray}) @test A == B # Test indexing up to 5 dimensions i=0 - for i5 = 1:trailingsize(B, 5) + for i5 = 1:size(B, 5) for i4 = 1:size(B, 4) for i3 = 1:size(B, 3) for i2 = 1:size(B, 2) @@ -266,7 +267,7 @@ function test_scalar_indexing{T}(::Type{T}, shape, ::Type{TestAbstractArray}) @test A[i1] == B[i1] == i end i=0 - for i2 = 1:trailingsize(B, 2) + for i2 = 1:size(B, 2) for i1 = 1:size(B, 1) i += 1 @test A[i1,i2] == B[i1,i2] == i @@ -274,7 +275,7 @@ function test_scalar_indexing{T}(::Type{T}, shape, ::Type{TestAbstractArray}) end @test A == B i=0 - for i3 = 1:trailingsize(B, 3) + for i3 = 1:size(B, 3) for i2 = 1:size(B, 2) for i1 = 1:size(B, 1) i += 1 @@ -290,7 +291,7 @@ function test_scalar_indexing{T}(::Type{T}, shape, ::Type{TestAbstractArray}) D2 = T(Int, shape) D3 = T(Int, shape) i=0 - for i5 = 1:trailingsize(B, 5) + for i5 = 1:size(B, 5) for i4 = 1:size(B, 4) for i3 = 1:size(B, 3) for i2 = 1:size(B, 2) @@ -322,20 +323,22 @@ function test_scalar_indexing{T}(::Type{T}, shape, ::Type{TestAbstractArray}) @test C == B == A C = T(Int, shape) i=0 - for i2 = 1:trailingsize(C, 2) - for i1 = 1:size(C, 1) + C2 = reshape(C, Val{2}) + for i2 = 1:size(C2, 2) + for i1 = 1:size(C2, 1) i += 1 - C[i1,i2] = i + C2[i1,i2] = i end end @test C == B == A C = T(Int, shape) i=0 - for i3 = 1:trailingsize(C, 3) - for i2 = 1:size(C, 2) - for i1 = 1:size(C, 1) + C3 = reshape(C, Val{3}) + for i3 = 1:size(C3, 3) + for i2 = 1:size(C3, 2) + for i1 = 1:size(C3, 1) i += 1 - C[i1,i2,i3] = i + C3[i1,i2,i3] = i end end end @@ -355,29 +358,29 @@ function test_vector_indexing{T}(::Type{T}, shape, ::Type{TestAbstractArray}) @test B[vec(idxs)] == A[vec(idxs)] == vec(idxs) @test B[:] == A[:] == collect(1:N) @test B[1:end] == A[1:end] == collect(1:N) - @test B[:,:] == A[:,:] == reshape(1:N, shape[1], prod(shape[2:end])) - @test B[1:end,1:end] == A[1:end,1:end] == reshape(1:N, shape[1], prod(shape[2:end])) + # @test B[:,:] == A[:,:] == reshape(1:N, shape[1], prod(shape[2:end])) # TODO: Re-enable after partial linear indexing deprecation + # @test B[1:end,1:end] == A[1:end,1:end] == reshape(1:N, shape[1], prod(shape[2:end])) # TODO: Re-enable after partial linear indexing deprecation # Test with containers that aren't Int[] @test B[[]] == A[[]] == [] @test B[convert(Array{Any}, idxs)] == A[convert(Array{Any}, idxs)] == idxs # Test adding dimensions with matrices idx1 = rand(1:size(A, 1), 3) - idx2 = rand(1:Base.trailingsize(A, 2), 4, 5) + idx2 = rand(1:size(A, 2), 4, 5) @test B[idx1, idx2] == A[idx1, idx2] == reshape(A[idx1, vec(idx2)], 3, 4, 5) == reshape(B[idx1, vec(idx2)], 3, 4, 5) @test B[1, idx2] == A[1, idx2] == reshape(A[1, vec(idx2)], 4, 5) == reshape(B[1, vec(idx2)], 4, 5) # test removing dimensions with 0-d arrays idx0 = reshape([rand(1:size(A, 1))]) @test B[idx0, idx2] == A[idx0, idx2] == reshape(A[idx0[], vec(idx2)], 4, 5) == reshape(B[idx0[], vec(idx2)], 4, 5) - @test B[reshape([end]), reshape([end])] == A[reshape([end]), reshape([end])] == reshape([A[end,end]]) == reshape([B[end,end]]) + # @test B[reshape([end]), reshape([end])] == A[reshape([end]), reshape([end])] == reshape([A[end,end]]) == reshape([B[end,end]]) # TODO: Re-enable after partial linear indexing deprecation # test logical indexing mask = bitrand(shape) @test B[mask] == A[mask] == B[find(mask)] == A[find(mask)] == find(mask) @test B[vec(mask)] == A[vec(mask)] == find(mask) mask1 = bitrand(size(A, 1)) - mask2 = bitrand(Base.trailingsize(A, 2)) + mask2 = bitrand(size(A, 2)) @test B[mask1, mask2] == A[mask1, mask2] == B[find(mask1), find(mask2)] @test B[mask1, 1] == A[mask1, 1] == find(mask1) end diff --git a/test/arrayops.jl b/test/arrayops.jl index 691dc7de3f160..a93825ead24b7 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -221,7 +221,7 @@ end @test A[2:6] == [2:6;] @test A[1:3,2,2:4] == cat(2,46:48,86:88,126:128) @test A[:,7:-3:1,5] == [191 176 161; 192 177 162; 193 178 163; 194 179 164; 195 180 165] - @test A[:,3:9] == reshape(11:45,5,7) + @test reshape(A, Val{2})[:,3:9] == reshape(11:45,5,7) rng = (2,2:3,2:2:5) tmp = zeros(Int,map(maximum,rng)...) tmp[rng...] = A[rng...] @@ -255,7 +255,7 @@ end B[4,[2,3]] = 7 @test B == [0 23 1 24 0; 11 12 13 14 15; 0 21 3 22 0; 0 7 7 0 0] - @test isequal(reshape(1:27, 3, 3, 3)[1,:], [1, 4, 7, 10, 13, 16, 19, 22, 25]) + @test isequal(reshape(reshape(1:27, 3, 3, 3), Val{2})[1,:], [1, 4, 7, 10, 13, 16, 19, 22, 25]) a = [3, 5, -7, 6] b = [4, 6, 2, -7, 1] @@ -1360,11 +1360,13 @@ end @test a[:, [CartesianIndex()], :, :] == (@view a[:, [CartesianIndex()], :, :]) == reshape(a, 3, 1, 4, 5) @test a[:, :, [CartesianIndex()], :] == (@view a[:, :, [CartesianIndex()], :]) == reshape(a, 3, 4, 1, 5) @test a[:, :, :, [CartesianIndex()]] == (@view a[:, :, :, [CartesianIndex()]]) == reshape(a, 3, 4, 5, 1) - @test a[[CartesianIndex()], :, :] == (@view a[[CartesianIndex()], :, :]) == reshape(a, 1, 3, 20) - @test a[:, [CartesianIndex()], :] == (@view a[:, [CartesianIndex()], :]) == reshape(a, 3, 1, 20) - @test a[:, :, [CartesianIndex()]] == (@view a[:, :, [CartesianIndex()]]) == reshape(a, 3, 20, 1) - @test a[[CartesianIndex()], :] == (@view a[[CartesianIndex()], :]) == reshape(a, 1, 60) - @test a[:, [CartesianIndex()]] == (@view a[:, [CartesianIndex()]]) == reshape(a, 60, 1) + a2 = reshape(a, Val{2}) + @test a2[[CartesianIndex()], :, :] == (@view a2[[CartesianIndex()], :, :]) == reshape(a, 1, 3, 20) + @test a2[:, [CartesianIndex()], :] == (@view a2[:, [CartesianIndex()], :]) == reshape(a, 3, 1, 20) + @test a2[:, :, [CartesianIndex()]] == (@view a2[:, :, [CartesianIndex()]]) == reshape(a, 3, 20, 1) + a1 = reshape(a, Val{1}) + @test a1[[CartesianIndex()], :] == (@view a1[[CartesianIndex()], :]) == reshape(a, 1, 60) + @test a1[:, [CartesianIndex()]] == (@view a1[:, [CartesianIndex()]]) == reshape(a, 60, 1) @test_throws BoundsError a[[CartesianIndex(1,5),CartesianIndex(2,4)],3:3] @test_throws BoundsError a[1:4, [CartesianIndex(1,3),CartesianIndex(2,4)]] diff --git a/test/subarray.jl b/test/subarray.jl index 0d5d3cb81e032..3791d38e8c01f 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -46,6 +46,9 @@ ensure_iterable(::Tuple{}) = () ensure_iterable(t::Tuple{Union{Number, CartesianIndex}, Vararg{Any}}) = ((t[1],), ensure_iterable(Base.tail(t))...) ensure_iterable(t::Tuple{Any, Vararg{Any}}) = (t[1], ensure_iterable(Base.tail(t))...) +index_ndims(t::Tuple) = tup2val(Base.index_ndims(t)) +tup2val{N}(::NTuple{N}) = Val{N} + # To avoid getting confused by manipulations that are implemented for SubArrays, # it's good to copy the contents to an Array. This version protects against # `similar` ever changing its meaning. @@ -127,9 +130,8 @@ test_mixed{T}(::AbstractArray{T,1}, ::Array) = nothing test_mixed{T}(::AbstractArray{T,2}, ::Array) = nothing test_mixed(A, B::Array) = _test_mixed(A, reshape(B, size(A))) function _test_mixed(A::ANY, B::ANY) - L = length(A) m = size(A, 1) - n = div(L, m) + n = size(A, 2) isgood = true for j = 1:n, i = 1:m if A[i,j] != B[i,j] @@ -224,9 +226,11 @@ end function runviews(SB::AbstractArray, indexN, indexNN, indexNNN) @assert ndims(SB) > 2 for i3 in indexN, i2 in indexN, i1 in indexN + ndims(SB) > 3 && i3 isa Colon && continue # TODO: Re-enable once Colon no longer spans partial trailing dimensions runsubarraytests(SB, i1, i2, i3) end - for i2 in indexNN, i1 in indexN + for i2 in indexN, i1 in indexN + i2 isa Colon && continue # TODO: Re-enable once Colon no longer spans partial trailing dimensions runsubarraytests(SB, i1, i2) end for i1 in indexNNN @@ -272,9 +276,15 @@ end # with the exception of Int-slicing oindex = (:, 6, 3:7, reshape([12]), [8,4,6,12,5,7], [3:7 1:5 2:6 4:8 5:9]) +_ndims{T,N}(::AbstractArray{T,N}) = N +_ndims(x) = 1 + if testfull let B = copy(reshape(1:13^3, 13, 13, 13)) for o3 in oindex, o2 in oindex, o1 in oindex + if (o3 isa Colon && (_ndims(o1) + _ndims(o2) != 2)) + continue # TODO: remove once Colon no longer spans partial trailing dimensions + end viewB = view(B, o1, o2, o3) runviews(viewB, index5, index25, index125) end @@ -298,8 +308,7 @@ if !testfull (CartesianIndex(13,6),[8,4,6,12,5,7]), (1,:,view(1:13,[9,12,4,13,1])), (view(1:13,[9,12,4,13,1]),2:6,4), - ([1:5 2:6 3:7 4:8 5:9], :, 3), - (:, [46:-1:42 88:-1:84 22:-1:18 49:-1:45 8:-1:4])) + ([1:5 2:6 3:7 4:8 5:9], :, 3)) runsubarraytests(B, oind...) viewB = view(B, oind...) runviews(viewB, index5, index25, index125) @@ -475,7 +484,7 @@ Y = 4:-1:1 @test X[1:end] == @view X[1:end] @test X[1:end-3] == @view X[1:end-3] @test X[1:end,2,2] == @view X[1:end,2,2] -@test X[1,1:end-2] == @view X[1,1:end-2] +# @test X[1,1:end-2] == @view X[1,1:end-2] # TODO: Re-enable after partial linear indexing deprecation @test X[1,2,1:end-2] == @view X[1,2,1:end-2] @test X[1,2,Y[2:end]] == @view X[1,2,Y[2:end]] @test X[1:end,2,Y[2:end]] == @view X[1:end,2,Y[2:end]]