@@ -32843,17 +32843,41 @@ pub(super) fn lower_stmt_error(span: crate::lexer::Span, message: &str) -> ! { |
| 32843 | 32843 | std::process::exit(1); |
| 32844 | 32844 | } |
| 32845 | 32845 | |
| 32846 | +/// Result of resolving an ALLOCATE/DEALLOCATE STAT= target: the |
| 32847 | +/// address the runtime helper writes to (always i32), plus an |
| 32848 | +/// optional writeback to the user's variable for kinds wider than |
| 32849 | +/// the default integer. `writeback_user_addr` and `writeback_ty` are |
| 32850 | +/// Some when the user's variable is e.g. integer(int64): the call |
| 32851 | +/// site stores the runtime's i32 result back via sign-extension at |
| 32852 | +/// the end of the statement so that subsequent `if (stat /= 0)` |
| 32853 | +/// checks read the actual status, not a stale or partially-written |
| 32854 | +/// stack slot. F2018 §9.7.1.3 requires the stat-variable to receive |
| 32855 | +/// the status code. |
| 32856 | +pub(super) struct AllocateStatTarget { |
| 32857 | + pub runtime_addr: ValueId, |
| 32858 | + pub writeback_user_addr: Option<ValueId>, |
| 32859 | + pub writeback_ty: Option<IrType>, |
| 32860 | +} |
| 32861 | + |
| 32846 | 32862 | pub(super) fn allocate_status_target_addr(b: &mut FuncBuilder, ctx: &LowerCtx, opts: &[IoControl]) -> ValueId { |
| 32863 | + allocate_status_target(b, ctx, opts).runtime_addr |
| 32864 | +} |
| 32865 | + |
| 32866 | +pub(super) fn allocate_status_target(b: &mut FuncBuilder, ctx: &LowerCtx, opts: &[IoControl]) -> AllocateStatTarget { |
| 32847 | 32867 | let Some(stat_expr) = allocate_keyword_expr(opts, "stat") else { |
| 32848 | | - return b.alloca(IrType::Int(IntWidth::I32)); |
| 32868 | + return AllocateStatTarget { |
| 32869 | + runtime_addr: b.alloca(IrType::Int(IntWidth::I32)), |
| 32870 | + writeback_user_addr: None, |
| 32871 | + writeback_ty: None, |
| 32872 | + }; |
| 32849 | 32873 | }; |
| 32850 | 32874 | // F2018 §9.7.1.3: stat-variable must be a scalar variable of type |
| 32851 | 32875 | // integer with a decimal exponent range of at least four — i.e. |
| 32852 | 32876 | // any kind ≥ default integer is acceptable, not strictly default. |
| 32853 | 32877 | // The runtime helpers store an i32 status, so when the user's |
| 32854 | 32878 | // variable is a wider kind we allocate a scratch i32, pass that |
| 32855 | | - // to the runtime, and the caller writes back to the user's var |
| 32856 | | - // after the call (handled in the dealloc/alloc emit sites). |
| 32879 | + // to the runtime, and the caller writes the (sign-extended) i32 |
| 32880 | + // back to the user's variable after the statement completes. |
| 32857 | 32881 | match &stat_expr.node { |
| 32858 | 32882 | Expr::Name { name } => { |
| 32859 | 32883 | let Some(info) = ctx.locals.get(&name.to_lowercase()) else { |
@@ -32871,20 +32895,15 @@ pub(super) fn allocate_status_target_addr(b: &mut FuncBuilder, ctx: &LowerCtx, o |
| 32871 | 32895 | ); |
| 32872 | 32896 | } |
| 32873 | 32897 | if matches!(info.ty, IrType::Int(IntWidth::I32)) { |
| 32874 | | - // Default kind — runtime stores directly. |
| 32875 | | - if info.by_ref { |
| 32876 | | - b.load(info.addr) |
| 32877 | | - } else { |
| 32878 | | - info.addr |
| 32879 | | - } |
| 32898 | + let runtime_addr = if info.by_ref { b.load(info.addr) } else { info.addr }; |
| 32899 | + AllocateStatTarget { runtime_addr, writeback_user_addr: None, writeback_ty: None } |
| 32880 | 32900 | } else { |
| 32881 | | - // Wider kind — return a scratch i32 the runtime can |
| 32882 | | - // store to. The user's variable stays uninitialized |
| 32883 | | - // (matches gfortran; stdlib's pattern of `stat /= 0` |
| 32884 | | - // checks the scratch's zero/non-zero status which the |
| 32885 | | - // user reads via a follow-on assignment from the same |
| 32886 | | - // call site, not from this variable directly). |
| 32887 | | - b.alloca(IrType::Int(IntWidth::I32)) |
| 32901 | + let user_addr = if info.by_ref { b.load(info.addr) } else { info.addr }; |
| 32902 | + AllocateStatTarget { |
| 32903 | + runtime_addr: b.alloca(IrType::Int(IntWidth::I32)), |
| 32904 | + writeback_user_addr: Some(user_addr), |
| 32905 | + writeback_ty: Some(info.ty.clone()), |
| 32906 | + } |
| 32888 | 32907 | } |
| 32889 | 32908 | } |
| 32890 | 32909 | Expr::ComponentAccess { .. } => { |
@@ -32900,10 +32919,20 @@ pub(super) fn allocate_status_target_addr(b: &mut FuncBuilder, ctx: &LowerCtx, o |
| 32900 | 32919 | crate::sema::symtab::TypeInfo::Integer { kind } |
| 32901 | 32920 | if *kind == Some(4) && field.dims.is_empty() => |
| 32902 | 32921 | { |
| 32903 | | - field_ptr |
| 32904 | | - } |
| 32905 | | - crate::sema::symtab::TypeInfo::Integer { .. } if field.dims.is_empty() => { |
| 32906 | | - b.alloca(IrType::Int(IntWidth::I32)) |
| 32922 | + AllocateStatTarget { runtime_addr: field_ptr, writeback_user_addr: None, writeback_ty: None } |
| 32923 | + } |
| 32924 | + crate::sema::symtab::TypeInfo::Integer { kind } if field.dims.is_empty() => { |
| 32925 | + let width = match kind { |
| 32926 | + Some(1) => IntWidth::I8, |
| 32927 | + Some(2) => IntWidth::I16, |
| 32928 | + Some(8) => IntWidth::I64, |
| 32929 | + _ => IntWidth::I64, |
| 32930 | + }; |
| 32931 | + AllocateStatTarget { |
| 32932 | + runtime_addr: b.alloca(IrType::Int(IntWidth::I32)), |
| 32933 | + writeback_user_addr: Some(field_ptr), |
| 32934 | + writeback_ty: Some(IrType::Int(width)), |
| 32935 | + } |
| 32907 | 32936 | } |
| 32908 | 32937 | _ => lower_stmt_error( |
| 32909 | 32938 | stat_expr.span, |
@@ -32918,6 +32947,24 @@ pub(super) fn allocate_status_target_addr(b: &mut FuncBuilder, ctx: &LowerCtx, o |
| 32918 | 32947 | } |
| 32919 | 32948 | } |
| 32920 | 32949 | |
| 32950 | +/// Sign-extend the runtime's i32 stat result back into the user's |
| 32951 | +/// wider integer variable, if STAT= named one. No-op when the user |
| 32952 | +/// variable is the default integer kind (runtime wrote directly) or |
| 32953 | +/// when no STAT= clause is present. |
| 32954 | +pub(super) fn emit_allocate_status_writeback(b: &mut FuncBuilder, target: &AllocateStatTarget) { |
| 32955 | + let (Some(user_addr), Some(ty)) = (target.writeback_user_addr, target.writeback_ty.as_ref()) else { |
| 32956 | + return; |
| 32957 | + }; |
| 32958 | + let i32_val = b.load_typed(target.runtime_addr, IrType::Int(IntWidth::I32)); |
| 32959 | + let widened = match ty { |
| 32960 | + IrType::Int(IntWidth::I32) => i32_val, |
| 32961 | + IrType::Int(width @ (IntWidth::I8 | IntWidth::I16)) => b.int_trunc(i32_val, *width), |
| 32962 | + IrType::Int(width) => b.int_extend(i32_val, *width, true), |
| 32963 | + _ => return, |
| 32964 | + }; |
| 32965 | + b.store(widened, user_addr); |
| 32966 | +} |
| 32967 | + |
| 32921 | 32968 | pub(super) fn resolve_errmsg_target_expr( |
| 32922 | 32969 | b: &mut FuncBuilder, |
| 32923 | 32970 | ctx: &LowerCtx, |