Add `from_bits` and the float constants, and order the constants
`from_bits` reinterprets an integer's bytes as a float, which is what Nim's `cast` means; the inverse of `to_bits`, with the width from the type it is called on. `EPSILON`, `MIN_POSITIVE`, `INFINITY`, `NEG_INFINITY` and `NAN` are written out. Nim has its own spellings for some and none for others, and these are the values Rust defines rather than whatever a library happens to agree on. Module-level constants get a pass of their own, ahead of the associated constants -- `const DEFAULT_TOLERANCE: Self::Float = F64_TOLERANCE;` names one and was being written before it. That is the fifth ordering of this kind now: consts, associated consts, types, forwards, statics, bodies. Each was found by something naming something else that had not been emitted yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e34bf8b parent: 7e4c463 modified
src/lower.rs +85 -1 | @@ -671,6 +671,16 @@ impl Lowerer { | ||
| 671 | 671 | self.resolve_discriminants()?; |
| 672 | 672 | self.resolve_opaque()?; |
| 673 | 673 | |
| 674 | + // Pass 1.8: module-level constants, which an associated constant may | |
| 675 | + // mention -- `const DEFAULT: Self::Float = F64_TOLERANCE;` does. | |
| 676 | + for (m, f) in files { | |
| 677 | + self.cur_mod = m.clone(); | |
| 678 | + for item in &f.items { | |
| 679 | + let r = self.emit_consts(item); | |
| 680 | + self.note(r)?; | |
| 681 | + } | |
| 682 | + } | |
| 683 | + | |
| 674 | 684 | // Pass 1.9: associated constants, which a type definition may mention. |
| 675 | 685 | // `struct T { data: [u64; Self::N] }` is the case -- the constant has |
| 676 | 686 | // to be declared before the object that uses it, and `impl T` is |
| @@ -2717,6 +2727,32 @@ impl Lowerer { | ||
| 2717 | 2727 | // --------------------------------------------------------------- items |
| 2718 | 2728 | |
| 2719 | 2729 | /// Emit the type definitions only: they must precede every signature. |
| 2730 | + /// Write out the module-level constants, ahead of everything that can | |
| 2731 | + /// name one. | |
| 2732 | + fn emit_consts(&mut self, item: &Item) -> Result<(), String> { | |
| 2733 | + if !self.cfg_keeps(item_attrs(item))? { | |
| 2734 | + return Ok(()); | |
| 2735 | + } | |
| 2736 | + match item { | |
| 2737 | + Item::Const(c) if derive_item_block(c).is_none() => self.item_inner(item), | |
| 2738 | + Item::Const(c) => { | |
| 2739 | + for i in &derive_item_block(c).unwrap() { | |
| 2740 | + self.emit_consts(i)?; | |
| 2741 | + } | |
| 2742 | + Ok(()) | |
| 2743 | + } | |
| 2744 | + Item::Mod(m) => { | |
| 2745 | + if let Some((_, items)) = &m.content { | |
| 2746 | + for i in items { | |
| 2747 | + self.emit_consts(i)?; | |
| 2748 | + } | |
| 2749 | + } | |
| 2750 | + Ok(()) | |
| 2751 | + } | |
| 2752 | + _ => Ok(()), | |
| 2753 | + } | |
| 2754 | + } | |
| 2755 | + | |
| 2720 | 2756 | /// Write out the `static` items, after the types and before the procs. |
| 2721 | 2757 | fn emit_statics(&mut self, item: &Item) -> Result<(), String> { |
| 2722 | 2758 | if !self.cfg_keeps(item_attrs(item))? { |
| @@ -2794,7 +2830,9 @@ impl Lowerer { | ||
| 2794 | 2830 | } |
| 2795 | 2831 | Ok(()) |
| 2796 | 2832 | } |
| 2797 | - Item::Struct(_) | Item::Enum(_) | Item::Const(_) => self.item_inner(item), | |
| 2833 | + // Constants were written out in their own pass. | |
| 2834 | + Item::Const(_) => Ok(()), | |
| 2835 | + Item::Struct(_) | Item::Enum(_) => self.item_inner(item), | |
| 2798 | 2836 | Item::Macro(m) if path_name(&m.mac.path) == "bitflags" => self.item_inner(item), |
| 2799 | 2837 | Item::ForeignMod(_) => self.item_inner(item), |
| 2800 | 2838 | // A function-local type becomes a module-level type, so it is |
| @@ -6007,6 +6045,33 @@ impl Lowerer { | ||
| 6007 | 6045 | } |
| 6008 | 6046 | |
| 6009 | 6047 | // `i32::MAX` and friends: an associated const on a primitive. |
| 6048 | + // The float constants, written out. Nim has its own spellings | |
| 6049 | + // for some and none for others, and these are the values Rust | |
| 6050 | + // defines rather than whatever a library happens to agree on. | |
| 6051 | + if let Some(q) = p.path.segments.iter().rev().nth(1) { | |
| 6052 | + let f32 = q.ident == "f32"; | |
| 6053 | + if f32 || q.ident == "f64" { | |
| 6054 | + let v = match name.as_str() { | |
| 6055 | + "EPSILON" if f32 => Some("1.1920928955078125e-7'f32"), | |
| 6056 | + "EPSILON" => Some("2.220446049250313e-16'f64"), | |
| 6057 | + "MIN_POSITIVE" if f32 => Some("1.1754943508222875e-38'f32"), | |
| 6058 | + "MIN_POSITIVE" => Some("2.2250738585072014e-308'f64"), | |
| 6059 | + "INFINITY" if f32 => Some("float32(Inf)"), | |
| 6060 | + "INFINITY" => Some("Inf"), | |
| 6061 | + "NEG_INFINITY" if f32 => Some("float32(-Inf)"), | |
| 6062 | + "NEG_INFINITY" => Some("-Inf"), | |
| 6063 | + "NAN" if f32 => Some("float32(NaN)"), | |
| 6064 | + "NAN" => Some("NaN"), | |
| 6065 | + _ => None, | |
| 6066 | + }; | |
| 6067 | + if let Some(v) = v { | |
| 6068 | + return Ok(Val::new( | |
| 6069 | + v, | |
| 6070 | + Some(Nim::Prim(if f32 { "float32" } else { "float64" }.into())), | |
| 6071 | + )); | |
| 6072 | + } | |
| 6073 | + } | |
| 6074 | + } | |
| 6010 | 6075 | if matches!(name.as_str(), "MAX" | "MIN") { |
| 6011 | 6076 | if let Some(q) = p.path.segments.iter().rev().nth(1) { |
| 6012 | 6077 | if let Some(t @ Nim::Prim(_)) = ty::prim(&q.ident.to_string()) { |
| @@ -7784,6 +7849,25 @@ impl Lowerer { | ||
| 7784 | 7849 | Some(Nim::Prim("bool".into())), |
| 7785 | 7850 | )); |
| 7786 | 7851 | } |
| 7852 | + // `f64::from_bits(u)` reinterprets the integer's bytes as a | |
| 7853 | + // float, which is what Nim's `cast` means. The inverse of | |
| 7854 | + // `to_bits`, and the width comes from the type it is called on. | |
| 7855 | + "from_bits" if codes.len() == 1 => { | |
| 7856 | + if let Some(q) = p.path.segments.iter().rev().nth(1) { | |
| 7857 | + let (to, from) = match q.ident.to_string().as_str() { | |
| 7858 | + "f32" => ("float32", "uint32"), | |
| 7859 | + "f64" => ("float64", "uint64"), | |
| 7860 | + _ => ("", ""), | |
| 7861 | + }; | |
| 7862 | + if !to.is_empty() { | |
| 7863 | + let v = self.expr_at(&c.args[0], Some(&Nim::Prim(from.into())))?; | |
| 7864 | + return Ok(Val::new( | |
| 7865 | + format!("cast[{}]({}({}))", to, from, v.code), | |
| 7866 | + Some(Nim::Prim(to.into())), | |
| 7867 | + )); | |
| 7868 | + } | |
| 7869 | + } | |
| 7870 | + } | |
| 7787 | 7871 | // `cmp::max` / `cmp::min` are Nim's `max` / `min` on the same |
| 7788 | 7872 | // ordering. |
| 7789 | 7873 | "max" | "min" if codes.len() == 2 => { |
| @@ -671,6 +671,16 @@ impl Lowerer { | |||
| 671 | self.resolve_discriminants()?; | 671 | self.resolve_discriminants()?; |
| 672 | self.resolve_opaque()?; | 672 | self.resolve_opaque()?; |
| 673 | 673 | ||
| 674 | + // Pass 1.8: module-level constants, which an associated constant may | ||
| 675 | + // mention -- `const DEFAULT: Self::Float = F64_TOLERANCE;` does. | ||
| 676 | + for (m, f) in files { | ||
| 677 | + self.cur_mod = m.clone(); | ||
| 678 | + for item in &f.items { | ||
| 679 | + let r = self.emit_consts(item); | ||
| 680 | + self.note(r)?; | ||
| 681 | + } | ||
| 682 | + } | ||
| 683 | + | ||
| 674 | // Pass 1.9: associated constants, which a type definition may mention. | 684 | // Pass 1.9: associated constants, which a type definition may mention. |
| 675 | // `struct T { data: [u64; Self::N] }` is the case -- the constant has | 685 | // `struct T { data: [u64; Self::N] }` is the case -- the constant has |
| 676 | // to be declared before the object that uses it, and `impl T` is | 686 | // to be declared before the object that uses it, and `impl T` is |
| @@ -2717,6 +2727,32 @@ impl Lowerer { | |||
| 2717 | // --------------------------------------------------------------- items | 2727 | // --------------------------------------------------------------- items |
| 2718 | 2728 | ||
| 2719 | /// Emit the type definitions only: they must precede every signature. | 2729 | /// Emit the type definitions only: they must precede every signature. |
| 2730 | + /// Write out the module-level constants, ahead of everything that can | ||
| 2731 | + /// name one. | ||
| 2732 | + fn emit_consts(&mut self, item: &Item) -> Result<(), String> { | ||
| 2733 | + if !self.cfg_keeps(item_attrs(item))? { | ||
| 2734 | + return Ok(()); | ||
| 2735 | + } | ||
| 2736 | + match item { | ||
| 2737 | + Item::Const(c) if derive_item_block(c).is_none() => self.item_inner(item), | ||
| 2738 | + Item::Const(c) => { | ||
| 2739 | + for i in &derive_item_block(c).unwrap() { | ||
| 2740 | + self.emit_consts(i)?; | ||
| 2741 | + } | ||
| 2742 | + Ok(()) | ||
| 2743 | + } | ||
| 2744 | + Item::Mod(m) => { | ||
| 2745 | + if let Some((_, items)) = &m.content { | ||
| 2746 | + for i in items { | ||
| 2747 | + self.emit_consts(i)?; | ||
| 2748 | + } | ||
| 2749 | + } | ||
| 2750 | + Ok(()) | ||
| 2751 | + } | ||
| 2752 | + _ => Ok(()), | ||
| 2753 | + } | ||
| 2754 | + } | ||
| 2755 | + | ||
| 2720 | /// Write out the `static` items, after the types and before the procs. | 2756 | /// Write out the `static` items, after the types and before the procs. |
| 2721 | fn emit_statics(&mut self, item: &Item) -> Result<(), String> { | 2757 | fn emit_statics(&mut self, item: &Item) -> Result<(), String> { |
| 2722 | if !self.cfg_keeps(item_attrs(item))? { | 2758 | if !self.cfg_keeps(item_attrs(item))? { |
| @@ -2794,7 +2830,9 @@ impl Lowerer { | |||
| 2794 | } | 2830 | } |
| 2795 | Ok(()) | 2831 | Ok(()) |
| 2796 | } | 2832 | } |
| 2797 | - Item::Struct(_) | Item::Enum(_) | Item::Const(_) => self.item_inner(item), | 2833 | + // Constants were written out in their own pass. |
| 2834 | + Item::Const(_) => Ok(()), | ||
| 2835 | + Item::Struct(_) | Item::Enum(_) => self.item_inner(item), | ||
| 2798 | Item::Macro(m) if path_name(&m.mac.path) == "bitflags" => self.item_inner(item), | 2836 | Item::Macro(m) if path_name(&m.mac.path) == "bitflags" => self.item_inner(item), |
| 2799 | Item::ForeignMod(_) => self.item_inner(item), | 2837 | Item::ForeignMod(_) => self.item_inner(item), |
| 2800 | // A function-local type becomes a module-level type, so it is | 2838 | // A function-local type becomes a module-level type, so it is |
| @@ -6007,6 +6045,33 @@ impl Lowerer { | |||
| 6007 | } | 6045 | } |
| 6008 | 6046 | ||
| 6009 | // `i32::MAX` and friends: an associated const on a primitive. | 6047 | // `i32::MAX` and friends: an associated const on a primitive. |
| 6048 | + // The float constants, written out. Nim has its own spellings | ||
| 6049 | + // for some and none for others, and these are the values Rust | ||
| 6050 | + // defines rather than whatever a library happens to agree on. | ||
| 6051 | + if let Some(q) = p.path.segments.iter().rev().nth(1) { | ||
| 6052 | + let f32 = q.ident == "f32"; | ||
| 6053 | + if f32 || q.ident == "f64" { | ||
| 6054 | + let v = match name.as_str() { | ||
| 6055 | + "EPSILON" if f32 => Some("1.1920928955078125e-7'f32"), | ||
| 6056 | + "EPSILON" => Some("2.220446049250313e-16'f64"), | ||
| 6057 | + "MIN_POSITIVE" if f32 => Some("1.1754943508222875e-38'f32"), | ||
| 6058 | + "MIN_POSITIVE" => Some("2.2250738585072014e-308'f64"), | ||
| 6059 | + "INFINITY" if f32 => Some("float32(Inf)"), | ||
| 6060 | + "INFINITY" => Some("Inf"), | ||
| 6061 | + "NEG_INFINITY" if f32 => Some("float32(-Inf)"), | ||
| 6062 | + "NEG_INFINITY" => Some("-Inf"), | ||
| 6063 | + "NAN" if f32 => Some("float32(NaN)"), | ||
| 6064 | + "NAN" => Some("NaN"), | ||
| 6065 | + _ => None, | ||
| 6066 | + }; | ||
| 6067 | + if let Some(v) = v { | ||
| 6068 | + return Ok(Val::new( | ||
| 6069 | + v, | ||
| 6070 | + Some(Nim::Prim(if f32 { "float32" } else { "float64" }.into())), | ||
| 6071 | + )); | ||
| 6072 | + } | ||
| 6073 | + } | ||
| 6074 | + } | ||
| 6010 | if matches!(name.as_str(), "MAX" | "MIN") { | 6075 | if matches!(name.as_str(), "MAX" | "MIN") { |
| 6011 | if let Some(q) = p.path.segments.iter().rev().nth(1) { | 6076 | if let Some(q) = p.path.segments.iter().rev().nth(1) { |
| 6012 | if let Some(t @ Nim::Prim(_)) = ty::prim(&q.ident.to_string()) { | 6077 | if let Some(t @ Nim::Prim(_)) = ty::prim(&q.ident.to_string()) { |
| @@ -7784,6 +7849,25 @@ impl Lowerer { | |||
| 7784 | Some(Nim::Prim("bool".into())), | 7849 | Some(Nim::Prim("bool".into())), |
| 7785 | )); | 7850 | )); |
| 7786 | } | 7851 | } |
| 7852 | + // `f64::from_bits(u)` reinterprets the integer's bytes as a | ||
| 7853 | + // float, which is what Nim's `cast` means. The inverse of | ||
| 7854 | + // `to_bits`, and the width comes from the type it is called on. | ||
| 7855 | + "from_bits" if codes.len() == 1 => { | ||
| 7856 | + if let Some(q) = p.path.segments.iter().rev().nth(1) { | ||
| 7857 | + let (to, from) = match q.ident.to_string().as_str() { | ||
| 7858 | + "f32" => ("float32", "uint32"), | ||
| 7859 | + "f64" => ("float64", "uint64"), | ||
| 7860 | + _ => ("", ""), | ||
| 7861 | + }; | ||
| 7862 | + if !to.is_empty() { | ||
| 7863 | + let v = self.expr_at(&c.args[0], Some(&Nim::Prim(from.into())))?; | ||
| 7864 | + return Ok(Val::new( | ||
| 7865 | + format!("cast[{}]({}({}))", to, from, v.code), | ||
| 7866 | + Some(Nim::Prim(to.into())), | ||
| 7867 | + )); | ||
| 7868 | + } | ||
| 7869 | + } | ||
| 7870 | + } | ||
| 7787 | // `cmp::max` / `cmp::min` are Nim's `max` / `min` on the same | 7871 | // `cmp::max` / `cmp::min` are Nim's `max` / `min` on the same |
| 7788 | // ordering. | 7872 | // ordering. |
| 7789 | "max" | "min" if codes.len() == 2 => { | 7873 | "max" | "min" if codes.len() == 2 => { |