@@ -47,6 +47,13 @@ pub(crate) enum CharKind { |
| 47 | 47 | AssumedLen { len_addr: ValueId }, |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | +#[derive(Clone, Copy, PartialEq, Eq)] |
| 51 | +enum HiddenResultAbi { |
| 52 | + None, |
| 53 | + ArrayDescriptor, |
| 54 | + StringDescriptor, |
| 55 | +} |
| 56 | + |
| 50 | 57 | /// Info about a local variable. |
| 51 | 58 | #[derive(Clone)] |
| 52 | 59 | struct LocalInfo { |
@@ -264,6 +271,7 @@ pub fn lower_file( |
| 264 | 271 | st: &SymbolTable, |
| 265 | 272 | type_layouts: &crate::sema::type_layout::TypeLayoutRegistry, |
| 266 | 273 | external_globals: HashMap<(String, String), ModuleGlobalInfo>, |
| 274 | + external_descriptor_params: HashMap<String, Vec<bool>>, |
| 267 | 275 | external_char_len_star: HashMap<String, Vec<bool>>, |
| 268 | 276 | ) -> (Module, HashMap<(String, String), ModuleGlobalInfo>) { |
| 269 | 277 | let mut module = Module::new("main".into()); |
@@ -353,7 +361,7 @@ pub fn lower_file( |
| 353 | 361 | collect_optional_params(&unit.node, &mut optional_params); |
| 354 | 362 | } |
| 355 | 363 | |
| 356 | | - let mut descriptor_params: HashMap<String, Vec<bool>> = HashMap::new(); |
| 364 | + let mut descriptor_params: HashMap<String, Vec<bool>> = external_descriptor_params; |
| 357 | 365 | for unit in units { |
| 358 | 366 | collect_descriptor_params(&unit.node, &mut descriptor_params); |
| 359 | 367 | } |
@@ -582,10 +590,63 @@ fn collect_internal_func_names( |
| 582 | 590 | } |
| 583 | 591 | } |
| 584 | 592 | |
| 593 | +fn function_hidden_result_abi( |
| 594 | + function_name: &str, |
| 595 | + result: &Option<String>, |
| 596 | + decls: &[crate::ast::decl::SpannedDecl], |
| 597 | +) -> HiddenResultAbi { |
| 598 | + use crate::ast::decl::Attribute; |
| 599 | + let result_key = result |
| 600 | + .as_deref() |
| 601 | + .unwrap_or(function_name) |
| 602 | + .to_ascii_lowercase(); |
| 603 | + for decl in decls { |
| 604 | + let Decl::TypeDecl { |
| 605 | + type_spec, |
| 606 | + attrs, |
| 607 | + entities, |
| 608 | + .. |
| 609 | + } = &decl.node |
| 610 | + else { |
| 611 | + continue; |
| 612 | + }; |
| 613 | + let attr_dims: Option<&Vec<ArraySpec>> = attrs.iter().find_map(|a| { |
| 614 | + if let Attribute::Dimension(specs) = a { |
| 615 | + Some(specs) |
| 616 | + } else { |
| 617 | + None |
| 618 | + } |
| 619 | + }); |
| 620 | + for entity in entities { |
| 621 | + if entity.name.to_ascii_lowercase() != result_key { |
| 622 | + continue; |
| 623 | + } |
| 624 | + let has_dims = entity.array_spec.as_ref().or(attr_dims).is_some(); |
| 625 | + let is_hidden_string = matches!( |
| 626 | + type_spec, |
| 627 | + TypeSpec::Character(Some(sel)) |
| 628 | + if matches!(sel.len, Some(crate::ast::decl::LenSpec::Colon)) |
| 629 | + ) && !has_dims |
| 630 | + && attrs |
| 631 | + .iter() |
| 632 | + .any(|a| matches!(a, Attribute::Allocatable | Attribute::Pointer)); |
| 633 | + if is_hidden_string { |
| 634 | + return HiddenResultAbi::StringDescriptor; |
| 635 | + } |
| 636 | + if attrs.iter().any(|a| matches!(a, Attribute::Allocatable)) { |
| 637 | + return HiddenResultAbi::ArrayDescriptor; |
| 638 | + } |
| 639 | + return HiddenResultAbi::None; |
| 640 | + } |
| 641 | + } |
| 642 | + HiddenResultAbi::None |
| 643 | +} |
| 644 | + |
| 585 | 645 | /// Walk a program unit and any nested `contains` to collect the |
| 586 | | -/// names of functions whose result variable is declared |
| 587 | | -/// `allocatable`. The set is keyed by lowercase function name and |
| 588 | | -/// is consulted at call sites in pass 2. |
| 646 | +/// names of functions whose result variable is lowered through the |
| 647 | +/// 384-byte array descriptor hidden-result ABI. Deferred-length |
| 648 | +/// scalar character results use the 32-byte string descriptor ABI |
| 649 | +/// and are intentionally excluded here. |
| 589 | 650 | /// |
| 590 | 651 | /// Audit6 BLOCKING-1: a function `function f() result(r); integer, |
| 591 | 652 | /// allocatable :: r(:)` cannot be returned by value through the |
@@ -595,23 +656,6 @@ fn collect_internal_func_names( |
| 595 | 656 | /// that the caller passes in, and the function writes its result |
| 596 | 657 | /// into that descriptor. |
| 597 | 658 | fn collect_alloc_return_funcs(unit: &ProgramUnit, out: &mut HashSet<String>) { |
| 598 | | - use crate::ast::decl::Attribute; |
| 599 | | - let scan_decls = |decls: &[crate::ast::decl::SpannedDecl], result_name: &str| -> bool { |
| 600 | | - let key = result_name.to_lowercase(); |
| 601 | | - for decl in decls { |
| 602 | | - if let Decl::TypeDecl { |
| 603 | | - entities, attrs, .. |
| 604 | | - } = &decl.node |
| 605 | | - { |
| 606 | | - for entity in entities { |
| 607 | | - if entity.name.to_lowercase() == key { |
| 608 | | - return attrs.iter().any(|a| matches!(a, Attribute::Allocatable)); |
| 609 | | - } |
| 610 | | - } |
| 611 | | - } |
| 612 | | - } |
| 613 | | - false |
| 614 | | - }; |
| 615 | 659 | match unit { |
| 616 | 660 | ProgramUnit::Function { |
| 617 | 661 | name, |
@@ -620,8 +664,7 @@ fn collect_alloc_return_funcs(unit: &ProgramUnit, out: &mut HashSet<String>) { |
| 620 | 664 | result, |
| 621 | 665 | .. |
| 622 | 666 | } => { |
| 623 | | - let result_name = result.as_deref().unwrap_or(name.as_str()); |
| 624 | | - if scan_decls(decls, result_name) { |
| 667 | + if function_hidden_result_abi(name, result, decls) == HiddenResultAbi::ArrayDescriptor { |
| 625 | 668 | out.insert(name.to_lowercase()); |
| 626 | 669 | } |
| 627 | 670 | for sub in contains { |
@@ -1769,14 +1812,11 @@ fn collect_module_globals( |
| 1769 | 1812 | let is_pointer = attrs.iter().any(|a| matches!(a, Attribute::Pointer)); |
| 1770 | 1813 | let global_char_kind = match type_spec { |
| 1771 | 1814 | TypeSpec::Character(Some(sel)) => match &sel.len { |
| 1772 | | - Some(crate::ast::decl::LenSpec::Expr(e)) => match &e.node { |
| 1773 | | - crate::ast::expr::Expr::IntegerLiteral { text, .. } => text |
| 1774 | | - .parse::<i64>() |
| 1775 | | - .ok() |
| 1815 | + Some(crate::ast::decl::LenSpec::Expr(e)) => { |
| 1816 | + eval_const_int_in_scope_or_any_scope(e, &HashMap::new(), st) |
| 1776 | 1817 | .map(CharKind::Fixed) |
| 1777 | | - .unwrap_or(CharKind::None), |
| 1778 | | - _ => CharKind::None, |
| 1779 | | - }, |
| 1818 | + .unwrap_or(CharKind::None) |
| 1819 | + } |
| 1780 | 1820 | Some(crate::ast::decl::LenSpec::Colon) => CharKind::Deferred, |
| 1781 | 1821 | Some(crate::ast::decl::LenSpec::Star) => CharKind::None, |
| 1782 | 1822 | None => CharKind::Fixed(1), |
@@ -1800,6 +1840,12 @@ fn collect_module_globals( |
| 1800 | 1840 | // descriptor body rather than an extra layer of |
| 1801 | 1841 | // indirection. |
| 1802 | 1842 | if (is_allocatable || is_pointer) && array_spec.is_some() { |
| 1843 | + let storage_ty = if let Some(type_name) = &derived_type_name { |
| 1844 | + derived_storage_ir_type(type_name, type_layouts) |
| 1845 | + .unwrap_or_else(|| ir_ty.clone()) |
| 1846 | + } else { |
| 1847 | + ir_ty.clone() |
| 1848 | + }; |
| 1803 | 1849 | let desc_ty = IrType::Array(Box::new(IrType::Int(IntWidth::I8)), 384); |
| 1804 | 1850 | module.add_global(Global { |
| 1805 | 1851 | name: symbol.clone(), |
@@ -1810,12 +1856,12 @@ fn collect_module_globals( |
| 1810 | 1856 | (mod_name.to_lowercase(), entity.name.to_lowercase()), |
| 1811 | 1857 | ModuleGlobalInfo { |
| 1812 | 1858 | symbol, |
| 1813 | | - ty: ir_ty.clone(), |
| 1859 | + ty: storage_ty, |
| 1814 | 1860 | dims: vec![], |
| 1815 | 1861 | allocatable: true, |
| 1816 | 1862 | is_pointer, |
| 1817 | 1863 | deferred_char: false, |
| 1818 | | - derived_type: None, |
| 1864 | + derived_type: derived_type_name.clone(), |
| 1819 | 1865 | char_kind: global_char_kind.clone(), |
| 1820 | 1866 | external: false, |
| 1821 | 1867 | }, |
@@ -2502,7 +2548,7 @@ fn lower_unit( |
| 2502 | 2548 | let ck = if let Some(&len_slot) = hidden_len_addrs.get(pname) { |
| 2503 | 2549 | CharKind::AssumedLen { len_addr: len_slot } |
| 2504 | 2550 | } else { |
| 2505 | | - arg_char_kind_from_decls(pname, decls) |
| 2551 | + arg_char_kind_from_decls(pname, decls, st) |
| 2506 | 2552 | }; |
| 2507 | 2553 | let info = LocalInfo { |
| 2508 | 2554 | addr: slot, |
@@ -2525,7 +2571,8 @@ fn lower_unit( |
| 2525 | 2571 | if *is_value || hidden_len_addrs.contains_key(pname) { |
| 2526 | 2572 | continue; |
| 2527 | 2573 | } |
| 2528 | | - let Some(len_expr) = arg_runtime_char_len_expr_from_decls(pname, decls) else { |
| 2574 | + let Some(len_expr) = arg_runtime_char_len_expr_from_decls(pname, decls, st) |
| 2575 | + else { |
| 2529 | 2576 | continue; |
| 2530 | 2577 | }; |
| 2531 | 2578 | let len_raw = lower_expr(&mut b, &ctx.locals, &len_expr, ctx.st); |
@@ -2663,18 +2710,22 @@ fn lower_unit( |
| 2663 | 2710 | let visible_param_consts = |
| 2664 | 2711 | collect_decl_param_consts_with_host(decls, host_param_consts); |
| 2665 | 2712 | |
| 2666 | | - // Audit6 BLOCKING-1: functions with allocatable result use the |
| 2667 | | - // sret (hidden-output-param) convention. The caller allocas a |
| 2668 | | - // 384-byte descriptor and passes its address as param 0; the |
| 2669 | | - // function writes its result into that descriptor and returns |
| 2670 | | - // void. This avoids trying to return 384 bytes "by value". |
| 2671 | | - let is_alloc_return = alloc_return_funcs.contains(&name.to_lowercase()); |
| 2672 | | - |
| 2673 | | - let (func_params, ir_ret_ty) = if is_alloc_return { |
| 2674 | | - // Hidden first param: ptr to caller-provided 384-byte descriptor. |
| 2713 | + // Hidden-result ABI: allocatable arrays use a 384-byte array |
| 2714 | + // descriptor, while deferred-length scalar character results use |
| 2715 | + // a 32-byte string descriptor. In both cases the caller provides |
| 2716 | + // the descriptor storage as param 0 and the callee returns void. |
| 2717 | + let hidden_result_abi = function_hidden_result_abi(name, result, decls); |
| 2718 | + let uses_hidden_result = hidden_result_abi != HiddenResultAbi::None; |
| 2719 | + |
| 2720 | + let (func_params, ir_ret_ty) = if uses_hidden_result { |
| 2721 | + let desc_size = match hidden_result_abi { |
| 2722 | + HiddenResultAbi::ArrayDescriptor => 384, |
| 2723 | + HiddenResultAbi::StringDescriptor => 32, |
| 2724 | + HiddenResultAbi::None => 0, |
| 2725 | + }; |
| 2675 | 2726 | let desc_ptr_ty = IrType::Ptr(Box::new(IrType::Array( |
| 2676 | 2727 | Box::new(IrType::Int(IntWidth::I8)), |
| 2677 | | - 384, |
| 2728 | + desc_size, |
| 2678 | 2729 | ))); |
| 2679 | 2730 | let sret = Param { |
| 2680 | 2731 | name: "_sret".into(), |
@@ -2832,7 +2883,7 @@ fn lower_unit( |
| 2832 | 2883 | char_len_star_params, |
| 2833 | 2884 | contained_host_refs, |
| 2834 | 2885 | ); |
| 2835 | | - ctx.is_alloc_return = is_alloc_return; |
| 2886 | + ctx.is_alloc_return = uses_hidden_result; |
| 2836 | 2887 | let mut pending_globals: Vec<PendingGlobal> = Vec::new(); |
| 2837 | 2888 | let combined_uses: Vec<crate::ast::decl::SpannedDecl> = |
| 2838 | 2889 | host_uses.iter().chain(uses.iter()).cloned().collect(); |
@@ -2893,7 +2944,7 @@ fn lower_unit( |
| 2893 | 2944 | let ck = if let Some(&len_slot) = hidden_len_addrs.get(pname) { |
| 2894 | 2945 | CharKind::AssumedLen { len_addr: len_slot } |
| 2895 | 2946 | } else { |
| 2896 | | - arg_char_kind_from_decls(pname, decls) |
| 2947 | + arg_char_kind_from_decls(pname, decls, st) |
| 2897 | 2948 | }; |
| 2898 | 2949 | ctx.locals.insert( |
| 2899 | 2950 | pname.clone(), |
@@ -2918,7 +2969,8 @@ fn lower_unit( |
| 2918 | 2969 | if *is_value || hidden_len_addrs.contains_key(pname) { |
| 2919 | 2970 | continue; |
| 2920 | 2971 | } |
| 2921 | | - let Some(len_expr) = arg_runtime_char_len_expr_from_decls(pname, decls) else { |
| 2972 | + let Some(len_expr) = arg_runtime_char_len_expr_from_decls(pname, decls, st) |
| 2973 | + else { |
| 2922 | 2974 | continue; |
| 2923 | 2975 | }; |
| 2924 | 2976 | let len_raw = lower_expr(&mut b, &ctx.locals, &len_expr, ctx.st); |
@@ -2941,10 +2993,8 @@ fn lower_unit( |
| 2941 | 2993 | |
| 2942 | 2994 | let result_is_pointer = decl_is_pointer(&result_name, decls); |
| 2943 | 2995 | |
| 2944 | | - if is_alloc_return { |
| 2945 | | - // The sret param (ValueId 0) IS the descriptor address. |
| 2946 | | - // Pre-insert the result variable as an allocatable backed by that |
| 2947 | | - // descriptor so alloc_decls skips it (locals.contains_key → continue). |
| 2996 | + if hidden_result_abi == HiddenResultAbi::ArrayDescriptor { |
| 2997 | + // The hidden first param is the caller-provided array descriptor. |
| 2948 | 2998 | let elem_ty = arg_type_from_decls(&result_name, decls, Some(st)); |
| 2949 | 2999 | ctx.locals.insert( |
| 2950 | 3000 | result_name.clone(), |
@@ -2962,7 +3012,25 @@ fn lower_unit( |
| 2962 | 3012 | runtime_dim_upper: vec![], |
| 2963 | 3013 | }, |
| 2964 | 3014 | ); |
| 2965 | | - // result_addr = None; is_alloc_return = true tells Stmt::Return to emit ret void. |
| 3015 | + } else if hidden_result_abi == HiddenResultAbi::StringDescriptor { |
| 3016 | + // Deferred-length scalar character result: the hidden first |
| 3017 | + // param is a caller-provided StringDescriptor. |
| 3018 | + ctx.locals.insert( |
| 3019 | + result_name.clone(), |
| 3020 | + LocalInfo { |
| 3021 | + addr: ValueId(0), |
| 3022 | + ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 3023 | + dims: vec![], |
| 3024 | + allocatable: true, |
| 3025 | + descriptor_arg: false, |
| 3026 | + by_ref: false, |
| 3027 | + char_kind: CharKind::Deferred, |
| 3028 | + derived_type: None, |
| 3029 | + inline_const: None, |
| 3030 | + is_pointer: result_is_pointer, |
| 3031 | + runtime_dim_upper: vec![], |
| 3032 | + }, |
| 3033 | + ); |
| 2966 | 3034 | } else if result_is_pointer { |
| 2967 | 3035 | let result_addr = b.alloca(ir_ret_ty.clone()); |
| 2968 | 3036 | let zero_byte = b.const_i32(0); |
@@ -3070,7 +3138,7 @@ fn lower_unit( |
| 3070 | 3138 | lower_stmts(&mut b, &mut ctx, body); |
| 3071 | 3139 | |
| 3072 | 3140 | if b.func().block(b.current_block()).terminator.is_none() { |
| 3073 | | - let skip = if is_alloc_return { |
| 3141 | + let skip = if uses_hidden_result { |
| 3074 | 3142 | Some(ValueId(0)) |
| 3075 | 3143 | } else { |
| 3076 | 3144 | None |
@@ -3085,7 +3153,7 @@ fn lower_unit( |
| 3085 | 3153 | Some(ctx.contained_host_refs), |
| 3086 | 3154 | skip, |
| 3087 | 3155 | ); |
| 3088 | | - if is_alloc_return { |
| 3156 | + if uses_hidden_result { |
| 3089 | 3157 | b.ret(None); |
| 3090 | 3158 | } else if derived_type_name_for_result_var(return_type, &result_name, decls) |
| 3091 | 3159 | .is_some() |
@@ -3873,7 +3941,7 @@ fn check_filtered_in_stmt(stmt: &crate::ast::stmt::SpannedStmt, filtered: &HashS |
| 3873 | 3941 | } |
| 3874 | 3942 | |
| 3875 | 3943 | // ---- Memory ---- |
| 3876 | | - Stmt::Allocate { items, opts } | Stmt::Deallocate { items, opts } => { |
| 3944 | + Stmt::Allocate { items, opts, .. } | Stmt::Deallocate { items, opts } => { |
| 3877 | 3945 | for item in items { |
| 3878 | 3946 | check_filtered_in_expr(item, filtered); |
| 3879 | 3947 | } |
@@ -4361,7 +4429,7 @@ fn alloc_decls( |
| 4361 | 4429 | TypeSpec::Character(Some(sel)) => { |
| 4362 | 4430 | match &sel.len { |
| 4363 | 4431 | Some(crate::ast::decl::LenSpec::Expr(e)) => { |
| 4364 | | - eval_const_int_in_scope(e, ¶m_consts) |
| 4432 | + eval_const_int_in_scope_or_any_scope(e, ¶m_consts, st) |
| 4365 | 4433 | } |
| 4366 | 4434 | Some(crate::ast::decl::LenSpec::Star) => None, // assumed |
| 4367 | 4435 | Some(crate::ast::decl::LenSpec::Colon) => None, // deferred |
@@ -4374,7 +4442,8 @@ fn alloc_decls( |
| 4374 | 4442 | let runtime_char_len_expr = match type_spec { |
| 4375 | 4443 | TypeSpec::Character(Some(sel)) => match &sel.len { |
| 4376 | 4444 | Some(crate::ast::decl::LenSpec::Expr(e)) |
| 4377 | | - if eval_const_int_in_scope(e, ¶m_consts).is_none() => |
| 4445 | + if eval_const_int_in_scope_or_any_scope(e, ¶m_consts, st) |
| 4446 | + .is_none() => |
| 4378 | 4447 | { |
| 4379 | 4448 | Some(e) |
| 4380 | 4449 | } |
@@ -4419,17 +4488,39 @@ fn alloc_decls( |
| 4419 | 4488 | // dims is left empty for a deferred-shape pointer; |
| 4420 | 4489 | // the descriptor carries the runtime rank and |
| 4421 | 4490 | // bounds after `=>` binds it to a target. |
| 4491 | + let pointer_elem_ty = if matches!(type_spec, TypeSpec::Character(_)) { |
| 4492 | + match char_len { |
| 4493 | + Some(len) => fixed_char_storage_ir_type(len), |
| 4494 | + None => elem_ty.clone(), |
| 4495 | + } |
| 4496 | + } else if let TypeSpec::Type(ref type_name) = type_spec { |
| 4497 | + derived_storage_ir_type(type_name, type_layouts) |
| 4498 | + .unwrap_or_else(|| elem_ty.clone()) |
| 4499 | + } else { |
| 4500 | + elem_ty.clone() |
| 4501 | + }; |
| 4502 | + let pointer_char_kind = if matches!(type_spec, TypeSpec::Character(_)) { |
| 4503 | + match char_len { |
| 4504 | + Some(len) => CharKind::Fixed(len), |
| 4505 | + None => CharKind::None, |
| 4506 | + } |
| 4507 | + } else { |
| 4508 | + CharKind::None |
| 4509 | + }; |
| 4422 | 4510 | locals.insert( |
| 4423 | 4511 | key, |
| 4424 | 4512 | LocalInfo { |
| 4425 | 4513 | addr, |
| 4426 | | - ty: elem_ty.clone(), |
| 4514 | + ty: pointer_elem_ty, |
| 4427 | 4515 | dims: vec![], |
| 4428 | 4516 | allocatable: true, |
| 4429 | 4517 | descriptor_arg: false, |
| 4430 | 4518 | by_ref: false, |
| 4431 | | - char_kind: CharKind::None, |
| 4432 | | - derived_type: None, |
| 4519 | + char_kind: pointer_char_kind, |
| 4520 | + derived_type: match type_spec { |
| 4521 | + TypeSpec::Type(type_name) => Some(type_name.clone()), |
| 4522 | + _ => None, |
| 4523 | + }, |
| 4433 | 4524 | inline_const: None, |
| 4434 | 4525 | is_pointer: true, |
| 4435 | 4526 | runtime_dim_upper: vec![], |
@@ -4472,8 +4563,11 @@ fn alloc_decls( |
| 4472 | 4563 | continue; |
| 4473 | 4564 | } |
| 4474 | 4565 | } |
| 4475 | | - if is_deferred_char && is_allocatable { |
| 4476 | | - // Deferred-length allocatable character: 32-byte StringDescriptor. |
| 4566 | + if is_deferred_char && is_allocatable && array_spec.is_none() { |
| 4567 | + // Deferred-length allocatable scalar character: |
| 4568 | + // 32-byte StringDescriptor. Deferred-length |
| 4569 | + // allocatable arrays fall through to the general |
| 4570 | + // array-descriptor path below. |
| 4477 | 4571 | let desc_ty = IrType::Array(Box::new(IrType::Int(IntWidth::I8)), 32); |
| 4478 | 4572 | let addr = b.alloca(desc_ty); |
| 4479 | 4573 | let zero = b.const_i32(0); |
@@ -4504,6 +4598,89 @@ fn alloc_decls( |
| 4504 | 4598 | if let Some(specs) = array_spec.filter(|_| !is_allocatable) { |
| 4505 | 4599 | let dims = extract_array_dims(specs, ¶m_consts); |
| 4506 | 4600 | let total_size: i64 = dims.iter().map(|(_, size)| *size).product(); |
| 4601 | + if len == 1 { |
| 4602 | + // `character(len=1)` arrays are byte arrays, not |
| 4603 | + // pointer tables. Keeping them contiguous matches |
| 4604 | + // descriptor-backed `character(kind=c_char)` locals |
| 4605 | + // and lets element stores land in `ptr<i8>` slots. |
| 4606 | + const STACK_THRESHOLD: i64 = 64 * 1024; |
| 4607 | + if total_size >= STACK_THRESHOLD { |
| 4608 | + let desc_ty = |
| 4609 | + IrType::Array(Box::new(IrType::Int(IntWidth::I8)), 384); |
| 4610 | + let addr = b.alloca(desc_ty); |
| 4611 | + let zero = b.const_i32(0); |
| 4612 | + let size384 = b.const_i64(384); |
| 4613 | + b.call( |
| 4614 | + FuncRef::External("memset".into()), |
| 4615 | + vec![addr, zero, size384], |
| 4616 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 4617 | + ); |
| 4618 | + let es = b.const_i64(1); |
| 4619 | + let n = b.const_i64(total_size); |
| 4620 | + b.call( |
| 4621 | + FuncRef::External("afs_allocate_1d".into()), |
| 4622 | + vec![addr, es, n], |
| 4623 | + IrType::Void, |
| 4624 | + ); |
| 4625 | + let space = b.const_i32(b' ' as i32); |
| 4626 | + let base = b.load_typed( |
| 4627 | + addr, |
| 4628 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 4629 | + ); |
| 4630 | + let bytes = b.const_i64(total_size); |
| 4631 | + b.call( |
| 4632 | + FuncRef::External("memset".into()), |
| 4633 | + vec![base, space, bytes], |
| 4634 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 4635 | + ); |
| 4636 | + locals.insert( |
| 4637 | + key, |
| 4638 | + LocalInfo { |
| 4639 | + addr, |
| 4640 | + ty: IrType::Int(IntWidth::I8), |
| 4641 | + dims, |
| 4642 | + allocatable: true, |
| 4643 | + descriptor_arg: false, |
| 4644 | + by_ref: false, |
| 4645 | + char_kind: CharKind::Fixed(1), |
| 4646 | + derived_type: None, |
| 4647 | + inline_const: None, |
| 4648 | + is_pointer: false, |
| 4649 | + runtime_dim_upper: vec![], |
| 4650 | + }, |
| 4651 | + ); |
| 4652 | + } else { |
| 4653 | + let arr_ty = IrType::Array( |
| 4654 | + Box::new(IrType::Int(IntWidth::I8)), |
| 4655 | + total_size as u64, |
| 4656 | + ); |
| 4657 | + let addr = b.alloca(arr_ty); |
| 4658 | + let space = b.const_i32(b' ' as i32); |
| 4659 | + let bytes = b.const_i64(total_size); |
| 4660 | + b.call( |
| 4661 | + FuncRef::External("memset".into()), |
| 4662 | + vec![addr, space, bytes], |
| 4663 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 4664 | + ); |
| 4665 | + locals.insert( |
| 4666 | + key, |
| 4667 | + LocalInfo { |
| 4668 | + addr, |
| 4669 | + ty: IrType::Int(IntWidth::I8), |
| 4670 | + dims, |
| 4671 | + allocatable: false, |
| 4672 | + descriptor_arg: false, |
| 4673 | + by_ref: false, |
| 4674 | + char_kind: CharKind::Fixed(1), |
| 4675 | + derived_type: None, |
| 4676 | + inline_const: None, |
| 4677 | + is_pointer: false, |
| 4678 | + runtime_dim_upper: vec![], |
| 4679 | + }, |
| 4680 | + ); |
| 4681 | + } |
| 4682 | + continue; |
| 4683 | + } |
| 4507 | 4684 | let table_ty = IrType::Array( |
| 4508 | 4685 | Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))), |
| 4509 | 4686 | total_size as u64, |
@@ -4663,6 +4840,17 @@ fn alloc_decls( |
| 4663 | 4840 | vec![addr, zero, size], |
| 4664 | 4841 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 4665 | 4842 | ); |
| 4843 | + let alloc_elem_ty = if matches!(type_spec, TypeSpec::Character(_)) { |
| 4844 | + match char_len { |
| 4845 | + Some(len) => fixed_char_storage_ir_type(len), |
| 4846 | + None => elem_ty.clone(), |
| 4847 | + } |
| 4848 | + } else if let TypeSpec::Type(ref type_name) = type_spec { |
| 4849 | + derived_storage_ir_type(type_name, type_layouts) |
| 4850 | + .unwrap_or_else(|| elem_ty.clone()) |
| 4851 | + } else { |
| 4852 | + elem_ty.clone() |
| 4853 | + }; |
| 4666 | 4854 | let char_kind = match char_len { |
| 4667 | 4855 | Some(len) if array_spec.is_some() => CharKind::Fixed(len), |
| 4668 | 4856 | _ => CharKind::None, |
@@ -4671,7 +4859,7 @@ fn alloc_decls( |
| 4671 | 4859 | key, |
| 4672 | 4860 | LocalInfo { |
| 4673 | 4861 | addr, |
| 4674 | | - ty: elem_ty.clone(), |
| 4862 | + ty: alloc_elem_ty, |
| 4675 | 4863 | dims: vec![], |
| 4676 | 4864 | allocatable: true, |
| 4677 | 4865 | descriptor_arg: false, |
@@ -5323,6 +5511,100 @@ fn eval_const_int_in_scope( |
| 5323 | 5511 | } |
| 5324 | 5512 | } |
| 5325 | 5513 | |
| 5514 | +fn eval_const_scalar_with_any_scope( |
| 5515 | + expr: &crate::ast::expr::SpannedExpr, |
| 5516 | + param_consts: &HashMap<String, ConstScalar>, |
| 5517 | + st: &SymbolTable, |
| 5518 | +) -> Option<ConstScalar> { |
| 5519 | + if let Some(value) = eval_const_scalar(expr, param_consts) { |
| 5520 | + return Some(value); |
| 5521 | + } |
| 5522 | + match &expr.node { |
| 5523 | + Expr::Name { name } => { |
| 5524 | + let key = name.to_ascii_lowercase(); |
| 5525 | + param_consts.get(&key).copied().or_else(|| { |
| 5526 | + st.find_symbol_any_scope(&key) |
| 5527 | + .and_then(|sym| sym.const_value.map(|v| ConstScalar::Int(v as i128))) |
| 5528 | + }) |
| 5529 | + } |
| 5530 | + Expr::ParenExpr { inner } => eval_const_scalar_with_any_scope(inner, param_consts, st), |
| 5531 | + Expr::UnaryOp { |
| 5532 | + op: UnaryOp::Minus, |
| 5533 | + operand, |
| 5534 | + } => { |
| 5535 | + let value = eval_const_scalar_with_any_scope(operand, param_consts, st)?; |
| 5536 | + Some(match value { |
| 5537 | + ConstScalar::Int(i) => ConstScalar::Int(-i), |
| 5538 | + ConstScalar::Float(f) => ConstScalar::Float(-f), |
| 5539 | + }) |
| 5540 | + } |
| 5541 | + Expr::BinaryOp { op, left, right } => { |
| 5542 | + let lv = eval_const_scalar_with_any_scope(left, param_consts, st)?; |
| 5543 | + let rv = eval_const_scalar_with_any_scope(right, param_consts, st)?; |
| 5544 | + let promote_float = |
| 5545 | + matches!(lv, ConstScalar::Float(_)) || matches!(rv, ConstScalar::Float(_)); |
| 5546 | + if promote_float { |
| 5547 | + let l = lv.to_float(); |
| 5548 | + let r = rv.to_float(); |
| 5549 | + match op { |
| 5550 | + BinaryOp::Add => Some(ConstScalar::Float(l + r)), |
| 5551 | + BinaryOp::Sub => Some(ConstScalar::Float(l - r)), |
| 5552 | + BinaryOp::Mul => Some(ConstScalar::Float(l * r)), |
| 5553 | + BinaryOp::Div => { |
| 5554 | + if r == 0.0 { |
| 5555 | + None |
| 5556 | + } else { |
| 5557 | + Some(ConstScalar::Float(l / r)) |
| 5558 | + } |
| 5559 | + } |
| 5560 | + BinaryOp::Pow => Some(ConstScalar::Float(l.powf(r))), |
| 5561 | + _ => None, |
| 5562 | + } |
| 5563 | + } else { |
| 5564 | + let (ConstScalar::Int(l), ConstScalar::Int(r)) = (lv, rv) else { |
| 5565 | + return None; |
| 5566 | + }; |
| 5567 | + match op { |
| 5568 | + BinaryOp::Add => Some(ConstScalar::Int(l.wrapping_add(r))), |
| 5569 | + BinaryOp::Sub => Some(ConstScalar::Int(l.wrapping_sub(r))), |
| 5570 | + BinaryOp::Mul => Some(ConstScalar::Int(l.wrapping_mul(r))), |
| 5571 | + BinaryOp::Div => { |
| 5572 | + if r == 0 { |
| 5573 | + None |
| 5574 | + } else { |
| 5575 | + Some(ConstScalar::Int(l / r)) |
| 5576 | + } |
| 5577 | + } |
| 5578 | + BinaryOp::Pow => { |
| 5579 | + if r < 0 { |
| 5580 | + None |
| 5581 | + } else { |
| 5582 | + let mut acc: i128 = 1; |
| 5583 | + for _ in 0..(r as usize) { |
| 5584 | + acc = acc.wrapping_mul(l); |
| 5585 | + } |
| 5586 | + Some(ConstScalar::Int(acc)) |
| 5587 | + } |
| 5588 | + } |
| 5589 | + _ => None, |
| 5590 | + } |
| 5591 | + } |
| 5592 | + } |
| 5593 | + _ => None, |
| 5594 | + } |
| 5595 | +} |
| 5596 | + |
| 5597 | +fn eval_const_int_in_scope_or_any_scope( |
| 5598 | + expr: &crate::ast::expr::SpannedExpr, |
| 5599 | + param_consts: &HashMap<String, ConstScalar>, |
| 5600 | + st: &SymbolTable, |
| 5601 | +) -> Option<i64> { |
| 5602 | + match eval_const_scalar_with_any_scope(expr, param_consts, st)? { |
| 5603 | + ConstScalar::Int(v) => i64::try_from(v).ok(), |
| 5604 | + ConstScalar::Float(_) => None, |
| 5605 | + } |
| 5606 | +} |
| 5607 | + |
| 5326 | 5608 | /// Resolve the raw data pointer and declared length for a character argument expression. |
| 5327 | 5609 | /// Returns `None` if the argument is not a recognized fixed-length character. |
| 5328 | 5610 | /// Build (ptr, len) for a substring `base_ptr(start:end)` per F2018 7.4.4.2. |
@@ -5399,6 +5681,19 @@ fn descriptor_elem_size(b: &mut FuncBuilder, desc: ValueId) -> ValueId { |
| 5399 | 5681 | b.load_typed(ptr, IrType::Int(IntWidth::I64)) |
| 5400 | 5682 | } |
| 5401 | 5683 | |
| 5684 | +fn descriptor_backed_runtime_char_array(info: &LocalInfo) -> bool { |
| 5685 | + local_uses_array_descriptor(info) |
| 5686 | + && matches!( |
| 5687 | + info.ty, |
| 5688 | + IrType::Ptr(ref inner) if matches!(inner.as_ref(), IrType::Int(IntWidth::I8)) |
| 5689 | + ) |
| 5690 | + && (!info.dims.is_empty() |
| 5691 | + || info.descriptor_arg |
| 5692 | + || (info.allocatable |
| 5693 | + && info.char_kind == CharKind::None |
| 5694 | + && local_fixed_char_allocatable_scalar_len(info).is_none())) |
| 5695 | +} |
| 5696 | + |
| 5402 | 5697 | fn char_array_element_ptr_and_len( |
| 5403 | 5698 | b: &mut FuncBuilder, |
| 5404 | 5699 | locals: &HashMap<String, LocalInfo>, |
@@ -5406,7 +5701,10 @@ fn char_array_element_ptr_and_len( |
| 5406 | 5701 | args: &[crate::ast::expr::Argument], |
| 5407 | 5702 | st: &SymbolTable, |
| 5408 | 5703 | ) -> Option<(ValueId, ValueId)> { |
| 5409 | | - if info.char_kind == CharKind::None || args.is_empty() { |
| 5704 | + if args.is_empty() { |
| 5705 | + return None; |
| 5706 | + } |
| 5707 | + if info.char_kind == CharKind::None && !descriptor_backed_runtime_char_array(info) { |
| 5410 | 5708 | return None; |
| 5411 | 5709 | } |
| 5412 | 5710 | let idx64 = compute_flat_elem_offset(b, locals, info, args, st); |
@@ -5417,7 +5715,12 @@ fn char_array_element_ptr_and_len( |
| 5417 | 5715 | let desc = array_descriptor_addr(b, info); |
| 5418 | 5716 | descriptor_elem_size(b, desc) |
| 5419 | 5717 | } |
| 5420 | | - CharKind::Deferred | CharKind::None => return None, |
| 5718 | + CharKind::Deferred => return None, |
| 5719 | + CharKind::None if descriptor_backed_runtime_char_array(info) => { |
| 5720 | + let desc = array_descriptor_addr(b, info); |
| 5721 | + descriptor_elem_size(b, desc) |
| 5722 | + } |
| 5723 | + CharKind::None => return None, |
| 5421 | 5724 | }; |
| 5422 | 5725 | if !local_uses_array_descriptor(info) && !info.by_ref { |
| 5423 | 5726 | let slot_ptr = b.gep( |
@@ -5520,7 +5823,13 @@ fn char_addr_and_runtime_len( |
| 5520 | 5823 | Some((ptr, len)) |
| 5521 | 5824 | } |
| 5522 | 5825 | CharKind::None => { |
| 5523 | | - if info.by_ref |
| 5826 | + if let Some(len) = local_fixed_char_allocatable_scalar_len(info) { |
| 5827 | + let desc = array_descriptor_addr(b, info); |
| 5828 | + let base = b.load_typed(desc, IrType::Ptr(Box::new(info.ty.clone()))); |
| 5829 | + let zero = b.const_i64(0); |
| 5830 | + let ptr = b.gep(base, vec![zero], IrType::Int(IntWidth::I8)); |
| 5831 | + Some((ptr, b.const_i64(len))) |
| 5832 | + } else if info.by_ref |
| 5524 | 5833 | && matches!( |
| 5525 | 5834 | info.ty, |
| 5526 | 5835 | IrType::Ptr(ref inner) if matches!(inner.as_ref(), IrType::Int(IntWidth::I8)) |
@@ -5588,7 +5897,8 @@ fn actual_char_arg_runtime_len( |
| 5588 | 5897 | Expr::FunctionCall { callee, args } => { |
| 5589 | 5898 | if let Expr::Name { name } = &callee.node { |
| 5590 | 5899 | if let Some(info) = locals.get(&name.to_lowercase()) { |
| 5591 | | - if let Some((_ptr, len)) = char_array_element_ptr_and_len(b, locals, info, args, st) |
| 5900 | + if let Some((_ptr, len)) = |
| 5901 | + char_array_element_ptr_and_len(b, locals, info, args, st) |
| 5592 | 5902 | { |
| 5593 | 5903 | return Some(len); |
| 5594 | 5904 | } |
@@ -6753,11 +7063,7 @@ fn lower_intrinsic(b: &mut FuncBuilder, name: &str, args: &[ValueId]) -> Option< |
| 6753 | 7063 | )), |
| 6754 | 7064 | |
| 6755 | 7065 | // ---- iso_c_binding functions ---- |
| 6756 | | - "c_loc" => { |
| 6757 | | - // c_loc(x) — return address of x. The arg is already passed by reference, |
| 6758 | | - // so the arg value IS the address. |
| 6759 | | - args.first().copied() |
| 6760 | | - } |
| 7066 | + "c_loc" => None, |
| 6761 | 7067 | "c_sizeof" => { |
| 6762 | 7068 | // c_sizeof(x) — return byte size of x's C representation. |
| 6763 | 7069 | if let Some(arg) = args.first() { |
@@ -7306,44 +7612,58 @@ fn resolved_symbol_call_target( |
| 7306 | 7612 | (fallback_name.to_string(), key.to_string()) |
| 7307 | 7613 | } |
| 7308 | 7614 | |
| 7309 | | -fn lower_alloc_return_call_into_desc( |
| 7615 | +fn emit_named_function_call( |
| 7310 | 7616 | b: &mut FuncBuilder, |
| 7311 | | - ctx: &LowerCtx, |
| 7312 | | - desc_addr: ValueId, |
| 7617 | + locals: &HashMap<String, LocalInfo>, |
| 7618 | + st: &SymbolTable, |
| 7619 | + type_layouts: Option<&crate::sema::type_layout::TypeLayoutRegistry>, |
| 7620 | + internal_funcs: Option<&HashMap<String, u32>>, |
| 7621 | + contained_host_refs: Option<&HashMap<String, Vec<String>>>, |
| 7622 | + descriptor_params: Option<&HashMap<String, Vec<bool>>>, |
| 7313 | 7623 | callee_name: &str, |
| 7314 | 7624 | args: &[crate::ast::expr::Argument], |
| 7315 | | -) { |
| 7625 | + hidden_result: Option<ValueId>, |
| 7626 | + ret_ty: IrType, |
| 7627 | +) -> ValueId { |
| 7316 | 7628 | let key = callee_name.to_lowercase(); |
| 7317 | | - let reordered = reorder_args_by_keyword(args, &key, ctx.st); |
| 7629 | + let reordered = reorder_args_by_keyword(args, &key, st); |
| 7318 | 7630 | let args: &[crate::ast::expr::Argument] = &reordered; |
| 7319 | 7631 | |
| 7320 | 7632 | let intrinsic_arg_vals: Vec<ValueId> = args |
| 7321 | 7633 | .iter() |
| 7322 | 7634 | .map(|a| match &a.value { |
| 7323 | | - crate::ast::expr::SectionSubscript::Element(e) => lower_expr_ctx(b, ctx, e), |
| 7635 | + crate::ast::expr::SectionSubscript::Element(e) => lower_expr_full( |
| 7636 | + b, |
| 7637 | + locals, |
| 7638 | + e, |
| 7639 | + st, |
| 7640 | + type_layouts, |
| 7641 | + internal_funcs, |
| 7642 | + contained_host_refs, |
| 7643 | + descriptor_params, |
| 7644 | + ), |
| 7324 | 7645 | _ => b.const_i32(0), |
| 7325 | 7646 | }) |
| 7326 | 7647 | .collect(); |
| 7327 | 7648 | |
| 7328 | | - let resolved_name = match resolve_generic_call(ctx.st, b, &key, &intrinsic_arg_vals) { |
| 7649 | + let resolved_name = match resolve_generic_call(st, b, &key, &intrinsic_arg_vals) { |
| 7329 | 7650 | Some(name) => name, |
| 7330 | 7651 | None => callee_name.to_string(), |
| 7331 | 7652 | }; |
| 7332 | 7653 | let resolved_key = resolved_name.to_lowercase(); |
| 7333 | | - let (call_name, callee_key) = |
| 7334 | | - resolved_symbol_call_target(ctx.st, &resolved_key, &resolved_name); |
| 7654 | + let (call_name, callee_key) = resolved_symbol_call_target(st, &resolved_key, &resolved_name); |
| 7335 | 7655 | |
| 7336 | 7656 | let callee_value_args = |
| 7337 | | - callee_value_arg_mask(ctx.st, &callee_key).or_else(|| callee_value_arg_mask(ctx.st, &key)); |
| 7338 | | - let callee_descriptor_args = ctx |
| 7339 | | - .descriptor_params |
| 7340 | | - .get(&callee_key) |
| 7341 | | - .cloned() |
| 7342 | | - .or_else(|| ctx.descriptor_params.get(&key).cloned()); |
| 7343 | | - let callee_string_descriptor_args = callee_string_descriptor_arg_mask(ctx.st, &callee_key) |
| 7344 | | - .or_else(|| callee_string_descriptor_arg_mask(ctx.st, &key)); |
| 7657 | + callee_value_arg_mask(st, &callee_key).or_else(|| callee_value_arg_mask(st, &key)); |
| 7658 | + let callee_descriptor_args = descriptor_params |
| 7659 | + .and_then(|m| m.get(&callee_key).cloned().or_else(|| m.get(&key).cloned())); |
| 7660 | + let callee_string_descriptor_args = callee_string_descriptor_arg_mask(st, &callee_key) |
| 7661 | + .or_else(|| callee_string_descriptor_arg_mask(st, &key)); |
| 7345 | 7662 | |
| 7346 | | - let mut call_args = vec![desc_addr]; |
| 7663 | + let mut call_args = Vec::new(); |
| 7664 | + if let Some(desc) = hidden_result { |
| 7665 | + call_args.push(desc); |
| 7666 | + } |
| 7347 | 7667 | call_args.extend(args.iter().enumerate().map(|(i, a)| { |
| 7348 | 7668 | let is_value = callee_value_args |
| 7349 | 7669 | .as_ref() |
@@ -7360,7 +7680,133 @@ fn lower_alloc_return_call_into_desc( |
| 7360 | 7680 | match &a.value { |
| 7361 | 7681 | crate::ast::expr::SectionSubscript::Element(e) => { |
| 7362 | 7682 | if is_value { |
| 7363 | | - lower_expr_ctx(b, ctx, e) |
| 7683 | + lower_expr_full( |
| 7684 | + b, |
| 7685 | + locals, |
| 7686 | + e, |
| 7687 | + st, |
| 7688 | + type_layouts, |
| 7689 | + internal_funcs, |
| 7690 | + contained_host_refs, |
| 7691 | + descriptor_params, |
| 7692 | + ) |
| 7693 | + } else if wants_descriptor { |
| 7694 | + lower_arg_descriptor(b, locals, e, st) |
| 7695 | + } else if wants_string_descriptor { |
| 7696 | + lower_arg_string_descriptor(b, locals, e, st, type_layouts) |
| 7697 | + } else { |
| 7698 | + lower_arg_by_ref(b, locals, e, st) |
| 7699 | + } |
| 7700 | + } |
| 7701 | + _ => b.const_i32(0), |
| 7702 | + } |
| 7703 | + })); |
| 7704 | + |
| 7705 | + if let Some(cls_flags) = |
| 7706 | + callee_char_len_star_mask(st, &callee_key).or_else(|| callee_char_len_star_mask(st, &key)) |
| 7707 | + { |
| 7708 | + for (i, flag) in cls_flags.iter().enumerate() { |
| 7709 | + if !*flag || i >= args.len() { |
| 7710 | + continue; |
| 7711 | + } |
| 7712 | + if let crate::ast::expr::SectionSubscript::Element(e) = &args[i].value { |
| 7713 | + if let Some((_ptr, len)) = char_addr_and_runtime_len(b, e, locals) { |
| 7714 | + call_args.push(len); |
| 7715 | + } else if let Expr::StringLiteral { value, .. } = &e.node { |
| 7716 | + call_args.push(b.const_i64(value.len() as i64)); |
| 7717 | + } else if expr_is_character_expr(b, locals, e, st, type_layouts) { |
| 7718 | + let (_ptr, len) = lower_string_expr_full( |
| 7719 | + b, |
| 7720 | + locals, |
| 7721 | + e, |
| 7722 | + st, |
| 7723 | + type_layouts, |
| 7724 | + internal_funcs, |
| 7725 | + contained_host_refs, |
| 7726 | + descriptor_params, |
| 7727 | + ); |
| 7728 | + call_args.push(len); |
| 7729 | + } else { |
| 7730 | + call_args.push(b.const_i64(0)); |
| 7731 | + } |
| 7732 | + } else { |
| 7733 | + call_args.push(b.const_i64(0)); |
| 7734 | + } |
| 7735 | + } |
| 7736 | + } |
| 7737 | + |
| 7738 | + let closure_key = if contained_host_refs |
| 7739 | + .map(|m| m.contains_key(&callee_key)) |
| 7740 | + .unwrap_or(false) |
| 7741 | + { |
| 7742 | + &callee_key |
| 7743 | + } else { |
| 7744 | + &key |
| 7745 | + }; |
| 7746 | + append_host_closure_args_raw(b, locals, contained_host_refs, closure_key, &mut call_args); |
| 7747 | + |
| 7748 | + let func_ref = internal_funcs |
| 7749 | + .and_then(|map| map.get(&callee_key).or_else(|| map.get(&key)).copied()) |
| 7750 | + .map(FuncRef::Internal) |
| 7751 | + .unwrap_or_else(|| FuncRef::External(call_name)); |
| 7752 | + b.call(func_ref, call_args, ret_ty) |
| 7753 | +} |
| 7754 | + |
| 7755 | +fn lower_alloc_return_call_into_desc( |
| 7756 | + b: &mut FuncBuilder, |
| 7757 | + ctx: &LowerCtx, |
| 7758 | + desc_addr: ValueId, |
| 7759 | + callee_name: &str, |
| 7760 | + args: &[crate::ast::expr::Argument], |
| 7761 | +) { |
| 7762 | + let key = callee_name.to_lowercase(); |
| 7763 | + let reordered = reorder_args_by_keyword(args, &key, ctx.st); |
| 7764 | + let args: &[crate::ast::expr::Argument] = &reordered; |
| 7765 | + |
| 7766 | + let intrinsic_arg_vals: Vec<ValueId> = args |
| 7767 | + .iter() |
| 7768 | + .map(|a| match &a.value { |
| 7769 | + crate::ast::expr::SectionSubscript::Element(e) => lower_expr_ctx(b, ctx, e), |
| 7770 | + _ => b.const_i32(0), |
| 7771 | + }) |
| 7772 | + .collect(); |
| 7773 | + |
| 7774 | + let resolved_name = match resolve_generic_call(ctx.st, b, &key, &intrinsic_arg_vals) { |
| 7775 | + Some(name) => name, |
| 7776 | + None => callee_name.to_string(), |
| 7777 | + }; |
| 7778 | + let resolved_key = resolved_name.to_lowercase(); |
| 7779 | + let (call_name, callee_key) = |
| 7780 | + resolved_symbol_call_target(ctx.st, &resolved_key, &resolved_name); |
| 7781 | + |
| 7782 | + let callee_value_args = |
| 7783 | + callee_value_arg_mask(ctx.st, &callee_key).or_else(|| callee_value_arg_mask(ctx.st, &key)); |
| 7784 | + let callee_descriptor_args = ctx |
| 7785 | + .descriptor_params |
| 7786 | + .get(&callee_key) |
| 7787 | + .cloned() |
| 7788 | + .or_else(|| ctx.descriptor_params.get(&key).cloned()); |
| 7789 | + let callee_string_descriptor_args = callee_string_descriptor_arg_mask(ctx.st, &callee_key) |
| 7790 | + .or_else(|| callee_string_descriptor_arg_mask(ctx.st, &key)); |
| 7791 | + |
| 7792 | + let mut call_args = vec![desc_addr]; |
| 7793 | + call_args.extend(args.iter().enumerate().map(|(i, a)| { |
| 7794 | + let is_value = callee_value_args |
| 7795 | + .as_ref() |
| 7796 | + .map(|mask| i < mask.len() && mask[i]) |
| 7797 | + .unwrap_or(false); |
| 7798 | + let wants_descriptor = callee_descriptor_args |
| 7799 | + .as_ref() |
| 7800 | + .map(|mask| i < mask.len() && mask[i]) |
| 7801 | + .unwrap_or(false); |
| 7802 | + let wants_string_descriptor = callee_string_descriptor_args |
| 7803 | + .as_ref() |
| 7804 | + .map(|mask| i < mask.len() && mask[i]) |
| 7805 | + .unwrap_or(false); |
| 7806 | + match &a.value { |
| 7807 | + crate::ast::expr::SectionSubscript::Element(e) => { |
| 7808 | + if is_value { |
| 7809 | + lower_expr_ctx(b, ctx, e) |
| 7364 | 7810 | } else if wants_string_descriptor { |
| 7365 | 7811 | lower_arg_string_descriptor(b, &ctx.locals, e, ctx.st, Some(ctx.type_layouts)) |
| 7366 | 7812 | } else if wants_descriptor { |
@@ -7799,14 +8245,125 @@ fn lower_intrinsic_subroutine( |
| 7799 | 8245 | // ---- iso_c_binding subroutines ---- |
| 7800 | 8246 | "c_f_pointer" => { |
| 7801 | 8247 | // call c_f_pointer(cptr, fptr [, shape]) |
| 7802 | | - // Store the C pointer value into the Fortran pointer variable. |
| 7803 | | - // cptr is i64 (c_ptr), fptr is a by-reference Fortran pointer. |
| 7804 | | - // Convert i64 address to pointer type before storing. |
| 7805 | | - let cptr = nth_arg_val(b, ctx, args, 0, 0); |
| 8248 | + // |
| 8249 | + // Scalar pointers store the raw address directly into the |
| 8250 | + // pointer slot. Array pointers are descriptor-backed in |
| 8251 | + // armfortas, so we must populate the 384-byte descriptor |
| 8252 | + // with base_addr/elem_size/rank/bounds instead of |
| 8253 | + // treating the second argument like a plain Ptr<T>. |
| 8254 | + let raw_cptr = nth_arg_val(b, ctx, args, 0, 0); |
| 8255 | + let cptr = match b.func().value_type(raw_cptr) { |
| 8256 | + Some(IrType::Int(IntWidth::I64)) => raw_cptr, |
| 8257 | + _ => b.int_extend(raw_cptr, IntWidth::I64, false), |
| 8258 | + }; |
| 8259 | + |
| 8260 | + let target_expr = args.get(1).and_then(|arg| { |
| 8261 | + if let crate::ast::expr::SectionSubscript::Element(expr) = &arg.value { |
| 8262 | + Some(expr) |
| 8263 | + } else { |
| 8264 | + None |
| 8265 | + } |
| 8266 | + }); |
| 8267 | + if let Some(expr) = target_expr { |
| 8268 | + if let Some((target_addr, elem_ty, descriptor_backed)) = |
| 8269 | + c_f_pointer_target(b, ctx, expr) |
| 8270 | + { |
| 8271 | + if descriptor_backed { |
| 8272 | + let zero32 = b.const_i32(0); |
| 8273 | + let sz384 = b.const_i64(384); |
| 8274 | + b.call( |
| 8275 | + FuncRef::External("memset".into()), |
| 8276 | + vec![target_addr, zero32, sz384], |
| 8277 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 8278 | + ); |
| 8279 | + |
| 8280 | + let base_ptr = b.int_to_ptr(cptr, elem_ty.clone()); |
| 8281 | + store_byte_aggregate_field( |
| 8282 | + b, |
| 8283 | + target_addr, |
| 8284 | + 0, |
| 8285 | + IrType::Ptr(Box::new(elem_ty.clone())), |
| 8286 | + base_ptr, |
| 8287 | + ); |
| 8288 | + let elem_size = b.const_i64(ir_scalar_byte_size(&elem_ty)); |
| 8289 | + store_byte_aggregate_field( |
| 8290 | + b, |
| 8291 | + target_addr, |
| 8292 | + 8, |
| 8293 | + IrType::Int(IntWidth::I64), |
| 8294 | + elem_size, |
| 8295 | + ); |
| 8296 | + |
| 8297 | + let shape_vals = c_f_pointer_shape_values(args); |
| 8298 | + let rank = shape_vals.map_or(0, |vals| vals.len()); |
| 8299 | + let rank_val = b.const_i32(rank as i32); |
| 8300 | + store_byte_aggregate_field( |
| 8301 | + b, |
| 8302 | + target_addr, |
| 8303 | + 16, |
| 8304 | + IrType::Int(IntWidth::I32), |
| 8305 | + rank_val, |
| 8306 | + ); |
| 8307 | + |
| 8308 | + let null_cptr = b.const_i64(0); |
| 8309 | + let is_associated = b.icmp(CmpOp::Ne, cptr, null_cptr); |
| 8310 | + let assoc_flag = b.const_i32(2); |
| 8311 | + let disassoc_flag = b.const_i32(0); |
| 8312 | + let flags = b.select(is_associated, assoc_flag, disassoc_flag); |
| 8313 | + store_byte_aggregate_field( |
| 8314 | + b, |
| 8315 | + target_addr, |
| 8316 | + 20, |
| 8317 | + IrType::Int(IntWidth::I32), |
| 8318 | + flags, |
| 8319 | + ); |
| 8320 | + |
| 8321 | + if let Some(values) = shape_vals { |
| 8322 | + for (i, value) in values.iter().enumerate() { |
| 8323 | + let crate::ast::expr::AcValue::Expr(extent_expr) = value else { |
| 8324 | + continue; |
| 8325 | + }; |
| 8326 | + let raw_extent = lower_expr_ctx(b, ctx, extent_expr); |
| 8327 | + let extent = match b.func().value_type(raw_extent) { |
| 8328 | + Some(IrType::Int(IntWidth::I64)) => raw_extent, |
| 8329 | + _ => b.int_extend(raw_extent, IntWidth::I64, true), |
| 8330 | + }; |
| 8331 | + let base = 24 + (i as i64) * 24; |
| 8332 | + let one64 = b.const_i64(1); |
| 8333 | + store_byte_aggregate_field( |
| 8334 | + b, |
| 8335 | + target_addr, |
| 8336 | + base, |
| 8337 | + IrType::Int(IntWidth::I64), |
| 8338 | + one64, |
| 8339 | + ); |
| 8340 | + store_byte_aggregate_field( |
| 8341 | + b, |
| 8342 | + target_addr, |
| 8343 | + base + 8, |
| 8344 | + IrType::Int(IntWidth::I64), |
| 8345 | + extent, |
| 8346 | + ); |
| 8347 | + let stride64 = b.const_i64(1); |
| 8348 | + store_byte_aggregate_field( |
| 8349 | + b, |
| 8350 | + target_addr, |
| 8351 | + base + 16, |
| 8352 | + IrType::Int(IntWidth::I64), |
| 8353 | + stride64, |
| 8354 | + ); |
| 8355 | + } |
| 8356 | + } |
| 8357 | + return true; |
| 8358 | + } |
| 8359 | + |
| 8360 | + let ptr_val = b.int_to_ptr(cptr, elem_ty); |
| 8361 | + b.store(ptr_val, target_addr); |
| 8362 | + return true; |
| 8363 | + } |
| 8364 | + } |
| 8365 | + |
| 7806 | 8366 | let fptr = nth_arg_ref(b, ctx, args, 1); |
| 7807 | | - // fptr is Ptr<Ptr<T>> (by-ref pointer variable). |
| 7808 | | - // We need to store a Ptr<T> into it. Extract T from |
| 7809 | | - // the double-pointer type and build Ptr<T> via int_to_ptr. |
| 7810 | 8367 | let inner_pointee = b |
| 7811 | 8368 | .func() |
| 7812 | 8369 | .value_type(fptr) |
@@ -7833,6 +8390,55 @@ fn lower_intrinsic_subroutine( |
| 7833 | 8390 | |
| 7834 | 8391 | /// Look up a dummy argument's declared type from the declaration list. |
| 7835 | 8392 | /// Returns the IR type for the argument, defaulting to I32 if not found. |
| 8393 | +fn c_f_pointer_target( |
| 8394 | + b: &mut FuncBuilder, |
| 8395 | + ctx: &LowerCtx, |
| 8396 | + expr: &crate::ast::expr::SpannedExpr, |
| 8397 | +) -> Option<(ValueId, IrType, bool)> { |
| 8398 | + match &expr.node { |
| 8399 | + Expr::Name { name } => { |
| 8400 | + let info = ctx.locals.get(&name.to_lowercase())?; |
| 8401 | + if !info.is_pointer { |
| 8402 | + return None; |
| 8403 | + } |
| 8404 | + if local_uses_array_descriptor(info) { |
| 8405 | + Some((array_descriptor_addr(b, info), info.ty.clone(), true)) |
| 8406 | + } else { |
| 8407 | + let addr = if info.by_ref { |
| 8408 | + b.load(info.addr) |
| 8409 | + } else { |
| 8410 | + info.addr |
| 8411 | + }; |
| 8412 | + Some((addr, info.ty.clone(), false)) |
| 8413 | + } |
| 8414 | + } |
| 8415 | + Expr::ComponentAccess { .. } => { |
| 8416 | + let (field_ptr, field) = |
| 8417 | + resolve_component_field_access(b, &ctx.locals, expr, ctx.st, ctx.type_layouts)?; |
| 8418 | + if !field.pointer { |
| 8419 | + return None; |
| 8420 | + } |
| 8421 | + let elem_ty = type_info_to_storage_ir_type(&field.type_info, ctx.type_layouts); |
| 8422 | + Some((field_ptr, elem_ty, field.size == 384)) |
| 8423 | + } |
| 8424 | + _ => None, |
| 8425 | + } |
| 8426 | +} |
| 8427 | + |
| 8428 | +fn c_f_pointer_shape_values( |
| 8429 | + args: &[crate::ast::expr::Argument], |
| 8430 | +) -> Option<&[crate::ast::expr::AcValue]> { |
| 8431 | + let arg = args.get(2)?; |
| 8432 | + let crate::ast::expr::SectionSubscript::Element(expr) = &arg.value else { |
| 8433 | + return None; |
| 8434 | + }; |
| 8435 | + if let Expr::ArrayConstructor { values, .. } = &expr.node { |
| 8436 | + Some(values.as_slice()) |
| 8437 | + } else { |
| 8438 | + None |
| 8439 | + } |
| 8440 | +} |
| 8441 | + |
| 7836 | 8442 | /// Determine the CharKind for a dummy argument from its declaration. |
| 7837 | 8443 | /// |
| 7838 | 8444 | /// Returns `CharKind::Fixed(n)` if the declaration is |
@@ -7840,7 +8446,11 @@ fn lower_intrinsic_subroutine( |
| 7840 | 8446 | /// dummies (`character(len=*)`) currently return `CharKind::None` |
| 7841 | 8447 | /// because the hidden-length ABI parameter that would supply the |
| 7842 | 8448 | /// runtime length is not yet implemented. |
| 7843 | | -fn arg_char_kind_from_decls(arg_name: &str, decls: &[crate::ast::decl::SpannedDecl]) -> CharKind { |
| 8449 | +fn arg_char_kind_from_decls( |
| 8450 | + arg_name: &str, |
| 8451 | + decls: &[crate::ast::decl::SpannedDecl], |
| 8452 | + st: &SymbolTable, |
| 8453 | +) -> CharKind { |
| 7844 | 8454 | let key = arg_name.to_lowercase(); |
| 7845 | 8455 | for decl in decls { |
| 7846 | 8456 | if let Decl::TypeDecl { |
@@ -7859,7 +8469,9 @@ fn arg_char_kind_from_decls(arg_name: &str, decls: &[crate::ast::decl::SpannedDe |
| 7859 | 8469 | return CharKind::Deferred; |
| 7860 | 8470 | } |
| 7861 | 8471 | if let Some(crate::ast::decl::LenSpec::Expr(e)) = &sel.len { |
| 7862 | | - if let Some(n) = eval_const_int_in_scope(e, &HashMap::new()) { |
| 8472 | + if let Some(n) = |
| 8473 | + eval_const_int_in_scope_or_any_scope(e, &HashMap::new(), st) |
| 8474 | + { |
| 7863 | 8475 | return CharKind::Fixed(n); |
| 7864 | 8476 | } |
| 7865 | 8477 | } |
@@ -7878,6 +8490,7 @@ fn arg_char_kind_from_decls(arg_name: &str, decls: &[crate::ast::decl::SpannedDe |
| 7878 | 8490 | fn arg_runtime_char_len_expr_from_decls( |
| 7879 | 8491 | arg_name: &str, |
| 7880 | 8492 | decls: &[crate::ast::decl::SpannedDecl], |
| 8493 | + st: &SymbolTable, |
| 7881 | 8494 | ) -> Option<crate::ast::expr::SpannedExpr> { |
| 7882 | 8495 | let key = arg_name.to_lowercase(); |
| 7883 | 8496 | for decl in decls { |
@@ -7891,7 +8504,9 @@ fn arg_runtime_char_len_expr_from_decls( |
| 7891 | 8504 | if entity.name.to_lowercase() == key { |
| 7892 | 8505 | if let TypeSpec::Character(Some(sel)) = type_spec { |
| 7893 | 8506 | if let Some(crate::ast::decl::LenSpec::Expr(e)) = &sel.len { |
| 7894 | | - if eval_const_int_in_scope(e, &HashMap::new()).is_none() { |
| 8507 | + if eval_const_int_in_scope_or_any_scope(e, &HashMap::new(), st) |
| 8508 | + .is_none() |
| 8509 | + { |
| 7895 | 8510 | return Some(e.clone()); |
| 7896 | 8511 | } |
| 7897 | 8512 | } |
@@ -8421,7 +9036,7 @@ fn build_host_ref_params( |
| 8421 | 9036 | id: pid, |
| 8422 | 9037 | elem_ty, |
| 8423 | 9038 | dims: arg_dims_from_decls(hname, host_decls, &host_visible), |
| 8424 | | - char_kind: arg_char_kind_from_decls(hname, host_decls), |
| 9039 | + char_kind: arg_char_kind_from_decls(hname, host_decls, st), |
| 8425 | 9040 | derived_type: arg_derived_type_name(hname, host_decls), |
| 8426 | 9041 | descriptor_arg, |
| 8427 | 9042 | allocatable: alloc, |
@@ -8686,6 +9301,72 @@ fn callee_return_ir_type(st: &SymbolTable, callee_name: &str) -> Option<IrType> |
| 8686 | 9301 | result_type |
| 8687 | 9302 | } |
| 8688 | 9303 | |
| 9304 | +#[derive(Clone, Copy, PartialEq, Eq)] |
| 9305 | +enum CharacterReturnAbi { |
| 9306 | + Direct(Option<i64>), |
| 9307 | + HiddenDescriptor, |
| 9308 | +} |
| 9309 | + |
| 9310 | +fn callee_character_return_abi(st: &SymbolTable, callee_name: &str) -> Option<CharacterReturnAbi> { |
| 9311 | + use crate::sema::symtab::{SymbolKind, TypeInfo}; |
| 9312 | + |
| 9313 | + let key = callee_name.to_ascii_lowercase(); |
| 9314 | + let sym = st.scopes.iter().find_map(|scope| scope.symbols.get(&key))?; |
| 9315 | + match sym.kind { |
| 9316 | + SymbolKind::Function |
| 9317 | + | SymbolKind::ExternalProc |
| 9318 | + | SymbolKind::IntrinsicProc |
| 9319 | + | SymbolKind::ProcedurePointer => {} |
| 9320 | + _ => return None, |
| 9321 | + } |
| 9322 | + let TypeInfo::Character { len, .. } = sym.type_info.as_ref()? else { |
| 9323 | + return None; |
| 9324 | + }; |
| 9325 | + if sym.attrs.allocatable || sym.attrs.pointer { |
| 9326 | + Some(CharacterReturnAbi::HiddenDescriptor) |
| 9327 | + } else { |
| 9328 | + Some(CharacterReturnAbi::Direct(*len)) |
| 9329 | + } |
| 9330 | +} |
| 9331 | + |
| 9332 | +fn local_fixed_char_allocatable_scalar_len(info: &LocalInfo) -> Option<i64> { |
| 9333 | + if !info.allocatable || !info.dims.is_empty() || info.char_kind != CharKind::None { |
| 9334 | + return None; |
| 9335 | + } |
| 9336 | + match &info.ty { |
| 9337 | + IrType::Array(inner, len) |
| 9338 | + if matches!(inner.as_ref(), IrType::Int(IntWidth::I8)) && *len > 1 => |
| 9339 | + { |
| 9340 | + Some(*len as i64) |
| 9341 | + } |
| 9342 | + _ => None, |
| 9343 | + } |
| 9344 | +} |
| 9345 | + |
| 9346 | +fn local_is_string_scalar(info: &LocalInfo) -> bool { |
| 9347 | + (info.allocatable && info.dims.is_empty() && matches!(info.char_kind, CharKind::Deferred)) |
| 9348 | + || local_fixed_char_allocatable_scalar_len(info).is_some() |
| 9349 | +} |
| 9350 | + |
| 9351 | +fn local_is_array_like(info: &LocalInfo) -> bool { |
| 9352 | + (!info.dims.is_empty() || info.allocatable) && !local_is_string_scalar(info) |
| 9353 | +} |
| 9354 | + |
| 9355 | +fn named_expr_callable_character_return_abi( |
| 9356 | + st: &SymbolTable, |
| 9357 | + locals: &HashMap<String, LocalInfo>, |
| 9358 | + callee_name: &str, |
| 9359 | +) -> Option<CharacterReturnAbi> { |
| 9360 | + let key = callee_name.to_ascii_lowercase(); |
| 9361 | + if locals.contains_key(&key) { |
| 9362 | + let sym = st.scopes.iter().find_map(|scope| scope.symbols.get(&key))?; |
| 9363 | + if sym.kind != crate::sema::symtab::SymbolKind::ProcedurePointer { |
| 9364 | + return None; |
| 9365 | + } |
| 9366 | + } |
| 9367 | + callee_character_return_abi(st, &key) |
| 9368 | +} |
| 9369 | + |
| 8689 | 9370 | /// Check if a dummy argument has the VALUE attribute in its declaration. |
| 8690 | 9371 | fn arg_has_value_attr(arg_name: &str, decls: &[crate::ast::decl::SpannedDecl]) -> bool { |
| 8691 | 9372 | let key = arg_name.to_lowercase(); |
@@ -8844,19 +9525,23 @@ fn lower_string_expr_full( |
| 8844 | 9525 | } |
| 8845 | 9526 | } |
| 8846 | 9527 | CharKind::None => { |
| 8847 | | - // Not a character variable — shouldn't happen but fall back. |
| 8848 | | - let val = lower_expr_full( |
| 8849 | | - b, |
| 8850 | | - locals, |
| 8851 | | - expr, |
| 8852 | | - st, |
| 8853 | | - type_layouts, |
| 8854 | | - internal_funcs, |
| 8855 | | - contained_host_refs, |
| 8856 | | - descriptor_params, |
| 8857 | | - ); |
| 8858 | | - let zero = b.const_i64(0); |
| 8859 | | - (val, zero) |
| 9528 | + if let Some((ptr, len)) = char_addr_and_runtime_len(b, expr, locals) { |
| 9529 | + (ptr, len) |
| 9530 | + } else { |
| 9531 | + // Not a character variable — shouldn't happen but fall back. |
| 9532 | + let val = lower_expr_full( |
| 9533 | + b, |
| 9534 | + locals, |
| 9535 | + expr, |
| 9536 | + st, |
| 9537 | + type_layouts, |
| 9538 | + internal_funcs, |
| 9539 | + contained_host_refs, |
| 9540 | + descriptor_params, |
| 9541 | + ); |
| 9542 | + let zero = b.const_i64(0); |
| 9543 | + (val, zero) |
| 9544 | + } |
| 8860 | 9545 | } |
| 8861 | 9546 | } |
| 8862 | 9547 | } else { |
@@ -9108,7 +9793,8 @@ fn lower_string_expr_full( |
| 9108 | 9793 | if args.len() == 1 { |
| 9109 | 9794 | if let crate::ast::expr::SectionSubscript::Element(_) = &args[0].value { |
| 9110 | 9795 | if let Some(info) = locals.get(&key) { |
| 9111 | | - if info.char_kind != CharKind::None |
| 9796 | + if (info.char_kind != CharKind::None |
| 9797 | + || descriptor_backed_runtime_char_array(info)) |
| 9112 | 9798 | && (!info.dims.is_empty() || local_uses_array_descriptor(info)) |
| 9113 | 9799 | { |
| 9114 | 9800 | if let Some(result) = |
@@ -9133,7 +9819,8 @@ fn lower_string_expr_full( |
| 9133 | 9819 | } = args[1].value |
| 9134 | 9820 | { |
| 9135 | 9821 | if let Some(info) = locals.get(&key) { |
| 9136 | | - if info.char_kind != CharKind::None |
| 9822 | + if (info.char_kind != CharKind::None |
| 9823 | + || descriptor_backed_runtime_char_array(info)) |
| 9137 | 9824 | && (!info.dims.is_empty() || local_uses_array_descriptor(info)) |
| 9138 | 9825 | { |
| 9139 | 9826 | if let Some((elem_ptr, elem_len)) = |
@@ -9160,13 +9847,61 @@ fn lower_string_expr_full( |
| 9160 | 9847 | } |
| 9161 | 9848 | } |
| 9162 | 9849 | } |
| 9850 | + |
| 9851 | + if let Some(ret_abi) = named_expr_callable_character_return_abi(st, locals, &key) { |
| 9852 | + match ret_abi { |
| 9853 | + CharacterReturnAbi::HiddenDescriptor => { |
| 9854 | + let desc = |
| 9855 | + b.alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I8)), 32)); |
| 9856 | + let zero_i32 = b.const_i32(0); |
| 9857 | + let size32 = b.const_i64(32); |
| 9858 | + b.call( |
| 9859 | + FuncRef::External("memset".into()), |
| 9860 | + vec![desc, zero_i32, size32], |
| 9861 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 9862 | + ); |
| 9863 | + emit_named_function_call( |
| 9864 | + b, |
| 9865 | + locals, |
| 9866 | + st, |
| 9867 | + type_layouts, |
| 9868 | + internal_funcs, |
| 9869 | + contained_host_refs, |
| 9870 | + descriptor_params, |
| 9871 | + name, |
| 9872 | + args, |
| 9873 | + Some(desc), |
| 9874 | + IrType::Void, |
| 9875 | + ); |
| 9876 | + return load_string_descriptor_view(b, desc); |
| 9877 | + } |
| 9878 | + CharacterReturnAbi::Direct(len) => { |
| 9879 | + let ptr = emit_named_function_call( |
| 9880 | + b, |
| 9881 | + locals, |
| 9882 | + st, |
| 9883 | + type_layouts, |
| 9884 | + internal_funcs, |
| 9885 | + contained_host_refs, |
| 9886 | + descriptor_params, |
| 9887 | + name, |
| 9888 | + args, |
| 9889 | + None, |
| 9890 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 9891 | + ); |
| 9892 | + return (ptr, b.const_i64(len.unwrap_or(0))); |
| 9893 | + } |
| 9894 | + } |
| 9895 | + } |
| 9163 | 9896 | } |
| 9164 | 9897 | if let Expr::ComponentAccess { .. } = &callee.node { |
| 9165 | 9898 | if let Some(tl) = type_layouts { |
| 9166 | 9899 | if let Some(info) = component_array_local_info(b, locals, callee, st, tl) { |
| 9167 | 9900 | if args.len() == 1 { |
| 9168 | 9901 | if let crate::ast::expr::SectionSubscript::Element(_) = &args[0].value { |
| 9169 | | - if info.char_kind != CharKind::None { |
| 9902 | + if info.char_kind != CharKind::None |
| 9903 | + || descriptor_backed_runtime_char_array(&info) |
| 9904 | + { |
| 9170 | 9905 | if let Some(result) = |
| 9171 | 9906 | char_array_element_ptr_and_len(b, locals, &info, args, st) |
| 9172 | 9907 | { |
@@ -9183,7 +9918,9 @@ fn lower_string_expr_full( |
| 9183 | 9918 | .. |
| 9184 | 9919 | } = args[1].value |
| 9185 | 9920 | { |
| 9186 | | - if info.char_kind != CharKind::None { |
| 9921 | + if info.char_kind != CharKind::None |
| 9922 | + || descriptor_backed_runtime_char_array(&info) |
| 9923 | + { |
| 9187 | 9924 | if let Some((elem_ptr, elem_len)) = |
| 9188 | 9925 | char_array_element_ptr_and_len( |
| 9189 | 9926 | b, |
@@ -9529,6 +10266,14 @@ fn lower_type_spec(ts: &TypeSpec) -> IrType { |
| 9529 | 10266 | lower_type_spec_st(ts, None) |
| 9530 | 10267 | } |
| 9531 | 10268 | |
| 10269 | +fn fixed_char_storage_ir_type(len: i64) -> IrType { |
| 10270 | + if len <= 1 { |
| 10271 | + IrType::Int(IntWidth::I8) |
| 10272 | + } else { |
| 10273 | + IrType::Array(Box::new(IrType::Int(IntWidth::I8)), len as u64) |
| 10274 | + } |
| 10275 | +} |
| 10276 | + |
| 9532 | 10277 | fn lower_type_spec_st(ts: &TypeSpec, st: Option<&SymbolTable>) -> IrType { |
| 9533 | 10278 | match ts { |
| 9534 | 10279 | TypeSpec::Integer(sel) => IrType::int_from_kind(extract_kind_with_st( |
@@ -9648,6 +10393,14 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 9648 | 10393 | return; |
| 9649 | 10394 | } |
| 9650 | 10395 | if let Some(info) = ctx.locals.get(&key).cloned() { |
| 10396 | + if local_is_array_like(&info) |
| 10397 | + && (info.char_kind != CharKind::None |
| 10398 | + || descriptor_backed_runtime_char_array(&info)) |
| 10399 | + && local_uses_array_descriptor(&info) |
| 10400 | + { |
| 10401 | + lower_array_assign(b, ctx, name, &info, value); |
| 10402 | + return; |
| 10403 | + } |
| 9651 | 10404 | match &info.char_kind { |
| 9652 | 10405 | CharKind::Fixed(len) => { |
| 9653 | 10406 | // Fixed-length character assignment: copy with space padding. |
@@ -9698,7 +10451,18 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 9698 | 10451 | ); |
| 9699 | 10452 | } |
| 9700 | 10453 | CharKind::None => { |
| 9701 | | - if !info.dims.is_empty() || info.allocatable { |
| 10454 | + if local_fixed_char_allocatable_scalar_len(&info).is_some() { |
| 10455 | + let (src_ptr, src_len) = lower_string_expr_ctx(b, ctx, value); |
| 10456 | + if let Some((dest_ptr, dest_len)) = |
| 10457 | + char_addr_and_runtime_len(b, target, &ctx.locals) |
| 10458 | + { |
| 10459 | + b.call( |
| 10460 | + FuncRef::External("afs_assign_char_fixed".into()), |
| 10461 | + vec![dest_ptr, dest_len, src_ptr, src_len], |
| 10462 | + IrType::Void, |
| 10463 | + ); |
| 10464 | + } |
| 10465 | + } else if !info.dims.is_empty() || info.allocatable { |
| 9702 | 10466 | if try_lower_elemental_array_assign(b, ctx, name, &info, value) |
| 9703 | 10467 | { |
| 9704 | 10468 | return; |
@@ -9821,7 +10585,12 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 9821 | 10585 | if let Expr::Name { name } = &callee.node { |
| 9822 | 10586 | let akey = name.to_lowercase(); |
| 9823 | 10587 | if let Some(info) = ctx.locals.get(&akey).cloned() { |
| 9824 | | - if lower_1d_section_assign(b, ctx, &info, args, value) { |
| 10588 | + let is_scalar_fixed_alloc_char = |
| 10589 | + local_fixed_char_allocatable_scalar_len(&info).is_some(); |
| 10590 | + if local_is_array_like(&info) |
| 10591 | + && !is_scalar_fixed_alloc_char |
| 10592 | + && lower_1d_section_assign(b, ctx, &info, args, value) |
| 10593 | + { |
| 9825 | 10594 | return; |
| 9826 | 10595 | } |
| 9827 | 10596 | // Substring LHS: s(lo:hi) = rhs where s is a |
@@ -9829,7 +10598,7 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 9829 | 10598 | // pointer+length, get the RHS as (ptr, len), and |
| 9830 | 10599 | // call afs_assign_char_fixed to do the bounded |
| 9831 | 10600 | // copy with space-padding. |
| 9832 | | - if info.char_kind != CharKind::None |
| 10601 | + if (info.char_kind != CharKind::None || is_scalar_fixed_alloc_char) |
| 9833 | 10602 | && info.dims.is_empty() |
| 9834 | 10603 | && args.len() == 1 |
| 9835 | 10604 | && matches!( |
@@ -9864,9 +10633,11 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 9864 | 10633 | ); |
| 9865 | 10634 | } |
| 9866 | 10635 | } |
| 9867 | | - } else if !info.dims.is_empty() || info.allocatable { |
| 10636 | + } else if !is_scalar_fixed_alloc_char && local_is_array_like(&info) { |
| 9868 | 10637 | // Array element assignment: a(i) = val |
| 9869 | | - if matches!(info.char_kind, CharKind::Fixed(_)) { |
| 10638 | + if info.char_kind != CharKind::None |
| 10639 | + || descriptor_backed_runtime_char_array(&info) |
| 10640 | + { |
| 9870 | 10641 | lower_char_array_store( |
| 9871 | 10642 | b, |
| 9872 | 10643 | &ctx.locals, |
@@ -9877,6 +10648,22 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 9877 | 10648 | ); |
| 9878 | 10649 | } else { |
| 9879 | 10650 | let arr_val = lower_expr_ctx(b, ctx, value); |
| 10651 | + if matches!( |
| 10652 | + b.func().value_type(arr_val), |
| 10653 | + Some(IrType::Array(inner, 4096)) |
| 10654 | + if matches!(inner.as_ref(), IrType::Int(IntWidth::I8)) |
| 10655 | + ) && matches!(info.ty, IrType::Ptr(ref inner) if matches!(inner.as_ref(), IrType::Int(IntWidth::I8))) |
| 10656 | + { |
| 10657 | + eprintln!( |
| 10658 | + "DEBUG suspicious array store target={} dims={:?} alloc={} by_ref={} descriptor={} ty={:?}", |
| 10659 | + name, |
| 10660 | + info.dims, |
| 10661 | + info.allocatable, |
| 10662 | + info.by_ref, |
| 10663 | + info.descriptor_arg, |
| 10664 | + info.ty |
| 10665 | + ); |
| 10666 | + } |
| 9880 | 10667 | lower_array_store(b, &ctx.locals, &info, args, arr_val, ctx.st); |
| 9881 | 10668 | } |
| 9882 | 10669 | } |
@@ -9889,11 +10676,15 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 9889 | 10676 | ctx.st, |
| 9890 | 10677 | ctx.type_layouts, |
| 9891 | 10678 | ) { |
| 9892 | | - if lower_1d_section_assign(b, ctx, &info, args, value) { |
| 10679 | + if local_is_array_like(&info) |
| 10680 | + && lower_1d_section_assign(b, ctx, &info, args, value) |
| 10681 | + { |
| 9893 | 10682 | return; |
| 9894 | 10683 | } |
| 9895 | | - if !info.dims.is_empty() || info.allocatable { |
| 9896 | | - if info.char_kind != CharKind::None { |
| 10684 | + if local_is_array_like(&info) { |
| 10685 | + if info.char_kind != CharKind::None |
| 10686 | + || descriptor_backed_runtime_char_array(&info) |
| 10687 | + { |
| 9897 | 10688 | lower_char_array_store( |
| 9898 | 10689 | b, |
| 9899 | 10690 | &ctx.locals, |
@@ -9978,12 +10769,10 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 9978 | 10769 | IrType::Void, |
| 9979 | 10770 | ); |
| 9980 | 10771 | } else if is_deferred_char_component_field(field) { |
| 9981 | | - let (dest_ptr, dest_len) = |
| 9982 | | - load_string_descriptor_view(b, field_ptr); |
| 9983 | 10772 | let (src_ptr, src_len) = lower_string_expr_ctx(b, ctx, value); |
| 9984 | 10773 | b.call( |
| 9985 | | - FuncRef::External("afs_assign_char_fixed".into()), |
| 9986 | | - vec![dest_ptr, dest_len, src_ptr, src_len], |
| 10774 | + FuncRef::External("afs_assign_char_deferred".into()), |
| 10775 | + vec![field_ptr, src_ptr, src_len], |
| 9987 | 10776 | IrType::Void, |
| 9988 | 10777 | ); |
| 9989 | 10778 | } else { |
@@ -10189,12 +10978,35 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 10189 | 10978 | } else { |
| 10190 | 10979 | resolve_subroutine_call_name(ctx.st, b, name, &key, &arg_vals, callee.span) |
| 10191 | 10980 | }; |
| 10192 | | - if let Some(desc_mask) = ctx |
| 10981 | + if let Some(value_mask) = callee_value_arg_mask(ctx.st, &resolved_key) |
| 10982 | + .or_else(|| callee_value_arg_mask(ctx.st, &signature_key)) |
| 10983 | + .or_else(|| callee_value_arg_mask(ctx.st, &key)) |
| 10984 | + { |
| 10985 | + for (i, a) in args.iter().enumerate() { |
| 10986 | + if !value_mask.get(i).copied().unwrap_or(false) { |
| 10987 | + continue; |
| 10988 | + } |
| 10989 | + arg_vals[i] = match &a.value { |
| 10990 | + crate::ast::expr::SectionSubscript::Element(e) => lower_expr_full( |
| 10991 | + b, |
| 10992 | + &ctx.locals, |
| 10993 | + e, |
| 10994 | + ctx.st, |
| 10995 | + Some(ctx.type_layouts), |
| 10996 | + Some(ctx.internal_funcs), |
| 10997 | + Some(ctx.contained_host_refs), |
| 10998 | + Some(ctx.descriptor_params), |
| 10999 | + ), |
| 11000 | + _ => b.const_i32(0), |
| 11001 | + }; |
| 11002 | + } |
| 11003 | + } |
| 11004 | + let desc_mask = ctx |
| 10193 | 11005 | .descriptor_params |
| 10194 | 11006 | .get(&resolved_key) |
| 10195 | 11007 | .or_else(|| ctx.descriptor_params.get(&signature_key)) |
| 10196 | | - .or_else(|| ctx.descriptor_params.get(&key)) |
| 10197 | | - { |
| 11008 | + .or_else(|| ctx.descriptor_params.get(&key)); |
| 11009 | + if let Some(desc_mask) = desc_mask { |
| 10198 | 11010 | for (i, a) in args.iter().enumerate() { |
| 10199 | 11011 | if !desc_mask.get(i).copied().unwrap_or(false) { |
| 10200 | 11012 | continue; |
@@ -10213,6 +11025,12 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 10213 | 11025 | .or_else(|| callee_string_descriptor_arg_mask(ctx.st, &key)) |
| 10214 | 11026 | { |
| 10215 | 11027 | for (i, a) in args.iter().enumerate() { |
| 11028 | + if desc_mask |
| 11029 | + .map(|mask| mask.get(i).copied().unwrap_or(false)) |
| 11030 | + .unwrap_or(false) |
| 11031 | + { |
| 11032 | + continue; |
| 11033 | + } |
| 10216 | 11034 | if !string_desc_mask.get(i).copied().unwrap_or(false) { |
| 10217 | 11035 | continue; |
| 10218 | 11036 | } |
@@ -10758,7 +11576,11 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 10758 | 11576 | b.unreachable(); |
| 10759 | 11577 | } |
| 10760 | 11578 | |
| 10761 | | - Stmt::Allocate { items, opts } => { |
| 11579 | + Stmt::Allocate { |
| 11580 | + type_spec, |
| 11581 | + items, |
| 11582 | + opts, |
| 11583 | + } => { |
| 10762 | 11584 | // Resolve STAT= option: find the user's stat variable address. |
| 10763 | 11585 | // The runtime writes 0 on success or a nonzero error code to this slot. |
| 10764 | 11586 | // If absent, use a private scratch slot (allocation failure aborts). |
@@ -10781,37 +11603,149 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 10781 | 11603 | } else { |
| 10782 | 11604 | b.alloca(IrType::Int(IntWidth::I32)) |
| 10783 | 11605 | } |
| 10784 | | - } else { |
| 10785 | | - b.alloca(IrType::Int(IntWidth::I32)) |
| 11606 | + } else { |
| 11607 | + b.alloca(IrType::Int(IntWidth::I32)) |
| 11608 | + } |
| 11609 | + }; |
| 11610 | + let typed_char_len = |
| 11611 | + typed_allocate_char_len(b, &ctx.locals, type_spec.as_ref(), ctx.st); |
| 11612 | + |
| 11613 | + for item in items { |
| 11614 | + if let Expr::FunctionCall { callee, args } = &item.node { |
| 11615 | + if let Expr::ComponentAccess { .. } = &callee.node { |
| 11616 | + if let Some((field_ptr, field)) = resolve_component_field_access( |
| 11617 | + b, |
| 11618 | + &ctx.locals, |
| 11619 | + callee, |
| 11620 | + ctx.st, |
| 11621 | + ctx.type_layouts, |
| 11622 | + ) { |
| 11623 | + if matches!(field_char_kind(&field), CharKind::Deferred) |
| 11624 | + && field.size == 32 |
| 11625 | + { |
| 11626 | + let Some(len_val) = typed_char_len else { |
| 11627 | + eprintln!( |
| 11628 | + "armfortas: error: {}:{}: deferred-length character ALLOCATE requires a typed length or SOURCE/MOLD support", |
| 11629 | + stmt.span.start.line, stmt.span.start.col |
| 11630 | + ); |
| 11631 | + let _ = std::io::stderr().flush(); |
| 11632 | + std::process::exit(1); |
| 11633 | + }; |
| 11634 | + init_allocated_string_descriptor(b, field_ptr, len_val); |
| 11635 | + continue; |
| 11636 | + } |
| 11637 | + if field.size == 384 && (field.allocatable || field.pointer) { |
| 11638 | + let elem_ty = type_info_to_storage_ir_type( |
| 11639 | + &field.type_info, |
| 11640 | + ctx.type_layouts, |
| 11641 | + ); |
| 11642 | + let elem_size_bytes = descriptor_element_size_bytes(&LocalInfo { |
| 11643 | + addr: field_ptr, |
| 11644 | + ty: elem_ty.clone(), |
| 11645 | + dims: vec![], |
| 11646 | + allocatable: true, |
| 11647 | + descriptor_arg: false, |
| 11648 | + by_ref: false, |
| 11649 | + char_kind: field_char_kind(&field), |
| 11650 | + derived_type: field_derived_type_name(&field), |
| 11651 | + inline_const: None, |
| 11652 | + is_pointer: field.pointer, |
| 11653 | + runtime_dim_upper: vec![], |
| 11654 | + }); |
| 11655 | + let es = if matches!( |
| 11656 | + field.type_info, |
| 11657 | + crate::sema::symtab::TypeInfo::Character { len: None, .. } |
| 11658 | + ) { |
| 11659 | + typed_char_len.unwrap_or_else(|| b.const_i64(elem_size_bytes)) |
| 11660 | + } else { |
| 11661 | + b.const_i64(elem_size_bytes) |
| 11662 | + }; |
| 11663 | + let rank = args.len(); |
| 11664 | + let one_i64 = b.const_i64(1); |
| 11665 | + let dim_buf = if rank == 0 { |
| 11666 | + b.const_i64(0) |
| 11667 | + } else { |
| 11668 | + let dim_buf_bytes = (rank * 24) as u64; |
| 11669 | + let dim_buf = b.alloca(IrType::Array( |
| 11670 | + Box::new(IrType::Int(IntWidth::I8)), |
| 11671 | + dim_buf_bytes, |
| 11672 | + )); |
| 11673 | + for (i, arg) in args.iter().enumerate() { |
| 11674 | + let (lo64, up64) = lower_alloc_bounds(b, ctx, &arg.value); |
| 11675 | + let base = (i * 24) as i64; |
| 11676 | + let off_lo = b.const_i64(base); |
| 11677 | + let off_up = b.const_i64(base + 8); |
| 11678 | + let off_st = b.const_i64(base + 16); |
| 11679 | + let p_lo = |
| 11680 | + b.gep(dim_buf, vec![off_lo], IrType::Int(IntWidth::I8)); |
| 11681 | + let p_up = |
| 11682 | + b.gep(dim_buf, vec![off_up], IrType::Int(IntWidth::I8)); |
| 11683 | + let p_st = |
| 11684 | + b.gep(dim_buf, vec![off_st], IrType::Int(IntWidth::I8)); |
| 11685 | + b.store(lo64, p_lo); |
| 11686 | + b.store(up64, p_up); |
| 11687 | + b.store(one_i64, p_st); |
| 11688 | + } |
| 11689 | + dim_buf |
| 11690 | + }; |
| 11691 | + let rank_val = b.const_i32(rank as i32); |
| 11692 | + b.call( |
| 11693 | + FuncRef::External("afs_allocate_array".into()), |
| 11694 | + vec![field_ptr, es, rank_val, dim_buf, stat_addr], |
| 11695 | + IrType::Void, |
| 11696 | + ); |
| 11697 | + continue; |
| 11698 | + } |
| 11699 | + } |
| 11700 | + } |
| 10786 | 11701 | } |
| 10787 | | - }; |
| 10788 | | - |
| 10789 | | - for item in items { |
| 10790 | | - if let Expr::FunctionCall { callee, args } = &item.node { |
| 10791 | | - let base_name = extract_base_name(callee); |
| 10792 | | - if let Some(name) = base_name { |
| 10793 | | - if let Some(info) = ctx.locals.get(&name.to_lowercase()).cloned() { |
| 10794 | | - let elem_size_bytes: i64 = match &info.ty { |
| 10795 | | - IrType::Int(IntWidth::I64) | IrType::Float(FloatWidth::F64) => 8, |
| 10796 | | - IrType::Int(IntWidth::I128) => 16, |
| 10797 | | - IrType::Int(IntWidth::I32) | IrType::Float(FloatWidth::F32) => 4, |
| 10798 | | - IrType::Bool => 4, |
| 10799 | | - _ => 8, |
| 11702 | + let (base_name, args): (Option<String>, &[crate::ast::expr::Argument]) = |
| 11703 | + match &item.node { |
| 11704 | + Expr::FunctionCall { callee, args } => (extract_base_name(callee), args), |
| 11705 | + Expr::Name { name } => (Some(name.clone()), &[]), |
| 11706 | + _ => (None, &[]), |
| 11707 | + }; |
| 11708 | + if let Some(name) = base_name { |
| 11709 | + if let Some(info) = ctx.locals.get(&name.to_lowercase()).cloned() { |
| 11710 | + if matches!(info.char_kind, CharKind::Deferred) { |
| 11711 | + let Some(len_val) = typed_char_len else { |
| 11712 | + eprintln!( |
| 11713 | + "armfortas: error: {}:{}: deferred-length character ALLOCATE requires a typed length or SOURCE/MOLD support", |
| 11714 | + stmt.span.start.line, stmt.span.start.col |
| 11715 | + ); |
| 11716 | + let _ = std::io::stderr().flush(); |
| 11717 | + std::process::exit(1); |
| 10800 | 11718 | }; |
| 10801 | | - |
| 10802 | | - if info.allocatable { |
| 10803 | | - // Build a stack DimDescriptor[rank] honoring |
| 10804 | | - // each subscript's actual (lower, upper) bounds, |
| 10805 | | - // then call afs_allocate_array. Both 1-D and |
| 10806 | | - // multi-D go through the same path now. |
| 10807 | | - let es = b.const_i64(elem_size_bytes); |
| 10808 | | - let rank = args.len(); |
| 11719 | + let desc = string_descriptor_addr(b, &info); |
| 11720 | + init_allocated_string_descriptor(b, desc, len_val); |
| 11721 | + continue; |
| 11722 | + } |
| 11723 | + let elem_size_bytes = descriptor_element_size_bytes(&info); |
| 11724 | + |
| 11725 | + if info.allocatable || info.descriptor_arg { |
| 11726 | + // Build a stack DimDescriptor[rank] honoring |
| 11727 | + // each subscript's actual (lower, upper) bounds, |
| 11728 | + // then call afs_allocate_array. Descriptor-backed |
| 11729 | + // dummy arrays use the caller-owned descriptor |
| 11730 | + // rather than the local spill slot that holds its |
| 11731 | + // address. Scalar allocatables lower as a rank-0 |
| 11732 | + // descriptor allocation. |
| 11733 | + let es = if descriptor_backed_runtime_char_array(&info) { |
| 11734 | + typed_char_len.unwrap_or_else(|| b.const_i64(elem_size_bytes)) |
| 11735 | + } else { |
| 11736 | + b.const_i64(elem_size_bytes) |
| 11737 | + }; |
| 11738 | + let desc = array_descriptor_addr(b, &info); |
| 11739 | + let rank = args.len(); |
| 11740 | + let one_i64 = b.const_i64(1); |
| 11741 | + let dim_buf = if rank == 0 { |
| 11742 | + b.const_i64(0) |
| 11743 | + } else { |
| 10809 | 11744 | let dim_buf_bytes = (rank * 24) as u64; |
| 10810 | 11745 | let dim_buf = b.alloca(IrType::Array( |
| 10811 | 11746 | Box::new(IrType::Int(IntWidth::I8)), |
| 10812 | 11747 | dim_buf_bytes, |
| 10813 | 11748 | )); |
| 10814 | | - let one_i64 = b.const_i64(1); |
| 10815 | 11749 | for (i, arg) in args.iter().enumerate() { |
| 10816 | 11750 | let (lo64, up64) = lower_alloc_bounds(b, ctx, &arg.value); |
| 10817 | 11751 | let base = (i * 24) as i64; |
@@ -10828,22 +11762,23 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 10828 | 11762 | b.store(up64, p_up); |
| 10829 | 11763 | b.store(one_i64, p_st); |
| 10830 | 11764 | } |
| 10831 | | - let rank_val = b.const_i32(rank as i32); |
| 10832 | | - b.call( |
| 10833 | | - FuncRef::External("afs_allocate_array".into()), |
| 10834 | | - vec![info.addr, es, rank_val, dim_buf, stat_addr], |
| 10835 | | - IrType::Void, |
| 10836 | | - ); |
| 10837 | | - } else { |
| 10838 | | - // Non-allocatable array: old path (shouldn't happen for ALLOCATE). |
| 10839 | | - let size_val = b.const_i32(elem_size_bytes as i32); |
| 10840 | | - let ptr = b.runtime_call( |
| 10841 | | - RuntimeFunc::Allocate, |
| 10842 | | - vec![size_val], |
| 10843 | | - IrType::Ptr(Box::new(info.ty.clone())), |
| 10844 | | - ); |
| 10845 | | - b.store(ptr, info.addr); |
| 10846 | | - } |
| 11765 | + dim_buf |
| 11766 | + }; |
| 11767 | + let rank_val = b.const_i32(rank as i32); |
| 11768 | + b.call( |
| 11769 | + FuncRef::External("afs_allocate_array".into()), |
| 11770 | + vec![desc, es, rank_val, dim_buf, stat_addr], |
| 11771 | + IrType::Void, |
| 11772 | + ); |
| 11773 | + } else { |
| 11774 | + // Non-allocatable array: old path (shouldn't happen for ALLOCATE). |
| 11775 | + let size_val = b.const_i32(elem_size_bytes as i32); |
| 11776 | + let ptr = b.runtime_call( |
| 11777 | + RuntimeFunc::Allocate, |
| 11778 | + vec![size_val], |
| 11779 | + IrType::Ptr(Box::new(info.ty.clone())), |
| 11780 | + ); |
| 11781 | + b.store(ptr, info.addr); |
| 10847 | 11782 | } |
| 10848 | 11783 | } |
| 10849 | 11784 | } |
@@ -10852,16 +11787,44 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 10852 | 11787 | |
| 10853 | 11788 | Stmt::Deallocate { items, .. } => { |
| 10854 | 11789 | for item in items { |
| 11790 | + if let Expr::ComponentAccess { .. } = &item.node { |
| 11791 | + if let Some((field_ptr, field)) = resolve_component_field_access( |
| 11792 | + b, |
| 11793 | + &ctx.locals, |
| 11794 | + item, |
| 11795 | + ctx.st, |
| 11796 | + ctx.type_layouts, |
| 11797 | + ) { |
| 11798 | + if is_deferred_char_component_field(&field) { |
| 11799 | + b.call( |
| 11800 | + FuncRef::External("afs_dealloc_string".into()), |
| 11801 | + vec![field_ptr], |
| 11802 | + IrType::Void, |
| 11803 | + ); |
| 11804 | + continue; |
| 11805 | + } |
| 11806 | + if field.size == 384 && (field.allocatable || field.pointer) { |
| 11807 | + let stat_slot = b.alloca(IrType::Int(IntWidth::I32)); |
| 11808 | + b.call( |
| 11809 | + FuncRef::External("afs_deallocate_array".into()), |
| 11810 | + vec![field_ptr, stat_slot], |
| 11811 | + IrType::Void, |
| 11812 | + ); |
| 11813 | + continue; |
| 11814 | + } |
| 11815 | + } |
| 11816 | + } |
| 10855 | 11817 | let base_name = extract_base_name(item); |
| 10856 | 11818 | if let Some(name) = base_name { |
| 10857 | 11819 | if let Some(info) = ctx.locals.get(&name.to_lowercase()) { |
| 10858 | | - if info.allocatable { |
| 11820 | + if info.allocatable || info.descriptor_arg { |
| 10859 | 11821 | // Pass descriptor address to runtime with null STAT. |
| 10860 | 11822 | // Alloca a dummy STAT to avoid abort on already-deallocated. |
| 10861 | 11823 | let stat_slot = b.alloca(IrType::Int(IntWidth::I32)); |
| 11824 | + let desc = array_descriptor_addr(b, info); |
| 10862 | 11825 | b.call( |
| 10863 | 11826 | FuncRef::External("afs_deallocate_array".into()), |
| 10864 | | - vec![info.addr, stat_slot], |
| 11827 | + vec![desc, stat_slot], |
| 10865 | 11828 | IrType::Void, |
| 10866 | 11829 | ); |
| 10867 | 11830 | } else { |
@@ -11674,6 +12637,47 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { |
| 11674 | 12637 | if !tgt_info.is_pointer { |
| 11675 | 12638 | return; |
| 11676 | 12639 | } |
| 12640 | + if matches!(tgt_info.char_kind, CharKind::Deferred) { |
| 12641 | + let tgt_desc = string_descriptor_addr(b, &tgt_info); |
| 12642 | + if let Expr::FunctionCall { callee, .. } = &value.node { |
| 12643 | + if let Expr::Name { name } = &callee.node { |
| 12644 | + if name.eq_ignore_ascii_case("null") { |
| 12645 | + let zero = b.const_i64(0); |
| 12646 | + let null = b.int_to_ptr(zero, IrType::Int(IntWidth::I8)); |
| 12647 | + store_string_descriptor_view(b, tgt_desc, null, zero); |
| 12648 | + return; |
| 12649 | + } |
| 12650 | + } |
| 12651 | + } |
| 12652 | + if let Some((src_field_ptr, src_field)) = |
| 12653 | + resolve_component_field_access(b, &ctx.locals, value, ctx.st, ctx.type_layouts) |
| 12654 | + { |
| 12655 | + if is_deferred_char_component_field(&src_field) { |
| 12656 | + let (ptr, len) = load_string_descriptor_view(b, src_field_ptr); |
| 12657 | + store_string_descriptor_view(b, tgt_desc, ptr, len); |
| 12658 | + return; |
| 12659 | + } |
| 12660 | + } |
| 12661 | + if let Expr::Name { name: src_name } = &value.node { |
| 12662 | + if let Some(src_info) = ctx.locals.get(&src_name.to_lowercase()) { |
| 12663 | + if matches!(src_info.char_kind, CharKind::Deferred) { |
| 12664 | + let src_desc = string_descriptor_addr(b, src_info); |
| 12665 | + let (ptr, len) = load_string_descriptor_view(b, src_desc); |
| 12666 | + store_string_descriptor_view(b, tgt_desc, ptr, len); |
| 12667 | + return; |
| 12668 | + } |
| 12669 | + } |
| 12670 | + } |
| 12671 | + let (ptr, len) = lower_string_expr_with_layouts( |
| 12672 | + b, |
| 12673 | + &ctx.locals, |
| 12674 | + value, |
| 12675 | + ctx.st, |
| 12676 | + Some(ctx.type_layouts), |
| 12677 | + ); |
| 12678 | + store_string_descriptor_view(b, tgt_desc, ptr, len); |
| 12679 | + return; |
| 12680 | + } |
| 11677 | 12681 | |
| 11678 | 12682 | // Handle section-RHS: pa => ia(lo:hi). The RHS is a |
| 11679 | 12683 | // FunctionCall{Name(arr), [Range(lo,hi)]}. Build a |
@@ -12483,9 +13487,7 @@ fn lower_array_element( |
| 12483 | 13487 | args: &[crate::ast::expr::Argument], |
| 12484 | 13488 | st: &SymbolTable, |
| 12485 | 13489 | ) -> ValueId { |
| 12486 | | - let idx64 = compute_flat_elem_offset(b, locals, info, args, st); |
| 12487 | | - let base = array_base_addr(b, info); |
| 12488 | | - let elem_ptr = b.gep(base, vec![idx64], info.ty.clone()); |
| 13490 | + let elem_ptr = lower_array_element_addr(b, locals, info, args, st); |
| 12489 | 13491 | if info.derived_type.is_some() { |
| 12490 | 13492 | elem_ptr |
| 12491 | 13493 | } else { |
@@ -12493,6 +13495,18 @@ fn lower_array_element( |
| 12493 | 13495 | } |
| 12494 | 13496 | } |
| 12495 | 13497 | |
| 13498 | +fn lower_array_element_addr( |
| 13499 | + b: &mut FuncBuilder, |
| 13500 | + locals: &HashMap<String, LocalInfo>, |
| 13501 | + info: &LocalInfo, |
| 13502 | + args: &[crate::ast::expr::Argument], |
| 13503 | + st: &SymbolTable, |
| 13504 | +) -> ValueId { |
| 13505 | + let idx64 = compute_flat_elem_offset(b, locals, info, args, st); |
| 13506 | + let base = array_base_addr(b, info); |
| 13507 | + b.gep(base, vec![idx64], info.ty.clone()) |
| 13508 | +} |
| 13509 | + |
| 12496 | 13510 | fn emit_bounds_check(b: &mut FuncBuilder, index: ValueId, lower: ValueId, upper: ValueId) { |
| 12497 | 13511 | b.runtime_call( |
| 12498 | 13512 | RuntimeFunc::CheckBounds, |
@@ -12767,6 +13781,7 @@ fn lower_alloc_bounds( |
| 12767 | 13781 | /// over adjacent elements). |
| 12768 | 13782 | fn ir_scalar_byte_size(ty: &IrType) -> i64 { |
| 12769 | 13783 | match ty { |
| 13784 | + IrType::Array(elem, count) => (elem.size_bytes() * count) as i64, |
| 12770 | 13785 | IrType::Int(IntWidth::I8) | IrType::Bool => 1, |
| 12771 | 13786 | IrType::Int(IntWidth::I16) => 2, |
| 12772 | 13787 | IrType::Int(IntWidth::I32) | IrType::Float(FloatWidth::F32) => 4, |
@@ -12776,6 +13791,19 @@ fn ir_scalar_byte_size(ty: &IrType) -> i64 { |
| 12776 | 13791 | } |
| 12777 | 13792 | } |
| 12778 | 13793 | |
| 13794 | +fn descriptor_element_size_bytes(info: &LocalInfo) -> i64 { |
| 13795 | + match &info.ty { |
| 13796 | + IrType::Array(_, _) => info.ty.size_bytes() as i64, |
| 13797 | + IrType::Int(IntWidth::I8) => 1, |
| 13798 | + IrType::Int(IntWidth::I16) => 2, |
| 13799 | + IrType::Int(IntWidth::I32) | IrType::Float(FloatWidth::F32) => 4, |
| 13800 | + IrType::Int(IntWidth::I64) | IrType::Float(FloatWidth::F64) => 8, |
| 13801 | + IrType::Int(IntWidth::I128) => 16, |
| 13802 | + IrType::Bool => 4, |
| 13803 | + _ => 8, |
| 13804 | + } |
| 13805 | +} |
| 13806 | + |
| 12779 | 13807 | /// Store the literal values of an array constructor into a |
| 12780 | 13808 | /// destination buffer, one element at a time via byte-level GEP. |
| 12781 | 13809 | /// |
@@ -13173,7 +14201,7 @@ fn lower_write_items_adv( |
| 13173 | 14201 | ); |
| 13174 | 14202 | continue; |
| 13175 | 14203 | } |
| 13176 | | - if !info.dims.is_empty() || info.allocatable { |
| 14204 | + if local_is_array_like(&info) { |
| 13177 | 14205 | lower_whole_array_write(b, ctx, &info, unit); |
| 13178 | 14206 | continue; |
| 13179 | 14207 | } |
@@ -13211,7 +14239,7 @@ fn lower_write_items_adv( |
| 13211 | 14239 | if let Expr::Name { name } = &callee.node { |
| 13212 | 14240 | let key = name.to_lowercase(); |
| 13213 | 14241 | if let Some(info) = ctx.locals.get(&key).cloned() { |
| 13214 | | - if !info.dims.is_empty() || info.allocatable { |
| 14242 | + if local_is_array_like(&info) { |
| 13215 | 14243 | let has_range = args.iter().any(|a| { |
| 13216 | 14244 | matches!(a.value, crate::ast::expr::SectionSubscript::Range { .. }) |
| 13217 | 14245 | }); |
@@ -13971,7 +14999,7 @@ fn lower_read_target_addr( |
| 13971 | 14999 | Expr::Name { name } => { |
| 13972 | 15000 | let key = name.to_lowercase(); |
| 13973 | 15001 | let info = ctx.locals.get(&key)?; |
| 13974 | | - if !info.dims.is_empty() || info.allocatable { |
| 15002 | + if local_is_array_like(info) { |
| 13975 | 15003 | return None; |
| 13976 | 15004 | } |
| 13977 | 15005 | let addr = if info.by_ref { |
@@ -15177,7 +16205,7 @@ fn whole_array_expr_info( |
| 15177 | 16205 | let key = name.to_lowercase(); |
| 15178 | 16206 | locals |
| 15179 | 16207 | .get(&key) |
| 15180 | | - .filter(|info| !info.dims.is_empty() || info.allocatable) |
| 16208 | + .filter(|info| local_is_array_like(info)) |
| 15181 | 16209 | .cloned() |
| 15182 | 16210 | } |
| 15183 | 16211 | |
@@ -15191,7 +16219,7 @@ fn whole_array_named_info( |
| 15191 | 16219 | let key = name.to_lowercase(); |
| 15192 | 16220 | let info = locals |
| 15193 | 16221 | .get(&key) |
| 15194 | | - .filter(|info| !info.dims.is_empty() || info.allocatable) |
| 16222 | + .filter(|info| local_is_array_like(info)) |
| 15195 | 16223 | .cloned()?; |
| 15196 | 16224 | Some((key, info)) |
| 15197 | 16225 | } |
@@ -15644,7 +16672,7 @@ fn lower_array_expr_descriptor( |
| 15644 | 16672 | Expr::ParenExpr { inner } => lower_array_expr_descriptor(b, locals, inner, st), |
| 15645 | 16673 | Expr::Name { name } => { |
| 15646 | 16674 | let info = locals.get(&name.to_lowercase())?; |
| 15647 | | - if !info.dims.is_empty() || local_uses_array_descriptor(info) { |
| 16675 | + if local_is_array_like(info) { |
| 15648 | 16676 | let desc = if local_uses_array_descriptor(info) { |
| 15649 | 16677 | array_descriptor_addr(b, info) |
| 15650 | 16678 | } else { |
@@ -15660,7 +16688,7 @@ fn lower_array_expr_descriptor( |
| 15660 | 16688 | return None; |
| 15661 | 16689 | }; |
| 15662 | 16690 | let info = locals.get(&name.to_lowercase())?; |
| 15663 | | - if (!info.dims.is_empty() || local_uses_array_descriptor(info)) |
| 16691 | + if local_is_array_like(info) |
| 15664 | 16692 | && args.iter().any(|arg| { |
| 15665 | 16693 | matches!(arg.value, crate::ast::expr::SectionSubscript::Range { .. }) |
| 15666 | 16694 | }) |
@@ -15689,41 +16717,25 @@ fn lower_1d_section_assign( |
| 15689 | 16717 | dest_args[0].value, |
| 15690 | 16718 | crate::ast::expr::SectionSubscript::Range { .. } |
| 15691 | 16719 | ) |
| 15692 | | - || !matches!(dest_info.char_kind, CharKind::None) |
| 15693 | 16720 | { |
| 15694 | 16721 | return false; |
| 15695 | 16722 | } |
| 15696 | 16723 | |
| 15697 | 16724 | let dest_desc = lower_array_section(b, &ctx.locals, dest_info, dest_args, ctx.st); |
| 15698 | | - let dest_base = b.load_typed(dest_desc, IrType::Ptr(Box::new(dest_info.ty.clone()))); |
| 15699 | 16725 | let dest_n = b.call( |
| 15700 | 16726 | FuncRef::External("afs_array_size".into()), |
| 15701 | 16727 | vec![dest_desc], |
| 15702 | 16728 | IrType::Int(IntWidth::I64), |
| 15703 | 16729 | ); |
| 15704 | 16730 | let dest_stride = load_array_desc_i64_field(b, dest_desc, 24 + 16); |
| 15705 | | - let elem_bytes = b.const_i64(ir_scalar_byte_size(&dest_info.ty)); |
| 15706 | | - |
| 15707 | 16731 | let src_desc = lower_array_expr_descriptor(b, &ctx.locals, value, ctx.st); |
| 15708 | | - let scalar = src_desc.is_none().then(|| { |
| 15709 | | - let raw = lower_expr_ctx_tl(b, ctx, value); |
| 15710 | | - coerce_to_type(b, raw, &dest_info.ty) |
| 15711 | | - }); |
| 15712 | | - |
| 15713 | | - let (src_base, src_stride, src_n, src_ty) = if let Some((desc, ty)) = src_desc { |
| 15714 | | - ( |
| 15715 | | - Some(b.load_typed(desc, IrType::Ptr(Box::new(ty.clone())))), |
| 15716 | | - Some(load_array_desc_i64_field(b, desc, 24 + 16)), |
| 15717 | | - Some(b.call( |
| 15718 | | - FuncRef::External("afs_array_size".into()), |
| 15719 | | - vec![desc], |
| 15720 | | - IrType::Int(IntWidth::I64), |
| 15721 | | - )), |
| 15722 | | - Some(ty), |
| 16732 | + let src_n = src_desc.as_ref().map(|(desc, _)| { |
| 16733 | + b.call( |
| 16734 | + FuncRef::External("afs_array_size".into()), |
| 16735 | + vec![*desc], |
| 16736 | + IrType::Int(IntWidth::I64), |
| 15723 | 16737 | ) |
| 15724 | | - } else { |
| 15725 | | - (None, None, None, None) |
| 15726 | | - }; |
| 16738 | + }); |
| 15727 | 16739 | |
| 15728 | 16740 | let i_addr = b.alloca(IrType::Int(IntWidth::I64)); |
| 15729 | 16741 | let zero64 = b.const_i64(0); |
@@ -15745,22 +16757,63 @@ fn lower_1d_section_assign( |
| 15745 | 16757 | |
| 15746 | 16758 | b.set_block(bb_body); |
| 15747 | 16759 | let i_val = b.load(i_addr); |
| 15748 | | - let dest_index = b.imul(i_val, dest_stride); |
| 15749 | | - let dest_off = b.imul(dest_index, elem_bytes); |
| 15750 | | - let dest_ptr = b.gep(dest_base, vec![dest_off], IrType::Int(IntWidth::I8)); |
| 15751 | 16760 | |
| 15752 | | - let stored = if let (Some(src_base), Some(src_stride), Some(src_ty)) = |
| 15753 | | - (src_base, src_stride, src_ty.clone()) |
| 15754 | | - { |
| 15755 | | - let src_index = b.imul(i_val, src_stride); |
| 15756 | | - let src_off = b.imul(src_index, elem_bytes); |
| 15757 | | - let src_ptr = b.gep(src_base, vec![src_off], IrType::Int(IntWidth::I8)); |
| 15758 | | - let raw = b.load_typed(src_ptr, src_ty); |
| 15759 | | - coerce_to_type(b, raw, &dest_info.ty) |
| 16761 | + if dest_info.char_kind != CharKind::None || descriptor_backed_runtime_char_array(dest_info) { |
| 16762 | + let dest_base = b.load_typed(dest_desc, IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))); |
| 16763 | + let dest_elem_len = descriptor_elem_size(b, dest_desc); |
| 16764 | + let dest_index = b.imul(i_val, dest_stride); |
| 16765 | + let dest_off = b.imul(dest_index, dest_elem_len); |
| 16766 | + let dest_ptr = b.gep(dest_base, vec![dest_off], IrType::Int(IntWidth::I8)); |
| 16767 | + |
| 16768 | + let (src_ptr, src_len) = if let Some((desc, _)) = src_desc.as_ref() { |
| 16769 | + let src_base = b.load_typed(*desc, IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))); |
| 16770 | + let src_stride = load_array_desc_i64_field(b, *desc, 24 + 16); |
| 16771 | + let src_index = b.imul(i_val, src_stride); |
| 16772 | + let src_elem_len = descriptor_elem_size(b, *desc); |
| 16773 | + let src_off = b.imul(src_index, src_elem_len); |
| 16774 | + let src_ptr = b.gep(src_base, vec![src_off], IrType::Int(IntWidth::I8)); |
| 16775 | + (src_ptr, src_elem_len) |
| 16776 | + } else { |
| 16777 | + lower_string_expr_ctx(b, ctx, value) |
| 16778 | + }; |
| 16779 | + b.call( |
| 16780 | + FuncRef::External("afs_assign_char_fixed".into()), |
| 16781 | + vec![dest_ptr, dest_elem_len, src_ptr, src_len], |
| 16782 | + IrType::Void, |
| 16783 | + ); |
| 15760 | 16784 | } else { |
| 15761 | | - scalar.expect("scalar slice assignment should have scalar RHS") |
| 15762 | | - }; |
| 15763 | | - b.store(stored, dest_ptr); |
| 16785 | + let dest_base = b.load_typed(dest_desc, IrType::Ptr(Box::new(dest_info.ty.clone()))); |
| 16786 | + let elem_bytes = b.const_i64(ir_scalar_byte_size(&dest_info.ty)); |
| 16787 | + let scalar = src_desc.is_none().then(|| { |
| 16788 | + let raw = lower_expr_ctx_tl(b, ctx, value); |
| 16789 | + coerce_to_type(b, raw, &dest_info.ty) |
| 16790 | + }); |
| 16791 | + let (src_base, src_stride, src_ty) = if let Some((desc, ty)) = src_desc.as_ref() { |
| 16792 | + ( |
| 16793 | + Some(b.load_typed(*desc, IrType::Ptr(Box::new(ty.clone())))), |
| 16794 | + Some(load_array_desc_i64_field(b, *desc, 24 + 16)), |
| 16795 | + Some(ty.clone()), |
| 16796 | + ) |
| 16797 | + } else { |
| 16798 | + (None, None, None) |
| 16799 | + }; |
| 16800 | + |
| 16801 | + let dest_index = b.imul(i_val, dest_stride); |
| 16802 | + let dest_off = b.imul(dest_index, elem_bytes); |
| 16803 | + let dest_ptr = b.gep(dest_base, vec![dest_off], IrType::Int(IntWidth::I8)); |
| 16804 | + let stored = if let (Some(src_base), Some(src_stride), Some(src_ty)) = |
| 16805 | + (src_base, src_stride, src_ty) |
| 16806 | + { |
| 16807 | + let src_index = b.imul(i_val, src_stride); |
| 16808 | + let src_off = b.imul(src_index, elem_bytes); |
| 16809 | + let src_ptr = b.gep(src_base, vec![src_off], IrType::Int(IntWidth::I8)); |
| 16810 | + let raw = b.load_typed(src_ptr, src_ty); |
| 16811 | + coerce_to_type(b, raw, &dest_info.ty) |
| 16812 | + } else { |
| 16813 | + scalar.expect("scalar slice assignment should have scalar RHS") |
| 16814 | + }; |
| 16815 | + b.store(stored, dest_ptr); |
| 16816 | + } |
| 15764 | 16817 | |
| 15765 | 16818 | let one = b.const_i64(1); |
| 15766 | 16819 | let next_i = b.iadd(i_val, one); |
@@ -16053,6 +17106,77 @@ fn lower_array_assign( |
| 16053 | 17106 | return; |
| 16054 | 17107 | } |
| 16055 | 17108 | |
| 17109 | + if (dest_info.char_kind != CharKind::None || descriptor_backed_runtime_char_array(dest_info)) |
| 17110 | + && local_uses_array_descriptor(dest_info) |
| 17111 | + { |
| 17112 | + let dest_desc = array_descriptor_addr(b, dest_info); |
| 17113 | + let dest_base = b.load_typed(dest_desc, IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))); |
| 17114 | + let dest_n = b.call( |
| 17115 | + FuncRef::External("afs_array_size".into()), |
| 17116 | + vec![dest_desc], |
| 17117 | + IrType::Int(IntWidth::I64), |
| 17118 | + ); |
| 17119 | + let dest_stride = load_array_desc_i64_field(b, dest_desc, 24 + 16); |
| 17120 | + let dest_elem_len = descriptor_elem_size(b, dest_desc); |
| 17121 | + let src_desc = lower_array_expr_descriptor(b, &ctx.locals, value, ctx.st); |
| 17122 | + let src_n = src_desc.as_ref().map(|(desc, _)| { |
| 17123 | + b.call( |
| 17124 | + FuncRef::External("afs_array_size".into()), |
| 17125 | + vec![*desc], |
| 17126 | + IrType::Int(IntWidth::I64), |
| 17127 | + ) |
| 17128 | + }); |
| 17129 | + |
| 17130 | + let i_addr = b.alloca(IrType::Int(IntWidth::I64)); |
| 17131 | + let zero = b.const_i64(0); |
| 17132 | + b.store(zero, i_addr); |
| 17133 | + |
| 17134 | + let bb_check = b.create_block("char_array_assign_check"); |
| 17135 | + let bb_body = b.create_block("char_array_assign_body"); |
| 17136 | + let bb_exit = b.create_block("char_array_assign_exit"); |
| 17137 | + b.branch(bb_check, vec![]); |
| 17138 | + |
| 17139 | + b.set_block(bb_check); |
| 17140 | + let i = b.load(i_addr); |
| 17141 | + let mut done = b.icmp(CmpOp::Ge, i, dest_n); |
| 17142 | + if let Some(src_len) = src_n { |
| 17143 | + let src_done = b.icmp(CmpOp::Ge, i, src_len); |
| 17144 | + done = b.or(done, src_done); |
| 17145 | + } |
| 17146 | + b.cond_branch(done, bb_exit, vec![], bb_body, vec![]); |
| 17147 | + |
| 17148 | + b.set_block(bb_body); |
| 17149 | + let i_val = b.load(i_addr); |
| 17150 | + let dest_index = b.imul(i_val, dest_stride); |
| 17151 | + let dest_off = b.imul(dest_index, dest_elem_len); |
| 17152 | + let dest_ptr = b.gep(dest_base, vec![dest_off], IrType::Int(IntWidth::I8)); |
| 17153 | + |
| 17154 | + let (src_ptr, src_len) = if let Some((desc, _)) = src_desc.as_ref() { |
| 17155 | + let src_base = b.load_typed(*desc, IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))); |
| 17156 | + let src_stride = load_array_desc_i64_field(b, *desc, 24 + 16); |
| 17157 | + let src_index = b.imul(i_val, src_stride); |
| 17158 | + let src_elem_len = descriptor_elem_size(b, *desc); |
| 17159 | + let src_off = b.imul(src_index, src_elem_len); |
| 17160 | + let src_ptr = b.gep(src_base, vec![src_off], IrType::Int(IntWidth::I8)); |
| 17161 | + (src_ptr, src_elem_len) |
| 17162 | + } else { |
| 17163 | + lower_string_expr_ctx(b, ctx, value) |
| 17164 | + }; |
| 17165 | + b.call( |
| 17166 | + FuncRef::External("afs_assign_char_fixed".into()), |
| 17167 | + vec![dest_ptr, dest_elem_len, src_ptr, src_len], |
| 17168 | + IrType::Void, |
| 17169 | + ); |
| 17170 | + |
| 17171 | + let one = b.const_i64(1); |
| 17172 | + let next_i = b.iadd(i_val, one); |
| 17173 | + b.store(next_i, i_addr); |
| 17174 | + b.branch(bb_check, vec![]); |
| 17175 | + |
| 17176 | + b.set_block(bb_exit); |
| 17177 | + return; |
| 17178 | + } |
| 17179 | + |
| 16056 | 17180 | if try_lower_elemental_array_assign(b, ctx, dest_name, dest_info, value) { |
| 16057 | 17181 | return; |
| 16058 | 17182 | } |
@@ -16145,7 +17269,7 @@ fn collect_array_names( |
| 16145 | 17269 | Expr::Name { name } => { |
| 16146 | 17270 | let key = name.to_lowercase(); |
| 16147 | 17271 | if let Some(info) = locals.get(&key) { |
| 16148 | | - if (!info.dims.is_empty() || info.allocatable) && !out.contains(&key) { |
| 17272 | + if local_is_array_like(info) && !out.contains(&key) { |
| 16149 | 17273 | out.push(key); |
| 16150 | 17274 | } |
| 16151 | 17275 | } |
@@ -16526,7 +17650,7 @@ fn component_intrinsic_local_info( |
| 16526 | 17650 | if field.size == 384 && (field.allocatable || field.pointer) { |
| 16527 | 17651 | return Some(LocalInfo { |
| 16528 | 17652 | addr: field_ptr, |
| 16529 | | - ty: type_info_to_ir_type(&field.type_info), |
| 17653 | + ty: type_info_to_storage_ir_type(&field.type_info, tl), |
| 16530 | 17654 | dims: vec![], |
| 16531 | 17655 | allocatable: true, |
| 16532 | 17656 | descriptor_arg: false, |
@@ -16543,7 +17667,7 @@ fn component_intrinsic_local_info( |
| 16543 | 17667 | } |
| 16544 | 17668 | Some(LocalInfo { |
| 16545 | 17669 | addr: field_ptr, |
| 16546 | | - ty: type_info_to_ir_type(&field.type_info), |
| 17670 | + ty: type_info_to_storage_ir_type(&field.type_info, tl), |
| 16547 | 17671 | dims: field.dims.clone(), |
| 16548 | 17672 | allocatable: false, |
| 16549 | 17673 | descriptor_arg: false, |
@@ -16970,6 +18094,29 @@ fn type_info_to_ir_type(ti: &crate::sema::symtab::TypeInfo) -> IrType { |
| 16970 | 18094 | } |
| 16971 | 18095 | } |
| 16972 | 18096 | |
| 18097 | +fn derived_storage_ir_type( |
| 18098 | + type_name: &str, |
| 18099 | + tl: &crate::sema::type_layout::TypeLayoutRegistry, |
| 18100 | +) -> Option<IrType> { |
| 18101 | + let layout = tl.get(type_name)?; |
| 18102 | + Some(IrType::Array( |
| 18103 | + Box::new(IrType::Int(IntWidth::I8)), |
| 18104 | + layout.size as u64, |
| 18105 | + )) |
| 18106 | +} |
| 18107 | + |
| 18108 | +fn type_info_to_storage_ir_type( |
| 18109 | + ti: &crate::sema::symtab::TypeInfo, |
| 18110 | + tl: &crate::sema::type_layout::TypeLayoutRegistry, |
| 18111 | +) -> IrType { |
| 18112 | + if let crate::sema::symtab::TypeInfo::Derived(type_name) = ti { |
| 18113 | + if let Some(storage_ty) = derived_storage_ir_type(type_name, tl) { |
| 18114 | + return storage_ty; |
| 18115 | + } |
| 18116 | + } |
| 18117 | + type_info_to_ir_type(ti) |
| 18118 | +} |
| 18119 | + |
| 16973 | 18120 | fn lower_fixed_component_array_element_ptr( |
| 16974 | 18121 | b: &mut FuncBuilder, |
| 16975 | 18122 | locals: &HashMap<String, LocalInfo>, |
@@ -17033,6 +18180,8 @@ fn resolve_component_base( |
| 17033 | 18180 | // Dereference once to get the struct base. |
| 17034 | 18181 | let addr = if info.is_pointer { |
| 17035 | 18182 | b.load_typed(info.addr, IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))) |
| 18183 | + } else if info.allocatable { |
| 18184 | + array_base_addr(b, info) |
| 17036 | 18185 | } else if info.by_ref { |
| 17037 | 18186 | b.load(info.addr) |
| 17038 | 18187 | } else { |
@@ -17120,7 +18269,7 @@ fn resolve_component_field_access( |
| 17120 | 18269 | } |
| 17121 | 18270 | |
| 17122 | 18271 | fn is_deferred_char_component_field(field: &crate::sema::type_layout::FieldLayout) -> bool { |
| 17123 | | - field.pointer |
| 18272 | + (field.pointer || field.allocatable) |
| 17124 | 18273 | && matches!( |
| 17125 | 18274 | field.type_info, |
| 17126 | 18275 | crate::sema::symtab::TypeInfo::Character { len: None, .. } |
@@ -17169,7 +18318,7 @@ fn component_array_local_info( |
| 17169 | 18318 | } |
| 17170 | 18319 | Some(LocalInfo { |
| 17171 | 18320 | addr: field_ptr, |
| 17172 | | - ty: type_info_to_ir_type(&field.type_info), |
| 18321 | + ty: type_info_to_storage_ir_type(&field.type_info, tl), |
| 17173 | 18322 | dims: vec![], |
| 17174 | 18323 | allocatable: true, |
| 17175 | 18324 | descriptor_arg: false, |
@@ -17193,6 +18342,9 @@ fn expr_is_array_designator( |
| 17193 | 18342 | Expr::Name { name } => locals |
| 17194 | 18343 | .get(&name.to_lowercase()) |
| 17195 | 18344 | .map(|info| { |
| 18345 | + if local_fixed_char_allocatable_scalar_len(info).is_some() { |
| 18346 | + return false; |
| 18347 | + } |
| 17196 | 18348 | if matches!(info.char_kind, CharKind::Deferred) { |
| 17197 | 18349 | false |
| 17198 | 18350 | } else { |
@@ -17222,7 +18374,10 @@ fn expr_is_character_expr( |
| 17222 | 18374 | } => true, |
| 17223 | 18375 | Expr::Name { name } => locals |
| 17224 | 18376 | .get(&name.to_lowercase()) |
| 17225 | | - .map(|info| info.char_kind != CharKind::None) |
| 18377 | + .map(|info| { |
| 18378 | + info.char_kind != CharKind::None |
| 18379 | + || local_fixed_char_allocatable_scalar_len(info).is_some() |
| 18380 | + }) |
| 17226 | 18381 | .unwrap_or(false), |
| 17227 | 18382 | Expr::ComponentAccess { .. } => type_layouts |
| 17228 | 18383 | .and_then(|tl| resolve_component_field_access(b, locals, expr, st, tl)) |
@@ -17269,10 +18424,13 @@ fn expr_is_character_expr( |
| 17269 | 18424 | } |
| 17270 | 18425 | }) |
| 17271 | 18426 | .unwrap_or(false)) |
| 18427 | + || named_expr_callable_character_return_abi(st, locals, &key).is_some() |
| 17272 | 18428 | || locals |
| 17273 | 18429 | .get(&key) |
| 17274 | 18430 | .map(|info| { |
| 17275 | | - info.char_kind != CharKind::None |
| 18431 | + (info.char_kind != CharKind::None |
| 18432 | + || descriptor_backed_runtime_char_array(info) |
| 18433 | + || local_fixed_char_allocatable_scalar_len(info).is_some()) |
| 17276 | 18434 | && (info.dims.is_empty() |
| 17277 | 18435 | || args.iter().all(|arg| { |
| 17278 | 18436 | matches!( |
@@ -17290,7 +18448,7 @@ fn expr_is_character_expr( |
| 17290 | 18448 | resolve_component_field_access(b, locals, callee, st, tl).map( |
| 17291 | 18449 | |(field_ptr, field)| LocalInfo { |
| 17292 | 18450 | addr: field_ptr, |
| 17293 | | - ty: type_info_to_ir_type(&field.type_info), |
| 18451 | + ty: type_info_to_storage_ir_type(&field.type_info, tl), |
| 17294 | 18452 | dims: vec![], |
| 17295 | 18453 | allocatable: field.allocatable, |
| 17296 | 18454 | descriptor_arg: false, |
@@ -17304,7 +18462,10 @@ fn expr_is_character_expr( |
| 17304 | 18462 | ) |
| 17305 | 18463 | }) |
| 17306 | 18464 | }) |
| 17307 | | - .map(|info| info.char_kind != CharKind::None) |
| 18465 | + .map(|info| { |
| 18466 | + info.char_kind != CharKind::None |
| 18467 | + || descriptor_backed_runtime_char_array(&info) |
| 18468 | + }) |
| 17308 | 18469 | .unwrap_or(false) |
| 17309 | 18470 | } else { |
| 17310 | 18471 | expr_is_character_expr(b, locals, callee, st, type_layouts) |
@@ -17328,6 +18489,51 @@ fn store_string_descriptor_view(b: &mut FuncBuilder, desc: ValueId, ptr: ValueId |
| 17328 | 18489 | store_byte_aggregate_field(b, desc, 24, IrType::Int(IntWidth::I32), flags); |
| 17329 | 18490 | } |
| 17330 | 18491 | |
| 18492 | +fn init_allocated_string_descriptor(b: &mut FuncBuilder, desc: ValueId, len: ValueId) { |
| 18493 | + let buf = b.runtime_call( |
| 18494 | + RuntimeFunc::Allocate, |
| 18495 | + vec![len], |
| 18496 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 18497 | + ); |
| 18498 | + let space = b.const_i32(b' ' as i32); |
| 18499 | + b.call( |
| 18500 | + FuncRef::External("memset".into()), |
| 18501 | + vec![buf, space, len], |
| 18502 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 18503 | + ); |
| 18504 | + store_byte_aggregate_field( |
| 18505 | + b, |
| 18506 | + desc, |
| 18507 | + 0, |
| 18508 | + IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))), |
| 18509 | + buf, |
| 18510 | + ); |
| 18511 | + store_byte_aggregate_field(b, desc, 8, IrType::Int(IntWidth::I64), len); |
| 18512 | + store_byte_aggregate_field(b, desc, 16, IrType::Int(IntWidth::I64), len); |
| 18513 | + let flags = b.const_i32(3); |
| 18514 | + store_byte_aggregate_field(b, desc, 24, IrType::Int(IntWidth::I32), flags); |
| 18515 | +} |
| 18516 | + |
| 18517 | +fn typed_allocate_char_len( |
| 18518 | + b: &mut FuncBuilder, |
| 18519 | + locals: &HashMap<String, LocalInfo>, |
| 18520 | + type_spec: Option<&TypeSpec>, |
| 18521 | + st: &SymbolTable, |
| 18522 | +) -> Option<ValueId> { |
| 18523 | + match type_spec { |
| 18524 | + Some(TypeSpec::Character(None)) => Some(b.const_i64(1)), |
| 18525 | + Some(TypeSpec::Character(Some(sel))) => match &sel.len { |
| 18526 | + Some(crate::ast::decl::LenSpec::Expr(e)) => { |
| 18527 | + let raw_len = lower_expr(b, locals, e, st); |
| 18528 | + Some(clamp_nonnegative_i64(b, raw_len)) |
| 18529 | + } |
| 18530 | + None => Some(b.const_i64(1)), |
| 18531 | + _ => None, |
| 18532 | + }, |
| 18533 | + _ => None, |
| 18534 | + } |
| 18535 | +} |
| 18536 | + |
| 17331 | 18537 | /// Resolve a base expression for a type-bound procedure call. |
| 17332 | 18538 | /// Returns (object_address, type_name) — the address of the base object. |
| 17333 | 18539 | /// For simple `obj%method()`, base is `obj` → returns (obj.addr, obj.type). |
@@ -17344,7 +18550,9 @@ fn resolve_component_base_for_method( |
| 17344 | 18550 | let key = name.to_lowercase(); |
| 17345 | 18551 | let info = locals.get(&key)?; |
| 17346 | 18552 | let type_name = info.derived_type.as_ref()?.clone(); |
| 17347 | | - let addr = if info.by_ref { |
| 18553 | + let addr = if info.allocatable { |
| 18554 | + array_base_addr(b, info) |
| 18555 | + } else if info.by_ref { |
| 17348 | 18556 | b.load(info.addr) |
| 17349 | 18557 | } else { |
| 17350 | 18558 | info.addr |
@@ -17487,7 +18695,13 @@ fn lower_char_arg_by_ref( |
| 17487 | 18695 | if !matches!(info.char_kind, CharKind::Fixed(_)) || info.dims.is_empty() { |
| 17488 | 18696 | return None; |
| 17489 | 18697 | } |
| 17490 | | - let (ptr, _len) = char_array_element_ptr_and_len(b, locals, info, args, st)?; |
| 18698 | + let ptr = if local_uses_array_descriptor(info) |
| 18699 | + || matches!(info.ty, IrType::Int(IntWidth::I8)) |
| 18700 | + { |
| 18701 | + char_array_element_ptr_and_len(b, locals, info, args, st)?.0 |
| 18702 | + } else { |
| 18703 | + lower_array_element(b, locals, info, args, st) |
| 18704 | + }; |
| 17491 | 18705 | let slot = b.alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)))); |
| 17492 | 18706 | b.store(ptr, slot); |
| 17493 | 18707 | Some(slot) |
@@ -17809,7 +19023,9 @@ fn lower_expr_full( |
| 17809 | 19023 | // of the struct as if they were a pointer, turning |
| 17810 | 19024 | // `b = a` into a memcpy from the garbage address held |
| 17811 | 19025 | // by a's first field slot. |
| 17812 | | - if info.by_ref { |
| 19026 | + if info.allocatable { |
| 19027 | + array_base_addr(b, info) |
| 19028 | + } else if info.by_ref { |
| 17813 | 19029 | b.load(info.addr) |
| 17814 | 19030 | } else { |
| 17815 | 19031 | info.addr |
@@ -18151,7 +19367,7 @@ fn lower_expr_full( |
| 18151 | 19367 | |
| 18152 | 19368 | // Check if this is an array element or section access. |
| 18153 | 19369 | if let Some(info) = locals.get(&key) { |
| 18154 | | - if !info.dims.is_empty() || info.allocatable { |
| 19370 | + if local_is_array_like(info) { |
| 18155 | 19371 | let has_range = args.iter().any(|a| { |
| 18156 | 19372 | matches!(a.value, crate::ast::expr::SectionSubscript::Range { .. }) |
| 18157 | 19373 | }); |
@@ -18278,15 +19494,99 @@ fn lower_expr_full( |
| 18278 | 19494 | return b.const_bool(true); |
| 18279 | 19495 | } |
| 18280 | 19496 | |
| 18281 | | - // c_loc(x) / c_funloc(f): return the address of the argument |
| 18282 | | - // as an i64 integer (matching type(c_ptr) which lowers to i64). |
| 18283 | | - // Must be handled before generic intrinsic lowering because |
| 18284 | | - // intrinsic args are loaded by value, but c_loc needs the address. |
| 18285 | | - if key == "c_loc" || key == "c_funloc" { |
| 19497 | + // c_loc(x): return the address of the target itself as an |
| 19498 | + // i64 integer (matching type(c_ptr)). This must bypass the |
| 19499 | + // normal by-ref argument path because character arguments use |
| 19500 | + // temporary pointer slots there, while c_loc needs the real |
| 19501 | + // underlying element/storage address. |
| 19502 | + if key == "c_loc" { |
| 19503 | + if let Some(arg0) = args.first() { |
| 19504 | + if let crate::ast::expr::SectionSubscript::Element(e) = &arg0.value { |
| 19505 | + if let Expr::Name { name } = &e.node { |
| 19506 | + if let Some(info) = locals.get(&name.to_lowercase()) { |
| 19507 | + let addr = if info.by_ref { |
| 19508 | + if info.descriptor_arg { |
| 19509 | + array_data_ptr_for_call(b, info) |
| 19510 | + } else if info.char_kind != CharKind::None |
| 19511 | + && info.dims.is_empty() |
| 19512 | + { |
| 19513 | + if let Some((ptr, _)) = |
| 19514 | + char_addr_and_runtime_len(b, e, locals) |
| 19515 | + { |
| 19516 | + ptr |
| 19517 | + } else { |
| 19518 | + b.load(info.addr) |
| 19519 | + } |
| 19520 | + } else { |
| 19521 | + b.load(info.addr) |
| 19522 | + } |
| 19523 | + } else if !info.dims.is_empty() |
| 19524 | + || local_uses_array_descriptor(info) |
| 19525 | + { |
| 19526 | + array_data_ptr_for_call(b, info) |
| 19527 | + } else if info.char_kind != CharKind::None { |
| 19528 | + if let Some((ptr, _)) = |
| 19529 | + char_addr_and_runtime_len(b, e, locals) |
| 19530 | + { |
| 19531 | + ptr |
| 19532 | + } else { |
| 19533 | + info.addr |
| 19534 | + } |
| 19535 | + } else { |
| 19536 | + info.addr |
| 19537 | + }; |
| 19538 | + return b.ptr_to_int(addr); |
| 19539 | + } |
| 19540 | + } |
| 19541 | + |
| 19542 | + if let Expr::FunctionCall { |
| 19543 | + callee, |
| 19544 | + args: subscripts, |
| 19545 | + } = &e.node |
| 19546 | + { |
| 19547 | + if let Expr::Name { name } = &callee.node { |
| 19548 | + if let Some(info) = locals.get(&name.to_lowercase()) { |
| 19549 | + if info.char_kind != CharKind::None { |
| 19550 | + let addr = if local_uses_array_descriptor(info) |
| 19551 | + || matches!(info.ty, IrType::Int(IntWidth::I8)) |
| 19552 | + { |
| 19553 | + char_array_element_ptr_and_len( |
| 19554 | + b, locals, info, subscripts, st, |
| 19555 | + ) |
| 19556 | + .map(|(ptr, _)| ptr) |
| 19557 | + .unwrap_or_else(|| { |
| 19558 | + lower_array_element_addr( |
| 19559 | + b, locals, info, subscripts, st, |
| 19560 | + ) |
| 19561 | + }) |
| 19562 | + } else { |
| 19563 | + lower_array_element(b, locals, info, subscripts, st) |
| 19564 | + }; |
| 19565 | + return b.ptr_to_int(addr); |
| 19566 | + } |
| 19567 | + if !info.dims.is_empty() |
| 19568 | + || local_uses_array_descriptor(info) |
| 19569 | + { |
| 19570 | + let addr = lower_array_element_addr( |
| 19571 | + b, locals, info, subscripts, st, |
| 19572 | + ); |
| 19573 | + return b.ptr_to_int(addr); |
| 19574 | + } |
| 19575 | + } |
| 19576 | + } |
| 19577 | + } |
| 19578 | + |
| 19579 | + let addr = lower_arg_by_ref(b, locals, e, st); |
| 19580 | + return b.ptr_to_int(addr); |
| 19581 | + } |
| 19582 | + } |
| 19583 | + } |
| 19584 | + |
| 19585 | + // c_funloc(f): return the entry address of the procedure. |
| 19586 | + if key == "c_funloc" { |
| 18286 | 19587 | if let Some(arg0) = args.first() { |
| 18287 | 19588 | if let crate::ast::expr::SectionSubscript::Element(e) = &arg0.value { |
| 18288 | 19589 | let addr = lower_arg_by_ref(b, locals, e, st); |
| 18289 | | - // Convert pointer to integer (c_ptr is i64). |
| 18290 | 19590 | return b.ptr_to_int(addr); |
| 18291 | 19591 | } |
| 18292 | 19592 | } |
@@ -18474,6 +19774,13 @@ fn lower_expr_full( |
| 18474 | 19774 | .or_else(|| callee_string_descriptor_arg_mask(st, &key)) |
| 18475 | 19775 | { |
| 18476 | 19776 | for (i, flag) in string_desc_flags.iter().enumerate() { |
| 19777 | + if callee_descriptor_args |
| 19778 | + .as_ref() |
| 19779 | + .map(|mask| mask.get(i).copied().unwrap_or(false)) |
| 19780 | + .unwrap_or(false) |
| 19781 | + { |
| 19782 | + continue; |
| 19783 | + } |
| 18477 | 19784 | if !*flag || i >= args.len() { |
| 18478 | 19785 | continue; |
| 18479 | 19786 | } |
@@ -18770,7 +20077,15 @@ mod tests { |
| 18770 | 20077 | let rr = resolve::resolve_file(&units, &[]).unwrap(); |
| 18771 | 20078 | (rr.st, rr.type_layouts) |
| 18772 | 20079 | }; |
| 18773 | | - lower_file(&units, &st, &layouts, HashMap::new(), HashMap::new()).0 |
| 20080 | + lower_file( |
| 20081 | + &units, |
| 20082 | + &st, |
| 20083 | + &layouts, |
| 20084 | + HashMap::new(), |
| 20085 | + HashMap::new(), |
| 20086 | + HashMap::new(), |
| 20087 | + ) |
| 20088 | + .0 |
| 18774 | 20089 | } |
| 18775 | 20090 | |
| 18776 | 20091 | fn lower_and_verify(src: &str) -> (Module, String) { |