@@ -28623,6 +28623,47 @@ fn cmplx_whole_array_with_kind_keyword_returns_correct_kind_descriptor() { |
| 28623 | let _ = std::fs::remove_file(&src); | 28623 | let _ = std::fs::remove_file(&src); |
| 28624 | } | 28624 | } |
| 28625 | | 28625 | |
| | 28626 | +#[test] |
| | 28627 | +fn allocatable_array_component_passed_to_assumed_size_unwraps_descriptor() { |
| | 28628 | + // F2018 §15.5.2.4: when an allocatable rank-N array component |
| | 28629 | + // (e.g. `c%idx` where `idx` is `integer, allocatable :: idx(:,:)`) |
| | 28630 | + // is passed to a by-ref dummy declared assumed-size or |
| | 28631 | + // explicit-shape (`a(2, *)`), the callee expects an element |
| | 28632 | + // pointer it can index column-major directly. lower_arg_by_ref_full |
| | 28633 | + // for the ComponentAccess path used to return the field's storage |
| | 28634 | + // address — which for an allocatable component is the address of |
| | 28635 | + // the 384-byte descriptor itself — so the dummy walked descriptor |
| | 28636 | + // bytes (base_addr, elem_size, rank fields) as if they were array |
| | 28637 | + // elements. Surfaced inside stdlib_sparse_conversion's |
| | 28638 | + // sort_coo_unique_dp where `a(1, ed)` returned descriptor-pointer |
| | 28639 | + // bits and triggered "Bounds check failed: index <garbage> outside |
| | 28640 | + // [0, num_rows]" inside count_i indexing. |
| | 28641 | + let src = write_program( |
| | 28642 | + "module m\n implicit none\n type :: container\n integer, allocatable :: idx(:,:)\n end type\ncontains\n subroutine sort_check(a, n, ok)\n integer, intent(inout) :: a(2,*)\n integer, intent(in) :: n\n logical, intent(out) :: ok\n integer :: ed\n ok = .true.\n do ed = 1, n\n if (a(1, ed) /= ed) ok = .false.\n if (a(2, ed) /= 100 + ed) ok = .false.\n end do\n end subroutine\nend module\nprogram p\n use m\n implicit none\n type(container) :: c\n integer :: ed\n logical :: ok\n allocate(c%idx(2, 10), source=0)\n do ed = 1, 10\n c%idx(1:2, ed) = [ed, 100 + ed]\n end do\n call sort_check(c%idx, 10, ok)\n if (.not. ok) error stop 1\n print *, 'ok'\nend program\n", |
| | 28643 | + "f90", |
| | 28644 | + ); |
| | 28645 | + let out = unique_path("alloc_component_to_assumed_size", "bin"); |
| | 28646 | + let compile = Command::new(compiler("armfortas")) |
| | 28647 | + .args([src.to_str().unwrap(), "-o", out.to_str().unwrap()]) |
| | 28648 | + .output() |
| | 28649 | + .expect("alloc-component compile failed to spawn"); |
| | 28650 | + assert!( |
| | 28651 | + compile.status.success(), |
| | 28652 | + "alloc-component compile failed: {}", |
| | 28653 | + String::from_utf8_lossy(&compile.stderr) |
| | 28654 | + ); |
| | 28655 | + let run = Command::new(&out).output().expect("run failed"); |
| | 28656 | + assert!( |
| | 28657 | + run.status.success() && String::from_utf8_lossy(&run.stdout).contains("ok"), |
| | 28658 | + "alloc-component run failed: status={:?} stdout={} stderr={}", |
| | 28659 | + run.status, |
| | 28660 | + String::from_utf8_lossy(&run.stdout), |
| | 28661 | + String::from_utf8_lossy(&run.stderr) |
| | 28662 | + ); |
| | 28663 | + let _ = std::fs::remove_file(&out); |
| | 28664 | + let _ = std::fs::remove_file(&src); |
| | 28665 | +} |
| | 28666 | + |
| 28626 | #[test] | 28667 | #[test] |
| 28627 | fn allocatable_rank2_section_row_assignment_uses_columnmajor_stride() { | 28668 | fn allocatable_rank2_section_row_assignment_uses_columnmajor_stride() { |
| 28628 | // F2018 §6.5.3: section assignment to a "row" of a column-major | 28669 | // F2018 §6.5.3: section assignment to a "row" of a column-major |