fortrangoingonforty/armfortas / 20fb85b

Browse files

Honor type_spec in reshape ArrayConstructor lowering

F2018 §7.8: a typed array constructor '[T :: ...]' has element type T
regardless of the element expressions' types. The reshape lowering at
lower_reshape_array_expr_descriptor inferred elem_ty solely via
first_array_constructor_type_info, which examines the first value
expression. For 'reshape([real(dp) :: 1, 2, 3, 4], [2, 2])' the values
are integer literals (4 bytes), so the materialised descriptor was
elem_size=4 instead of 8.

The malformed elem_size propagated through the reshape result; when
passed to an assumed-shape dummy 'a(:,:)' and used as SOURCE= in an
ALLOCATE, afs_prepare_array_copy saw 'dest.elem_size != source.elem_size'
(8 != 4), freed the freshly-allocated dest buffer, zeroed base_addr,
and the next read of 'amat(1,1)' SEGV'd. Surfaced across stdlib's det /
determinant / eig / qr clusters whose examples invoke
'det(reshape([real(dp)::1,2,3,4], [2,2]))'.

Consult type_spec first; fall back to first-element inference only when
no type_spec is present.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
20fb85bb5e67aed3accc3356da6603346b499116
Parents
8e5daf7
Tree
b86f7ff

2 changed files

StatusFile+-
M src/ir/lower/core.rs 16 2
M tests/cli_driver.rs 66 0
src/ir/lower/core.rsmodified
@@ -22963,8 +22963,22 @@ pub(super) fn lower_reshape_array_expr_descriptor(
2296322963
     // assignment plans handle them, but RESHAPE consumes the descriptor
2296422964
     // directly. Try materialization first; fall back to the regular
2296522965
     // descriptor path for Names, intrinsic calls, etc.
22966
-    let (source_desc, elem_ty) = if let Expr::ArrayConstructor { values, .. } = &source_expr.node {
22967
-        let elem_ty = first_array_constructor_type_info(values, Some(locals), st, type_layouts)
22966
+    let (source_desc, elem_ty) = if let Expr::ArrayConstructor { values, type_spec } = &source_expr.node {
22967
+        // F2018 §7.8: a typed array constructor `[T :: ...]` has element
22968
+        // type T regardless of the element expressions' types.  For
22969
+        // `reshape([real(dp) :: 1, 2, 3, 4], [2, 2])` the values are
22970
+        // integer literals, but the constructor's type is real(dp) — so
22971
+        // its descriptor must carry elem_size=8.  Without consulting
22972
+        // type_spec first the materialised descriptor was elem_size=4
22973
+        // (integer), and downstream `allocate(amat(...), source=…)` then
22974
+        // saw an elem_size mismatch in afs_prepare_array_copy and
22975
+        // freed the freshly-allocated dest, producing a SEGV on the
22976
+        // next read of `amat(1,1)`.
22977
+        let elem_ty = type_spec
22978
+            .as_ref()
22979
+            .and_then(|s| crate::sema::types::type_spec_to_fortran_type(s, st))
22980
+            .and_then(|fty| fortran_type_to_type_info(&fty))
22981
+            .or_else(|| first_array_constructor_type_info(values, Some(locals), st, type_layouts))
2296822982
             .map(|ti| type_info_to_ir_type(&ti))
2296922983
             .unwrap_or(IrType::Float(FloatWidth::F32));
2297022984
         let desc = lower_runtime_array_constructor_descriptor(
tests/cli_driver.rsmodified
@@ -3520,6 +3520,72 @@ fn allocate_bounds_size_intrinsic_lowers_without_raw_symbol() {
35203520
     let _ = std::fs::remove_file(&src);
35213521
 }
35223522
 
3523
+#[test]
3524
+fn reshape_typed_array_constructor_preserves_elem_size_through_assumed_shape() {
3525
+    // F2018 §7.8: a typed array constructor `[T :: ...]` has element
3526
+    // type T regardless of the element expressions' types.  The reshape
3527
+    // lowering used to ignore type_spec and infer elem_ty from the
3528
+    // first value — `[real(dp) :: 1, 2, 3, 4]` would resolve as integer
3529
+    // (4 bytes) instead of real(dp) (8 bytes).  The malformed elem_size
3530
+    // then propagated through the reshape result descriptor; when
3531
+    // passed to an assumed-shape dummy and used as the SOURCE= of an
3532
+    // ALLOCATE, afs_prepare_array_copy saw `dest.elem_size != source.elem_size`,
3533
+    // freed the freshly-allocated dest buffer, zeroed base_addr, and
3534
+    // SEGV'd on the next read.  Surfaced in stdlib's det / determinant /
3535
+    // eig / qr clusters where examples invoke
3536
+    // `det(reshape([real(dp)::1,2,3,4], [2,2]))`.
3537
+    let src = write_program(
3538
+        r#"
3539
+module m
3540
+  implicit none
3541
+  integer, parameter :: dp = kind(1.0d0)
3542
+contains
3543
+  function probe(a) result(s)
3544
+    real(dp), intent(in) :: a(:, :)
3545
+    real(dp) :: s
3546
+    real(dp), allocatable :: amat(:, :)
3547
+    allocate(amat(size(a,1), size(a,2)), source=a)
3548
+    s = amat(1, 1) + amat(2, 2)
3549
+    deallocate(amat)
3550
+  end function
3551
+end module
3552
+program t
3553
+  use m
3554
+  implicit none
3555
+  real(dp) :: r
3556
+  r = probe(reshape([real(dp) :: 1, 2, 3, 4], [2, 2]))
3557
+  if (abs(r - 5.0_dp) > 1.0e-12_dp) error stop 1
3558
+  print *, 'ok'
3559
+end program
3560
+"#,
3561
+        "f90",
3562
+    );
3563
+    let out = unique_path("reshape_typed_ac_elem_size", "bin");
3564
+    let compile = Command::new(compiler("armfortas"))
3565
+        .args([src.to_str().unwrap(), "-o", out.to_str().unwrap()])
3566
+        .output()
3567
+        .expect("reshape-typed-ac compile spawn failed");
3568
+    assert!(
3569
+        compile.status.success(),
3570
+        "should compile cleanly: {}",
3571
+        String::from_utf8_lossy(&compile.stderr)
3572
+    );
3573
+    let run = Command::new(&out).output().expect("run failed");
3574
+    assert!(
3575
+        run.status.success(),
3576
+        "reshape→source= runtime failed: status={:?} stderr={}",
3577
+        run.status,
3578
+        String::from_utf8_lossy(&run.stderr)
3579
+    );
3580
+    assert!(
3581
+        String::from_utf8_lossy(&run.stdout).contains("ok"),
3582
+        "expected 'ok' from reshape→source=: {}",
3583
+        String::from_utf8_lossy(&run.stdout)
3584
+    );
3585
+    let _ = std::fs::remove_file(&out);
3586
+    let _ = std::fs::remove_file(&src);
3587
+}
3588
+
35233589
 #[test]
35243590
 fn allocate_with_source_from_assumed_shape_dummy_populates_base_addr() {
35253591
     // F2018 §9.7.1.2: ALLOCATE(..., SOURCE=expr) requires only that