Skip to content

Commit a12179f

Browse files
committed
Deprecate partial linear indexing
1 parent 4045aff commit a12179f

11 files changed

+183
-64
lines changed

base/abstractarray.jl

+20-1
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,11 @@ function checkbounds(::Type{Bool}, A::AbstractArray, I...)
306306
@_inline_meta
307307
checkbounds_indices(Bool, indices(A), I)
308308
end
309+
# Linear indexing is explicitly allowed when there is only one (non-cartesian) index
310+
function checkbounds(::Type{Bool}, A::AbstractArray, i)
311+
@_inline_meta
312+
checkindex(Bool, linearindices(A), i)
313+
end
309314
# As a special extension, allow using logical arrays that match the source array exactly
310315
function checkbounds{_,N}(::Type{Bool}, A::AbstractArray{_,N}, I::AbstractArray{Bool,N})
311316
@_inline_meta
@@ -358,7 +363,21 @@ function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{Any})
358363
end
359364
function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{Any})
360365
@_inline_meta
361-
checkindex(Bool, OneTo(trailingsize(IA)), I[1]) # linear indexing
366+
checkbounds_linear_indices(Bool, IA, I[1])
367+
end
368+
function checkbounds_linear_indices(::Type{Bool}, IA::Tuple, i)
369+
@_inline_meta
370+
if checkindex(Bool, IA[1], i)
371+
return true
372+
elseif checkindex(Bool, OneTo(trailingsize(IA)), i) # partial linear indexing
373+
partial_linear_indexing_warning_lookup(length(IA))
374+
return true # TODO: Return false after the above function is removed in deprecated.jl
375+
end
376+
return false
377+
end
378+
function checkbounds_linear_indices(::Type{Bool}, IA::Tuple, i::Union{Slice,Colon})
379+
partial_linear_indexing_warning_lookup(length(IA))
380+
true
362381
end
363382
checkbounds_indices(::Type{Bool}, ::Tuple, ::Tuple{}) = true
364383

base/deprecated.jl

+58-20
Original file line numberDiff line numberDiff line change
@@ -59,41 +59,39 @@ end
5959
function depwarn(msg, funcsym)
6060
opts = JLOptions()
6161
if opts.depwarn > 0
62-
ln = Int(unsafe_load(cglobal(:jl_lineno, Cint)))
63-
fn = unsafe_string(unsafe_load(cglobal(:jl_filename, Ptr{Cchar})))
6462
bt = backtrace()
65-
caller = firstcaller(bt, funcsym)
66-
if opts.depwarn == 1 # raise a warning
67-
warn(msg, once=(caller != C_NULL), key=caller, bt=bt,
68-
filename=fn, lineno=ln)
69-
elseif opts.depwarn == 2 # raise an error
70-
throw(ErrorException(msg))
71-
end
63+
_depwarn(msg, opts, bt, firstcaller(bt, funcsym))
7264
end
7365
nothing
7466
end
67+
function _depwarn(msg, opts, bt, caller)
68+
ln = Int(unsafe_load(cglobal(:jl_lineno, Cint)))
69+
fn = unsafe_string(unsafe_load(cglobal(:jl_filename, Ptr{Cchar})))
70+
if opts.depwarn == 1 # raise a warning
71+
warn(msg, once=(caller != StackTraces.UNKNOWN), key=caller, bt=bt,
72+
filename=fn, lineno=ln)
73+
elseif opts.depwarn == 2 # raise an error
74+
throw(ErrorException(msg))
75+
end
76+
end
7577

7678
firstcaller(bt::Array{Ptr{Void},1}, funcsym::Symbol) = firstcaller(bt, (funcsym,))
7779
function firstcaller(bt::Array{Ptr{Void},1}, funcsyms)
7880
# Identify the calling line
79-
i = 1
80-
while i <= length(bt)
81-
lkups = StackTraces.lookup(bt[i])
82-
i += 1
81+
found = false
82+
lkup = StackTraces.UNKNOWN
83+
for frame in bt
84+
lkups = StackTraces.lookup(frame)
8385
for lkup in lkups
8486
if lkup === StackTraces.UNKNOWN
8587
continue
8688
end
87-
if lkup.func in funcsyms
88-
@goto found
89-
end
89+
found && @goto found
90+
found = lkup.func in funcsyms
9091
end
9192
end
9293
@label found
93-
if i <= length(bt)
94-
return bt[i]
95-
end
96-
return C_NULL
94+
return lkup
9795
end
9896

9997
deprecate(s::Symbol) = deprecate(current_module(), s)
@@ -1745,6 +1743,46 @@ end
17451743
export @test_approx_eq
17461744
# END code from base/test.jl
17471745

