(
&mut self,
id: &KId<M>,
field: u64,
wval: &KExpr<M>,
)
| 1388 | log::info!( |
| 1389 | "[nat_iota_trace] rec={} major_bits={} spine={} major_idx={}", |
| 1390 | rec_id, |
| 1391 | val.0.bits(), |
| 1392 | spine.len(), |
| 1393 | recr.major_idx |
| 1394 | ); |
| 1395 | } |
| 1396 | } |
| 1397 | major_was_nat_lit = true; |
| 1398 | major_whnf = self.nat_to_constructor(&val.clone()); |
| 1399 | } |
| 1400 | if let Some(cleaned) = self.cleanup_nat_offset_major(&major_whnf)? { |
| 1401 | major_whnf = cleaned; |
| 1402 | } |
| 1403 | // String literal → constructor form (matching lean4lean |
| 1404 | // Reduce.lean:71 / C++ inductive.h:95). The expansion takes one |
| 1405 | // delta step past `String.ofList` (see `str_lit_to_ctor_app`) so the |
| 1406 | // native collapse rule in `try_reduce_string` can't fold it straight |
| 1407 | // back into the literal; constructor fields stay lazy. |
| 1408 | if let ExprData::Str(val, _, _) = major_whnf.data() { |
| 1409 | let val = val.clone(); |
| 1410 | major_whnf = self.str_lit_to_ctor_app(&val)?; |
| 1411 | } |
| 1412 | // Native char values are kept stuck by whnf (they never unfold to |
| 1413 | // their `dite`-validity body on their own). A recursor that |
| 1414 | // genuinely scrutinizes a char pays for the structural expansion |
| 1415 | // here, once — exactly what pre-native reduction did on every whnf. |
| 1416 | // Cheap mode leaves the major stuck, as it did before the fast path |
| 1417 | // (`Char.ofNat` is a Defn head that cheap whnf never delta-unfolds). |
| 1418 | if !flags.cheap_rec |
| 1419 | && self.char_lit_value(&major_whnf).is_some() |
| 1420 | && let Some(unfolded) = self.delta_unfold_one(&major_whnf)? |
| 1421 | { |
| 1422 | major_whnf = self.whnf(&unfolded)?; |
| 1423 | } |
| 1424 | |
| 1425 | // Check if major is a constructor application |
| 1426 | let (ctor_head, ctor_args) = collect_app_spine(&major_whnf); |
| 1427 | let is_ctor = match ctor_head.data() { |
| 1428 | ExprData::Const(id, _, _) => { |
| 1429 | matches!(self.try_get_const(id)?, Some(KConst::Ctor { .. })) |
| 1430 | }, |
| 1431 | _ => false, |
| 1432 | }; |
| 1433 | |
| 1434 | // Diagnostic: when the major doesn't reduce to a ctor, iota is stuck. |
| 1435 | // Surface which recursor + major shape we got \u2014 the major's head |
| 1436 | // tells us which downstream reduction (delta, iota, nat, int) failed |
| 1437 | // to complete. |
| 1438 | if !is_ctor && let Some(filter) = IX_IOTA_STUCK.as_ref() { |
| 1439 | let rec_name = format!("{rec_id}"); |
| 1440 | if filter.is_empty() || rec_name.contains(filter) { |
| 1441 | log::info!("[iota stuck] rec={rec_name}"); |
| 1442 | log::info!("[iota stuck] major: {major}"); |
| 1443 | log::info!("[iota stuck] major whnf: {major_whnf}"); |
| 1444 | } |
| 1445 | } |
| 1446 |
no test coverage detected