nandi/rustnimpublic Fork
⑂ daf0fcd
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/rustnim.git
git clone ssh://git@rickub.com/nandi/rustnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

Resolve an enum variant before a unit struct of the same name

`State::Horizontal` was being read as a unit struct called `Horizontal` --
declared by an entirely different crate -- because the unit-struct case
matched on the path's last segment alone and ran first. Variant resolution
looks at the qualifier, so it goes first now.

Two things this also fixes about finding such a bug:

  * Enum discriminants are lowered in a pass that runs after all the files,
    not inside one, so its errors carried no module at all. Enums now record
    which input declared them; the message names the file and the enum.
  * That pass walked a `HashMap`, so with three hundred enums it was undefined
    *which* failure got reported. Sorted. An error that moves between runs is
    not one anybody can chase, and I chased this one into the wrong crate
    twice before noticing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-19T21:45:19-07:00 Browse files
daf0fcd parent: 16e1eca
modified src/lower.rs +26 -8
@@ -370,6 +370,10 @@ pub struct Lowerer {
370370 /// function in it is untranslatable answers a question nobody asked. The
371371 /// error belongs at the call site, and that is where it is raised.
372372 untranslatable: HashMap<String, String>,
373+ /// Which input declared each enum, so the pass that lowers discriminants
374+ /// -- which runs after all the files, not inside one -- can say where a
375+ /// failure came from.
376+ enum_module: HashMap<String, String>,
373377 /// Bounds discovered while rewriting `impl Trait` parameters. `rewrite_apit`
374378 /// runs from `&self`, so they are parked here and folded into `bounds` by
375379 /// whoever called it.
@@ -471,6 +475,7 @@ impl Lowerer {
471475 trait_methods: HashMap::new(),
472476 fn_blankets: HashMap::new(),
473477 untranslatable: HashMap::new(),
478+ enum_module: HashMap::new(),
474479 pending_apit_bounds: std::cell::RefCell::new(Vec::new()),
475480 bounds: HashMap::new(),
476481 const_names_at: std::cell::Cell::new(None),
@@ -1049,6 +1054,7 @@ impl Lowerer {
10491054 .or_default()
10501055 .push(name.clone());
10511056 }
1057+ self.enum_module.insert(name.clone(), self.cur_mod.clone());
10521058 self.enums.insert(
10531059 name.clone(),
10541060 EnumDef {
@@ -2201,13 +2207,22 @@ impl Lowerer {
22012207
22022208 /// Lower every enum discriminant, after all signatures are collected.
22032209 fn resolve_discriminants(&mut self) -> Result<(), String> {
2204- let names: Vec<String> = self.enums.keys().cloned().collect();
2210+ // Sorted: a `HashMap`'s order is not defined, and with three hundred
2211+ // enums that decides *which* failure gets reported. An error that
2212+ // moves between runs is not one anybody can chase.
2213+ let mut names: Vec<String> = self.enums.keys().cloned().collect();
2214+ names.sort();
22052215 for n in names {
22062216 let Some(def) = self.enums.get(&n).cloned() else { continue };
22072217 let mut out = def.clone();
2218+ self.cur_mod = self.enum_module.get(&n).cloned().unwrap_or_default();
22082219 for (i, v) in def.variants.iter().enumerate() {
22092220 let Some(e) = &v.disc_expr else { continue };
2210- out.variants[i].discriminant = Some(self.expr(e)?.code);
2221+ let code = self
2222+ .expr(e)
2223+ .map_err(|err| self.where_(&format!("`enum {n}`: {err}")))?
2224+ .code;
2225+ out.variants[i].discriminant = Some(code);
22112226 }
22122227 self.enums.insert(n, out);
22132228 }
@@ -5204,13 +5219,10 @@ impl Lowerer {
52045219 }
52055220 }
52065221 }
5207- if self.structs.get(&name).is_some_and(|f| f.is_empty()) {
5208- return Ok(Val::new(
5209- format!("{}()", ident(&name)),
5210- Some(Nim::Named(name.clone(), vec![])),
5211- ));
5212- }
52135222 // A unit enum variant used as a value: `Error::InvalidLength`.
5223+ // Before the unit-struct case below, which matches on the last
5224+ // segment alone: `State::Horizontal` is that enum's variant,
5225+ // whatever some other crate calls a struct of its own.
52145226 if let Some((def, v)) = self.resolve_variant(&p.path) {
52155227 let (ty, targs) = self.variant_type(&def, expect)?;
52165228 return Ok(if def.simple && targs.is_empty() {
@@ -5224,6 +5236,12 @@ impl Lowerer {
52245236 )
52255237 });
52265238 }
5239+ if self.structs.get(&name).is_some_and(|f| f.is_empty()) {
5240+ return Ok(Val::new(
5241+ format!("{}()", ident(&name)),
5242+ Some(Nim::Named(name.clone(), vec![])),
5243+ ));
5244+ }
52275245 // A `for` binding that stands for an element of the container
52285246 // it came from: using it must read (and assigning through it
52295247 // must write) that element, not a copy.
@@ -370,6 +370,10 @@ pub struct Lowerer {
370 /// function in it is untranslatable answers a question nobody asked. The370 /// function in it is untranslatable answers a question nobody asked. The
371 /// error belongs at the call site, and that is where it is raised.371 /// error belongs at the call site, and that is where it is raised.
372 untranslatable: HashMap<String, String>,372 untranslatable: HashMap<String, String>,
373+ /// Which input declared each enum, so the pass that lowers discriminants
374+ /// -- which runs after all the files, not inside one -- can say where a
375+ /// failure came from.
376+ enum_module: HashMap<String, String>,
373 /// Bounds discovered while rewriting `impl Trait` parameters. `rewrite_apit`377 /// Bounds discovered while rewriting `impl Trait` parameters. `rewrite_apit`
374 /// runs from `&self`, so they are parked here and folded into `bounds` by378 /// runs from `&self`, so they are parked here and folded into `bounds` by
375 /// whoever called it.379 /// whoever called it.
@@ -471,6 +475,7 @@ impl Lowerer {
471 trait_methods: HashMap::new(),475 trait_methods: HashMap::new(),
472 fn_blankets: HashMap::new(),476 fn_blankets: HashMap::new(),
473 untranslatable: HashMap::new(),477 untranslatable: HashMap::new(),
478+ enum_module: HashMap::new(),
474 pending_apit_bounds: std::cell::RefCell::new(Vec::new()),479 pending_apit_bounds: std::cell::RefCell::new(Vec::new()),
475 bounds: HashMap::new(),480 bounds: HashMap::new(),
476 const_names_at: std::cell::Cell::new(None),481 const_names_at: std::cell::Cell::new(None),
@@ -1049,6 +1054,7 @@ impl Lowerer {
1049 .or_default()1054 .or_default()
1050 .push(name.clone());1055 .push(name.clone());
1051 }1056 }
1057+ self.enum_module.insert(name.clone(), self.cur_mod.clone());
1052 self.enums.insert(1058 self.enums.insert(
1053 name.clone(),1059 name.clone(),
1054 EnumDef {1060 EnumDef {
@@ -2201,13 +2207,22 @@ impl Lowerer {
2201 2207
2202 /// Lower every enum discriminant, after all signatures are collected.2208 /// Lower every enum discriminant, after all signatures are collected.
2203 fn resolve_discriminants(&mut self) -> Result<(), String> {2209 fn resolve_discriminants(&mut self) -> Result<(), String> {
2204- let names: Vec<String> = self.enums.keys().cloned().collect();2210+ // Sorted: a `HashMap`'s order is not defined, and with three hundred
2211+ // enums that decides *which* failure gets reported. An error that
2212+ // moves between runs is not one anybody can chase.
2213+ let mut names: Vec<String> = self.enums.keys().cloned().collect();
2214+ names.sort();
2205 for n in names {2215 for n in names {
2206 let Some(def) = self.enums.get(&n).cloned() else { continue };2216 let Some(def) = self.enums.get(&n).cloned() else { continue };
2207 let mut out = def.clone();2217 let mut out = def.clone();
2218+ self.cur_mod = self.enum_module.get(&n).cloned().unwrap_or_default();
2208 for (i, v) in def.variants.iter().enumerate() {2219 for (i, v) in def.variants.iter().enumerate() {
2209 let Some(e) = &v.disc_expr else { continue };2220 let Some(e) = &v.disc_expr else { continue };
2210- out.variants[i].discriminant = Some(self.expr(e)?.code);2221+ let code = self
2222+ .expr(e)
2223+ .map_err(|err| self.where_(&format!("`enum {n}`: {err}")))?
2224+ .code;
2225+ out.variants[i].discriminant = Some(code);
2211 }2226 }
2212 self.enums.insert(n, out);2227 self.enums.insert(n, out);
2213 }2228 }
@@ -5204,13 +5219,10 @@ impl Lowerer {
5204 }5219 }
5205 }5220 }
5206 }5221 }
5207- if self.structs.get(&name).is_some_and(|f| f.is_empty()) {
5208- return Ok(Val::new(
5209- format!("{}()", ident(&name)),
5210- Some(Nim::Named(name.clone(), vec![])),
5211- ));
5212- }
5213 // A unit enum variant used as a value: `Error::InvalidLength`.5222 // A unit enum variant used as a value: `Error::InvalidLength`.
5223+ // Before the unit-struct case below, which matches on the last
5224+ // segment alone: `State::Horizontal` is that enum's variant,
5225+ // whatever some other crate calls a struct of its own.
5214 if let Some((def, v)) = self.resolve_variant(&p.path) {5226 if let Some((def, v)) = self.resolve_variant(&p.path) {
5215 let (ty, targs) = self.variant_type(&def, expect)?;5227 let (ty, targs) = self.variant_type(&def, expect)?;
5216 return Ok(if def.simple && targs.is_empty() {5228 return Ok(if def.simple && targs.is_empty() {
@@ -5224,6 +5236,12 @@ impl Lowerer {
5224 )5236 )
5225 });5237 });
5226 }5238 }
5239+ if self.structs.get(&name).is_some_and(|f| f.is_empty()) {
5240+ return Ok(Val::new(
5241+ format!("{}()", ident(&name)),
5242+ Some(Nim::Named(name.clone(), vec![])),
5243+ ));
5244+ }
5227 // A `for` binding that stands for an element of the container5245 // A `for` binding that stands for an element of the container
5228 // it came from: using it must read (and assigning through it5246 // it came from: using it must read (and assigning through it
5229 // must write) that element, not a copy.5247 // must write) that element, not a copy.