fortrangoingonforty/armfortas / f465834

Browse files

Test proc-ptr component call passes assumed-shape array as descriptor

Authored by espadonne
Committed by mfwolffe
SHA
f465834576208348b24da2a10df5459e60614fda
Parents
b9065ea
Tree
9dde485

1 changed file

StatusFile+-
M tests/cli_driver.rs 43 0
tests/cli_driver.rsmodified
@@ -13853,6 +13853,49 @@ fn inline_array_intrinsic_in_print_walks_descriptor_elements() {
1385313853
     let _ = std::fs::remove_file(&src);
1385413854
 }
1385513855
 
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
+
1385613899
 #[test]
1385713900
 fn merge_intrinsic_routes_array_operands_through_descriptor_path() {
1385813901
     // F2018 §16.9.135: MERGE is elemental and returns an array when any