1746+
# Deprecate partial linear indexing
1747+
function partial_linear_indexing_warning_lookup(nidxs_remaining)
1748+
# We need to figure out how many indices were passed for a sensible deprecation warning
1749+
opts = JLOptions()
1750+
if opts.depwarn > 0
1751+
# Find the caller -- this is very expensive so we don't want to do it twice
1752+
bt = backtrace()
1753+
found = false
1754+
call = StackTraces.UNKNOWN
1755+
caller = StackTraces.UNKNOWN
1756+
for frame in bt
1757+
lkups = StackTraces.lookup(frame)
1758+
for caller in lkups
1759+
if caller === StackTraces.UNKNOWN
1760+
continue
1761+
end
1762+
found && @goto found
1763+
if caller.func in (:getindex, :setindex!, :view)
1764+
found = true
1765+
call = caller
1766+
end
1767+
end
1768+
end
1769+
@label found
1770+
fn = "`reshape`"
1771+
if call != StackTraces.UNKNOWN && !isnull(call.linfo)
1772+
# Try to grab the number of dimensions in the parent array
1773+
mi = get(call.linfo)
1774+
args = mi.specTypes.parameters
1775+
if length(args) >= 2 && args[2] <: AbstractArray
1776+
fn = "`reshape(A, Val{$(ndims(args[2]) - nidxs_remaining + 1)})`"
1777+
end
1778+
end
1779+
_depwarn("Partial linear indexing is deprecated. Use $fn to make the dimensionality of the array match the number of indices.", opts, bt, caller)
1780+
end
1781+
end
1782+
function partial_linear_indexing_warning(n)
1783+
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))
1784+
end
1785+
17481786
# Deprecate Array(T, dims...) in favor of proper type constructors
17491787
@deprecate Array{T,N}(::Type{T}, d::NTuple{N,Int}) Array{T,N}(d)
17501788
@deprecate Array{T}(::Type{T}, d::Int...) Array{T,length(d)}(d...)

base/multidimensional.jl

+6
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ end # IteratorsMD
166166
using .IteratorsMD
167167

168168
## Bounds-checking with CartesianIndex
169+
# Disallow linear indexing with CartesianIndex
170+
function checkbounds(::Type{Bool}, A::AbstractArray, i::Union{CartesianIndex, AbstractArray{C} where C <: CartesianIndex})
171+
@_inline_meta
172+
checkbounds_indices(Bool, indices(A), (i,))
173+
end
174+
169175
@inline checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) =
170176
checkbounds_indices(Bool, (), (I[1].I..., tail(I)...))
171177
@inline checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) =

src/array.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ int jl_array_isdefined(jl_value_t **args0, int nargs)
530530
{
531531
assert(jl_is_array(args0[0]));
532532
jl_depwarn("`isdefined(a::Array, i::Int)` is deprecated, "
533-
"use `isassigned(a, i)` instead", jl_symbol("isdefined"));
533+
"use `isassigned(a, i)` instead", (jl_value_t*)jl_symbol("isdefined"));
534534

535535
jl_array_t *a = (jl_array_t*)args0[0];
536536
jl_value_t **args = &args0[1];

src/builtins.c

+23-3
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,7 @@ JL_CALLABLE(jl_f_invoke)
10451045
if (jl_is_tuple(args[1])) {
10461046
jl_depwarn("`invoke(f, (types...), ...)` is deprecated, "
10471047
"use `invoke(f, Tuple{types...}, ...)` instead",
1048-
jl_symbol("invoke"));
1048+
(jl_value_t*)jl_symbol("invoke"));
10491049
argtypes = (jl_value_t*)jl_apply_tuple_type_v((jl_value_t**)jl_data_ptr(argtypes),
10501050
jl_nfields(argtypes));
10511051
}
@@ -1701,7 +1701,7 @@ JL_DLLEXPORT void jl_breakpoint(jl_value_t *v)
17011701
// put a breakpoint in your debugger here
17021702
}
17031703

1704-
void jl_depwarn(const char *msg, jl_sym_t *sym)
1704+
void jl_depwarn(const char *msg, jl_value_t *sym)
17051705
{
17061706
static jl_value_t *depwarn_func = NULL;
17071707
if (!depwarn_func && jl_base_module) {
@@ -1715,11 +1715,31 @@ void jl_depwarn(const char *msg, jl_sym_t *sym)
17151715
JL_GC_PUSHARGS(depwarn_args, 3);
17161716
depwarn_args[0] = depwarn_func;
17171717
depwarn_args[1] = jl_cstr_to_string(msg);
1718-
depwarn_args[2] = (jl_value_t*)sym;
1718+
depwarn_args[2] = sym;
17191719
jl_apply(depwarn_args, 3);
17201720
JL_GC_POP();
17211721
}
17221722

1723+
void jl_depwarn_partial_indexing(size_t n)
1724+
{
1725+
static jl_value_t *depwarn_func = NULL;
1726+
if (!depwarn_func && jl_base_module) {
1727+
depwarn_func = jl_get_global(jl_base_module, jl_symbol("partial_linear_indexing_warning"));
1728+
}
1729+
if (!depwarn_func) {
1730+
jl_safe_printf("WARNING: Partial linear indexing is deprecated. Use "
1731+
"`reshape(A, Val{%zd})` to make the dimensionality of the array match "
1732+
"the number of indices\n", n);
1733+
return;
1734+
}
1735+
jl_value_t **depwarn_args;
1736+
JL_GC_PUSHARGS(depwarn_args, 2);
1737+
depwarn_args[0] = depwarn_func;
1738+
depwarn_args[1] = jl_box_long(n);
1739+
jl_apply(depwarn_args, 2);
1740+
JL_GC_POP();
1741+
}
1742+
17231743
#ifdef __cplusplus
17241744
}
17251745
#endif

