From f99ace8fd2de936e8876d213d9e69867a2c1ebc7 Mon Sep 17 00:00:00 2001
From: Matt Bauman <mbauman@gmail.com>
Date: Mon, 16 Jan 2017 10:26:27 -0600
Subject: [PATCH 1/5] Allow multiple lookup sites in depwarn

---
 base/deprecated.jl | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/base/deprecated.jl b/base/deprecated.jl
index 487fbfa131150..2445be65be604 100644
--- a/base/deprecated.jl
+++ b/base/deprecated.jl
@@ -73,7 +73,8 @@ function depwarn(msg, funcsym)
     nothing
 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)
@@ -83,7 +84,7 @@ function firstcaller(bt::Array{Ptr{Void},1}, funcsym::Symbol)
             if lkup === StackTraces.UNKNOWN
                 continue
             end
-            if lkup.func == funcsym
+            if lkup.func in funcsyms
                 @goto found
             end
         end

From abd35fe48be8ac049e0206c9469964a0caef8323 Mon Sep 17 00:00:00 2001
From: Matt Bauman <mbauman@gmail.com>
Date: Mon, 16 Jan 2017 10:18:20 -0600
Subject: [PATCH 2/5] Deprecate partial linear indexing

---
 base/abstractarray.jl    | 21 ++++++++++-
 base/deprecated.jl       | 79 ++++++++++++++++++++++++++++++----------
 base/multidimensional.jl |  6 +++
 src/array.c              |  2 +-
 src/builtins.c           | 26 +++++++++++--
 src/cgutils.cpp          | 26 ++++++++++++-
 src/codegen.cpp          |  8 ++++
 src/julia_internal.h     |  3 +-
 test/abstractarray.jl    | 59 ++++++++++++++++--------------
 test/arrayops.jl         | 16 ++++----
 test/subarray.jl         | 21 ++++++++---
 11 files changed, 198 insertions(+), 69 deletions(-)

diff --git a/base/abstractarray.jl b/base/abstractarray.jl
index ca4e20115094f..db682c9e07ee9 100644
--- a/base/abstractarray.jl
+++ b/base/abstractarray.jl
@@ -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 2445be65be604..3b810fcfbf04e 100644
--- a/base/deprecated.jl
+++ b/base/deprecated.jl
@@ -59,41 +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
 
 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 in funcsyms
-                @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)
@@ -1740,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 74bbd5eff46be..69392f73df1a3 100644
--- a/base/multidimensional.jl
+++ b/base/multidimensional.jl
@@ -172,6 +172,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 b8aa96d2cfc14..a1b2214ad33fd 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;
@@ -5774,6 +5775,13 @@ static void init_julia_llvm_env(Module *m)
                                         "jl_typeassert", m);
     add_named_global(jltypeassert_func, &jl_typeassert);
 
+    std::vector<Type*> 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..759cc7ddf1d6d 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) == true       # PLI
+# @test checkbounds(Bool, A, 5, 12) == true       # PLI
 @test checkbounds(Bool, A, 1, 13) == false      # PLI
-@test checkbounds(Bool, A, 6, 12) == false      # PLI
+# @test checkbounds(Bool, A, 6, 12) == false      # PLI
 
 # 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,))) == true
 @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,))) == true
+# @test checkbounds(Bool, A, CartesianIndex((5, 12,))) == true
 @test checkbounds(Bool, A, CartesianIndex((1, 13,))) == false
-@test checkbounds(Bool, A, CartesianIndex((6, 12,))) == false
+# @test checkbounds(Bool, A, CartesianIndex((6, 12,))) == false
 
 # 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) == true
 @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
 
 # 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)) == true
 @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
 @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]))
+    # @test B[1:end,1:end] == A[1:end,1:end] == reshape(1:N, shape[1], prod(shape[2:end]))
     # 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]])
 
     # 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 f1070b925ebac..e0f956e64c0b4 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 8cb911b03e4e6..878899ac50c0b 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)
@@ -476,7 +485,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]
 @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]]

From 5ff6bc40a5f44fc0bd3dc65560e50db7c1ce8af6 Mon Sep 17 00:00:00 2001
From: Matt Bauman <mbauman@gmail.com>
Date: Wed, 18 Jan 2017 18:40:29 -0600
Subject: [PATCH 3/5] Add NEWS.md [ci skip]

---
 NEWS.md | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/NEWS.md b/NEWS.md
index 5d37f11030bc7..407ec2b23b62c 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]).

From 606c88f71460cfecf8c0f5601131d9118794616e Mon Sep 17 00:00:00 2001
From: Matt Bauman <mbauman@gmail.com>
Date: Sun, 22 Jan 2017 18:18:14 -0600
Subject: [PATCH 4/5] Add comments on the disabled PLI tests

---
 test/abstractarray.jl | 28 ++++++++++++++--------------
 test/subarray.jl      |  2 +-
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/test/abstractarray.jl b/test/abstractarray.jl
index 759cc7ddf1d6d..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
@@ -64,9 +64,9 @@ A = rand(5,4,3)
 @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:4) == true
-# @test checkbounds(Bool, A, 1:5, 1:12) == 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
@@ -77,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
@@ -358,8 +358,8 @@ 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
@@ -373,7 +373,7 @@ function test_vector_indexing{T}(::Type{T}, shape, ::Type{TestAbstractArray})
     # 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)
diff --git a/test/subarray.jl b/test/subarray.jl
index 878899ac50c0b..b9c143759b330 100644
--- a/test/subarray.jl
+++ b/test/subarray.jl
@@ -485,7 +485,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]]

From fbb047cc43c105cfa8e320a3c06e236d603eaee9 Mon Sep 17 00:00:00 2001
From: Matt Bauman <mbauman@gmail.com>
Date: Sun, 22 Jan 2017 22:48:08 -0600
Subject: [PATCH 5/5] Ensure linearindices completely inlines (through _length)

---
 base/abstractarray.jl | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/base/abstractarray.jl b/base/abstractarray.jl
index db682c9e07ee9..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))]
 
 """