-
Notifications
You must be signed in to change notification settings - Fork 12.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rust fails to deduce enumerate()
range
#92174
Comments
I got this error when tried to work with such code: for (i, vi) in arr.iter().enumerate(){
for (j, vj) in arr[..i].iter().enumerate(){
...
}
} I wrote minimized version in the issue. |
silly workaround for the moment: pub fn iter_too(v: &[u32]){
for (_, i) in v.iter().zip(0..v.len()) {
assert!(i <= v.len());
}
} |
Another workaround: pub fn iter_too(v: &[u32]){
for (i, _) in v.iter().enumerate(){
assert!(i < v.len());
assert!(i <= v.len());
}
} |
Does this really fall under the category of bug? Seems like it could be considered an enhancement to optimization. Either way, here's some findings. The resulting optimized MIR is identical apart from With
With
But the resulting LLVM IR is vastly different: With ; playground::iter_too
; Function Attrs: nonlazybind uwtable
define void @_ZN10playground8iter_too17he8dda29a6929fd5aE([0 x i32]* noalias nonnull readonly align 4 %v.0, i64 %v.1) unnamed_addr #0 personality i32 (i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality {
bb6:
ret void
} With ; playground::iter_too
; Function Attrs: nonlazybind uwtable
define void @_ZN10playground8iter_too17he8dda29a6929fd5aE([0 x i32]* noalias nonnull readonly align 4 %v.0, i64 %v.1) unnamed_addr #0 personality i32 (i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality {
start:
%0 = getelementptr [0 x i32], [0 x i32]* %v.0, i64 0, i64 0
%1 = getelementptr inbounds [0 x i32], [0 x i32]* %v.0, i64 0, i64 %v.1
br label %bb4
bb4: ; preds = %bb8, %start
%iter.sroa.0.0 = phi i32* [ %0, %start ], [ %2, %bb8 ]
%iter.sroa.7.0 = phi i64 [ 0, %start ], [ %_11.0.i, %bb8 ]
%_12.i.i = icmp eq i32* %iter.sroa.0.0, %1
br i1 %_12.i.i, label %bb6, label %bb8
bb6: ; preds = %bb4
ret void
bb8: ; preds = %bb4
%_11.0.i = add nuw nsw i64 %iter.sroa.7.0, 1
%2 = getelementptr inbounds i32, i32* %iter.sroa.0.0, i64 1
%_16.not = icmp ugt i64 %iter.sroa.7.0, %v.1
br i1 %_16.not, label %bb9, label %bb4
bb9: ; preds = %bb8
; call core::panicking::panic
tail call void @_ZN4core9panicking5panic17h0ba7146865b2f9d6E([0 x i8]* noalias nonnull readonly align 1 bitcast (<{ [30 x i8] }>* @alloc19 to [0 x i8]*), i64 30, %"core::panic::location::Location"* noalias readonly align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc21 to %"core::panic::location::Location"*)) #2
unreachable
} So I think maybe this points to a problem with constant evaluation. @rustbot label +A-const-eval +T-compiler |
The basic problem here is that the loop has two induction variables, a counter and a pointer. The loop condition is on the pointer, and SCEV apparently is unable to determine the correlation between them in this case. Full IR for reference: https://llvm.godbolt.org/z/P8a8nr714 |
I think that could be improved on the libs side. At least when |
clang fails to optimize it too. #include <cstddef>
#include <vector>
void do_checks(const std::vector<int> v){
size_t idx = 0;
for(auto& item : v){
v.at(idx);
++idx;
}
} compiles to do_checks(std::vector<int, std::allocator<int> >): # @do_checks(std::vector<int, std::allocator<int> >)
push rax
mov rax, qword ptr [rdi]
mov rcx, qword ptr [rdi + 8]
mov rdx, rcx
sub rdx, rax
je .LBB0_4
sar rdx, 2
mov rsi, -1
.LBB0_2: # =>This Inner Loop Header: Depth=1
add rsi, 1
cmp rdx, rsi
je .LBB0_5
add rax, 4
cmp rax, rcx
jne .LBB0_2
.LBB0_4:
pop rax
ret
.LBB0_5:
mov edi, offset .L.str
mov rsi, rdx
xor eax, eax
call std::__throw_out_of_range_fmt(char const*, ...)
.L.str:
.asciz "vector::_M_range_check: __n (which is %zu) >= this->size() (which is %zu)" |
I think, it is not optimal approach. We would need specializations of |
It's actually not that difficult, we already have a specialization trait that should cover most cases. |
This still would increase complexity and harm maintainability. It is fine to fix the issue that way but I think we should try to fix LLVM first and increase complexity of library only if we fail. |
@nikic or @AngelicosPhosphoros can either of you file the appropriate LLVM issue? |
Done. |
Interesting @AngelicosPhosphoros in the example you posted on the LLVM issue, you used |
The linked LLVM issue was closed and the commit that closed it can be found in the current rust-lang/llvm-project tree, so it should be available on nightly. But the reproducer still shows the panic on godbolt |
Filed another issue to mainstream: llvm/llvm-project#56612 |
Still a problem with LLVM 16. |
I tried this code:
I expected to see this happen: Assertions is removed because it is always false.
Instead, this happened: Generated code contains loop, check and panic.
Surprisingly, compiler is able to understand that
i < v.len()
This code
compiles into
godbolt link
Meta
rustc --version --verbose
:stable 1.57 affected too.
The text was updated successfully, but these errors were encountered: