@@ -423,45 +423,47 @@ pub(crate) fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &Spanned |
| 423 | 423 | } else { |
| 424 | 424 | false |
| 425 | 425 | }; |
| 426 | | - // Scalar-returning call assigned to a whole array is a |
| 427 | | - // broadcast: `x = ieee_value(...)`, `x = sin(scalar_y)`, |
| 428 | | - // user `x = scalar_func()`. The fall-through path |
| 429 | | - // lowers the call as a scalar then treats the result as |
| 430 | | - // a source descriptor — `load %x from non-pointer f32` |
| 431 | | - // in the IR verifier, or afs_assign_allocatable with the |
| 432 | | - // scalar value in the wrong arg register at runtime |
| 433 | | - // (stdlib's pinv_s_operator hit this on the |
| 434 | | - // `pinva = ieee_value(1.0, NaN)` error path). Route |
| 435 | | - // through lower_array_assign so its bulk-fill plan |
| 436 | | - // applies. Skip when args contain array refs (the |
| 437 | | - // call really is producing an array result that other |
| 438 | | - // arms above already handle) or when the callee is an |
| 439 | | - // alloc-return user function (handled below). |
| 440 | | - let callee_is_scalar_broadcast = local_is_array_like(&info) |
| 441 | | - && !callee_is_local_array |
| 442 | | - && !callee_is_elemental_array_intrinsic |
| 443 | | - && !callee_is_transformational_intrinsic |
| 444 | | - && call_args.iter().all(|arg| { |
| 445 | | - if let crate::ast::expr::SectionSubscript::Element(e) |
| 446 | | - = &arg.value |
| 447 | | - { |
| 448 | | - !expr_contains_array_refs(e, &ctx.locals) |
| 449 | | - } else { |
| 450 | | - false |
| 451 | | - } |
| 452 | | - }) |
| 453 | | - && { |
| 454 | | - if let Expr::Name { name: cname } = &callee.node { |
| 455 | | - let lk = cname.to_lowercase(); |
| 456 | | - !ctx.alloc_return_funcs.contains(&lk) |
| 457 | | - } else { |
| 458 | | - false |
| 459 | | - } |
| 460 | | - }; |
| 426 | + // Scalar-returning intrinsic broadcast to a whole array: |
| 427 | + // `x = ieee_value(1.0, NaN)`, `x = epsilon(1.0)`, |
| 428 | + // `x = huge(1.0)`. The fall-through path lowers the |
| 429 | + // call as a scalar then treats the result as a source |
| 430 | + // descriptor pointer — IR verifier catches "load from |
| 431 | + // non-pointer fN" on fixed-size dests, and SEGV inside |
| 432 | + // afs_assign_allocatable on descriptor-backed dests |
| 433 | + // (stdlib's pinv_s_operator on the linalg-error path |
| 434 | + // `pinva = ieee_value(1.0_sp, ieee_quiet_nan)`). |
| 435 | + // Restricted to a known set of always-scalar |
| 436 | + // intrinsics — extending it broadly mis-routes user |
| 437 | + // functions that legitimately return arrays. |
| 438 | + let callee_is_scalar_broadcast_intrinsic = |
| 439 | + local_is_array_like(&info) |
| 440 | + && !callee_is_local_array |
| 441 | + && !callee_is_elemental_array_intrinsic |
| 442 | + && !callee_is_transformational_intrinsic |
| 443 | + && { |
| 444 | + if let Expr::Name { name: cname } = &callee.node { |
| 445 | + let lk = cname.to_lowercase(); |
| 446 | + matches!( |
| 447 | + lk.as_str(), |
| 448 | + "ieee_value" |
| 449 | + | "epsilon" |
| 450 | + | "huge" |
| 451 | + | "tiny" |
| 452 | + | "radix" |
| 453 | + | "digits" |
| 454 | + | "precision" |
| 455 | + | "range" |
| 456 | + | "minexponent" |
| 457 | + | "maxexponent" |
| 458 | + ) |
| 459 | + } else { |
| 460 | + false |
| 461 | + } |
| 462 | + }; |
| 461 | 463 | if callee_is_local_array |
| 462 | 464 | || callee_is_elemental_array_intrinsic |
| 463 | 465 | || callee_is_transformational_intrinsic |
| 464 | | - || callee_is_scalar_broadcast |
| 466 | + || callee_is_scalar_broadcast_intrinsic |
| 465 | 467 | { |
| 466 | 468 | lower_array_assign(b, ctx, name, &info, value); |
| 467 | 469 | return; |