src/cgutils.cpp

+24-2
Original file line numberDiff line numberDiff line change
@@ -1352,8 +1352,30 @@ static Value *emit_array_nd_index(const jl_cgval_t &ainfo, jl_value_t *ex, ssize
13521352
if (linear_indexing) {
13531353
// Compare the linearized index `i` against the linearized size of
13541354
// the accessed array, i.e. `if !(i < alen) goto error`.
1355-
Value *alen = emit_arraylen(ainfo, ex, ctx);
1356-
builder.CreateCondBr(builder.CreateICmpULT(i, alen), endBB, failBB);
1355+
if (nidxs > 1) {
1356+
// TODO: REMOVE DEPWARN AND RETURN FALSE AFTER 0.6.
1357+
// We need to check if this is inside the non-linearized size
1358+
BasicBlock *partidx = BasicBlock::Create(jl_LLVMContext, "partlinidx");
1359+
BasicBlock *partidxwarn = BasicBlock::Create(jl_LLVMContext, "partlinidxwarn");
1360+
Value *d = emit_arraysize_for_unsafe_dim(ainfo, ex, nidxs, nd, ctx);
1361+
builder.CreateCondBr(builder.CreateICmpULT(ii, d), endBB, partidx);
1362+
1363+
// We failed the normal bounds check; check to see if we're
1364+
// inside the linearized size (partial linear indexing):
1365+
ctx->f->getBasicBlockList().push_back(partidx);
1366+
builder.SetInsertPoint(partidx);
1367+
Value *alen = emit_arraylen(ainfo, ex, ctx);
1368+
builder.CreateCondBr(builder.CreateICmpULT(i, alen), partidxwarn, failBB);
1369+
1370+
// We passed the linearized bounds check; now throw the depwarn:
1371+
ctx->f->getBasicBlockList().push_back(partidxwarn);
1372+
builder.SetInsertPoint(partidxwarn);
1373+
builder.CreateCall(prepare_call(jldepwarnpi_func), ConstantInt::get(T_size, nidxs));
1374+
builder.CreateBr(endBB);
1375+
} else {
1376+
Value *alen = emit_arraylen(ainfo, ex, ctx);
1377+
builder.CreateCondBr(builder.CreateICmpULT(i, alen), endBB, failBB);
1378+
}
13571379
} else {
13581380
// Compare the last index of the access against the last dimension of
13591381
// the accessed array, i.e. `if !(last_index < last_dimension) goto error`.

src/codegen.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ static Function *expect_func;
389389
static Function *jldlsym_func;
390390
static Function *jlnewbits_func;
391391
static Function *jltypeassert_func;
392+
static Function *jldepwarnpi_func;
392393
#if JL_LLVM_VERSION < 30600
393394
static Function *jlpow_func;
394395
static Function *jlpowf_func;
@@ -5767,6 +5768,13 @@ static void init_julia_llvm_env(Module *m)
57675768
"jl_typeassert", m);
57685769
add_named_global(jltypeassert_func, &jl_typeassert);
57695770

5771+
std::vector<Type*> argsdepwarnpi(0);
5772+
argsdepwarnpi.push_back(T_size);
5773+
jldepwarnpi_func = Function::Create(FunctionType::get(T_void, argsdepwarnpi, false),
5774+
Function::ExternalLinkage,
5775+
"jl_depwarn_partial_indexing", m);
5776+
add_named_global(jldepwarnpi_func, &jl_depwarn_partial_indexing);
5777+
57705778
queuerootfun = Function::Create(FunctionType::get(T_void, args_1ptr, false),
57715779
Function::ExternalLinkage,
57725780
"jl_gc_queue_root", m);

src/julia_internal.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,8 @@ STATIC_INLINE void *jl_get_frame_addr(void)
811811
}
812812

813813
JL_DLLEXPORT jl_array_t *jl_array_cconvert_cstring(jl_array_t *a);
814-
void jl_depwarn(const char *msg, jl_sym_t *sym);
814+
void jl_depwarn(const char *msg, jl_value_t *sym);
815+
void jl_depwarn_partial_indexing(size_t n);
815816

816817
int isabspath(const char *in);
817818

0 commit comments

Comments
 (0)