@@ -13853,6 +13853,49 @@ fn inline_array_intrinsic_in_print_walks_descriptor_elements() { |
| 13853 | 13853 | let _ = std::fs::remove_file(&src); |
| 13854 | 13854 | } |
| 13855 | 13855 | |
| 13856 | +#[test] |
| 13857 | +fn proc_pointer_component_call_passes_assumed_shape_array_as_descriptor() { |
| 13858 | + // F2018 §15.5.2.5: an assumed-shape dummy receives a descriptor. |
| 13859 | + // The procedure-pointer component call dispatch in expr.rs was |
| 13860 | + // using `lower_arg_by_ref_full` for every actual, regardless of |
| 13861 | + // what the abstract interface declared — so an `x(:)` formal got |
| 13862 | + // only the array's base data pointer. The callee then read |
| 13863 | + // dims/rank out of the array bytes (size=1 silently, or SEGV at |
| 13864 | + // afs_array_size when the bogus rank exceeded 15). stdlib's |
| 13865 | + // iterative solvers (solve_cg/bicgstab/pcg) and pseudoinverse |
| 13866 | + // dispatched dot_product/matvec through procedure-pointer fields |
| 13867 | + // and crashed deep inside stdlib_dot_product_dp on a doubly- |
| 13868 | + // indirected dereference. The fix applies the same descriptor mask |
| 13869 | + // lookup the regular call path uses. |
| 13870 | + let src = write_program( |
| 13871 | + "module m\n implicit none\n abstract interface\n function reduce_iface(x, y) result(r)\n real(8), intent(in) :: x(:), y(:)\n real(8) :: r\n end function\n end interface\n type :: linop\n procedure(reduce_iface), nopass, pointer :: dot => null()\n end type\ncontains\n function default_dot(x, y) result(r)\n real(8), intent(in) :: x(:), y(:)\n real(8) :: r\n integer :: i\n r = 0.0_8\n do i = 1, size(x)\n r = r + x(i) * y(i)\n end do\n end function\nend module\nprogram p\n use m\n implicit none\n type(linop) :: opa\n real(8) :: vx(3), vy(3), s\n vx = [1.0_8, 2.0_8, 3.0_8]\n vy = [4.0_8, 5.0_8, 6.0_8]\n opa%dot => default_dot\n s = opa%dot(vx, vy)\n if (abs(s - 32.0_8) > 1.0e-12_8) error stop 1\n print *, 'ok'\nend program\n", |
| 13872 | + "f90", |
| 13873 | + ); |
| 13874 | + let out = unique_path("proc_ptr_desc", "bin"); |
| 13875 | + let compile = Command::new(compiler("armfortas")) |
| 13876 | + .args([src.to_str().unwrap(), "-o", out.to_str().unwrap()]) |
| 13877 | + .output() |
| 13878 | + .expect("proc-ptr compile failed"); |
| 13879 | + assert!( |
| 13880 | + compile.status.success(), |
| 13881 | + "proc-ptr compile should succeed: {}", |
| 13882 | + String::from_utf8_lossy(&compile.stderr) |
| 13883 | + ); |
| 13884 | + let run = Command::new(&out).output().expect("proc-ptr run failed"); |
| 13885 | + assert!( |
| 13886 | + run.status.success(), |
| 13887 | + "proc-ptr run should pass: status={:?} stdout={} stderr={}", |
| 13888 | + run.status, |
| 13889 | + String::from_utf8_lossy(&run.stdout), |
| 13890 | + String::from_utf8_lossy(&run.stderr) |
| 13891 | + ); |
| 13892 | + let stdout = String::from_utf8_lossy(&run.stdout); |
| 13893 | + assert!(stdout.contains("ok"), "expected ok, got: {}", stdout); |
| 13894 | + |
| 13895 | + let _ = std::fs::remove_file(&out); |
| 13896 | + let _ = std::fs::remove_file(&src); |
| 13897 | +} |
| 13898 | + |
| 13856 | 13899 | #[test] |
| 13857 | 13900 | fn merge_intrinsic_routes_array_operands_through_descriptor_path() { |
| 13858 | 13901 | // F2018 §16.9.135: MERGE is elemental and returns an array when any |