App spine comparison (lean4lean isDefEqApp): decompose both sides into head + args and compare componentwise. Handles multi-arg apps.
(
&mut self,
a: &KExpr<M>,
b: &KExpr<M>,
)
| 1256 | |
| 1257 | // Inductive must be struct-like (non-recursive, 0 indices, 1 ctor) |
| 1258 | if !self.is_struct_like(&induct_id)? { |
| 1259 | self.dump_eta_trace("not-struct-like", Some(&induct_id), 0, &t_norm, s); |
| 1260 | return Ok(false); |
| 1261 | } |
| 1262 | |
| 1263 | // Types must be def-eq (lean4lean tryEtaStructCore, line 515). |
| 1264 | // No Prop guard here — struct eta in def-eq is safe even for Prop types |
| 1265 | // because we're checking equality, not constructing terms. The Prop guard |
| 1266 | // is only needed in iota's toCtorWhenStruct (whnf.rs try_struct_eta_iota) |
| 1267 | // where eta-expanding creates projections that would be unsound for Prop. |
| 1268 | let s_ty = match self.with_infer_only(|tc| tc.infer(s)) { |
| 1269 | Ok(ty) => ty, |
| 1270 | Err(_) => { |
| 1271 | self.dump_eta_trace("infer-rhs-type", Some(&induct_id), 0, t, s); |
| 1272 | return Ok(false); |
| 1273 | }, |
| 1274 | }; |
| 1275 | let t_ty = match self.with_infer_only(|tc| tc.infer(&t_norm)) { |
| 1276 | Ok(ty) => ty, |
| 1277 | Err(_) => { |
| 1278 | self.dump_eta_trace("infer-lhs-type", Some(&induct_id), 0, &t_norm, s); |
| 1279 | return Ok(false); |
| 1280 | }, |
| 1281 | }; |
| 1282 | if !self.is_def_eq(&t_ty, &s_ty)? { |
| 1283 | self.dump_eta_trace("type-mismatch", Some(&induct_id), 0, &t_norm, s); |
| 1284 | return Ok(false); |
| 1285 | } |
no test coverage detected