diff --git a/crates/nargo_cli/src/cli/check_cmd.rs b/crates/nargo_cli/src/cli/check_cmd.rs index 4453616f32f..ef1cff5993d 100644 --- a/crates/nargo_cli/src/cli/check_cmd.rs +++ b/crates/nargo_cli/src/cli/check_cmd.rs @@ -105,7 +105,7 @@ fn create_input_toml_template( #[cfg(test)] mod tests { - use std::{collections::BTreeMap, path::PathBuf}; + use std::path::PathBuf; use noirc_abi::{AbiParameter, AbiType, AbiVisibility, Sign}; use noirc_driver::CompileOptions; @@ -128,13 +128,13 @@ mod tests { typed_param( "d", AbiType::Struct { - fields: BTreeMap::from([ + fields: vec![ (String::from("d1"), AbiType::Field), ( String::from("d2"), AbiType::Array { length: 3, typ: Box::new(AbiType::Field) }, ), - ]), + ], }, ), typed_param("e", AbiType::Boolean), diff --git a/crates/nargo_cli/tests/test_data/struct_fields_ordering/Nargo.toml b/crates/nargo_cli/tests/test_data/struct_fields_ordering/Nargo.toml new file mode 100644 index 00000000000..e0b467ce5da --- /dev/null +++ b/crates/nargo_cli/tests/test_data/struct_fields_ordering/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data/struct_fields_ordering/Prover.toml b/crates/nargo_cli/tests/test_data/struct_fields_ordering/Prover.toml new file mode 100644 index 00000000000..70640bba4cc --- /dev/null +++ b/crates/nargo_cli/tests/test_data/struct_fields_ordering/Prover.toml @@ -0,0 +1,3 @@ +[y] +foo = "5" +bar = "7" diff --git a/crates/nargo_cli/tests/test_data/struct_fields_ordering/src/main.nr b/crates/nargo_cli/tests/test_data/struct_fields_ordering/src/main.nr new file mode 100644 index 00000000000..0d6e411addf --- /dev/null +++ b/crates/nargo_cli/tests/test_data/struct_fields_ordering/src/main.nr @@ -0,0 +1,14 @@ +use dep::std; + +// Note that fields are not in alphabetical order. +// We want to check that this ordering is maintained +struct myStruct { + foo: u32, + bar: Field, +} + +fn main(y : pub myStruct) { + assert(y.foo == 5); + assert(y.bar == 7); +} + diff --git a/crates/noirc_abi/src/input_parser/mod.rs b/crates/noirc_abi/src/input_parser/mod.rs index 1b54cb84df8..7eaa5c6322c 100644 --- a/crates/noirc_abi/src/input_parser/mod.rs +++ b/crates/noirc_abi/src/input_parser/mod.rs @@ -50,9 +50,12 @@ impl InputValue { if map.len() != fields.len() { return false; } + + let field_types = BTreeMap::from_iter(fields.iter().cloned()); + // Check that all of the struct's fields' values match the ABI as well. map.iter().all(|(field_name, field_value)| { - if let Some(field_type) = fields.get(field_name) { + if let Some(field_type) = field_types.get(field_name) { field_value.matches_abi(field_type) } else { false @@ -137,13 +140,13 @@ mod serialization_tests { AbiParameter { name: "bar".into(), typ: AbiType::Struct { - fields: BTreeMap::from([ + fields: vec![ ("field1".into(), AbiType::Integer { sign: Sign::Unsigned, width: 8 }), ( "field2".into(), AbiType::Array { length: 2, typ: Box::new(AbiType::Boolean) }, ), - ]), + ], }, visibility: AbiVisibility::Private, }, diff --git a/crates/noirc_abi/src/lib.rs b/crates/noirc_abi/src/lib.rs index 191128b9407..481e31f08c4 100644 --- a/crates/noirc_abi/src/lib.rs +++ b/crates/noirc_abi/src/lib.rs @@ -59,7 +59,7 @@ pub enum AbiType { serialize_with = "serialization::serialize_struct_fields", deserialize_with = "serialization::deserialize_struct_fields" )] - fields: BTreeMap, + fields: Vec<(String, AbiType)>, }, String { length: u64, @@ -249,7 +249,7 @@ impl Abi { return Err(AbiError::TypeMismatch { param, value }); } - Self::encode_value(value).map(|v| (param_name, v)) + Self::encode_value(value, &expected_type).map(|v| (param_name, v)) }) .collect::>()?; @@ -275,7 +275,7 @@ impl Abi { value: return_value, }); } - let encoded_return_fields = Self::encode_value(return_value)?; + let encoded_return_fields = Self::encode_value(return_value, return_type)?; // We need to be more careful when writing the return value's witness values. // This is as it may share witness indices with other public inputs so we must check that when @@ -300,7 +300,7 @@ impl Abi { Ok(witness_map) } - fn encode_value(value: InputValue) -> Result, AbiError> { + fn encode_value(value: InputValue, abi_type: &AbiType) -> Result, AbiError> { let mut encoded_value = Vec::new(); match value { InputValue::Field(elem) => encoded_value.push(elem), @@ -310,11 +310,14 @@ impl Abi { string.bytes().map(|byte| FieldElement::from_be_bytes_reduce(&[byte])); encoded_value.extend(str_as_fields); } - InputValue::Struct(object) => { - for value in object.into_values() { - encoded_value.extend(Self::encode_value(value)?); + InputValue::Struct(object) => match abi_type { + AbiType::Struct { fields } => { + for (field, typ) in fields { + encoded_value.extend(Self::encode_value(object[field].clone(), typ)?); + } } - } + _ => unreachable!("value should have already been checked to match abi type"), + }, } Ok(encoded_value) } diff --git a/crates/noirc_abi/src/serialization.rs b/crates/noirc_abi/src/serialization.rs index 1156ad4473f..3fdf5507383 100644 --- a/crates/noirc_abi/src/serialization.rs +++ b/crates/noirc_abi/src/serialization.rs @@ -1,5 +1,5 @@ +use iter_extended::vecmap; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::collections::BTreeMap; use crate::AbiType; @@ -19,34 +19,30 @@ struct StructField { } pub(crate) fn serialize_struct_fields( - fields: &BTreeMap, + fields: &[(String, AbiType)], s: S, ) -> Result where S: Serializer, { - let fields_vector: Vec = fields - .iter() - .map(|(name, typ)| StructField { name: name.to_owned(), typ: typ.to_owned() }) - .collect(); + let fields_vector = + vecmap(fields, |(name, typ)| StructField { name: name.to_owned(), typ: typ.to_owned() }); + fields_vector.serialize(s) } pub(crate) fn deserialize_struct_fields<'de, D>( deserializer: D, -) -> Result, D::Error> +) -> Result, D::Error> where D: Deserializer<'de>, { let fields_vector = Vec::::deserialize(deserializer)?; - let fields = fields_vector.into_iter().map(|StructField { name, typ }| (name, typ)).collect(); - Ok(fields) + Ok(vecmap(fields_vector, |StructField { name, typ }| (name, typ))) } #[cfg(test)] mod tests { - use std::collections::BTreeMap; - use crate::{AbiParameter, AbiType, AbiVisibility, Sign}; #[test] @@ -123,13 +119,13 @@ mod tests { let expected_struct = AbiParameter { name: "thing3".to_string(), typ: AbiType::Struct { - fields: BTreeMap::from([ + fields: vec![ ("field1".to_string(), AbiType::Integer { sign: Sign::Unsigned, width: 3 }), ( "field2".to_string(), AbiType::Array { length: 2, typ: Box::new(AbiType::Field) }, ), - ]), + ], }, visibility: AbiVisibility::Private, }; diff --git a/crates/noirc_evaluator/src/lib.rs b/crates/noirc_evaluator/src/lib.rs index 533adce4325..94878edffae 100644 --- a/crates/noirc_evaluator/src/lib.rs +++ b/crates/noirc_evaluator/src/lib.rs @@ -17,7 +17,7 @@ use acvm::{ Language, }; use errors::{RuntimeError, RuntimeErrorKind}; -use iter_extended::btree_map; +use iter_extended::vecmap; use noirc_abi::{Abi, AbiType, AbiVisibility}; use noirc_frontend::monomorphization::ast::*; use ssa::{node::ObjectType, ssa_gen::IrGenerator}; @@ -218,7 +218,7 @@ impl Evaluator { vec![witness] } AbiType::Struct { fields } => { - let new_fields = btree_map(fields, |(inner_name, value)| { + let new_fields = vecmap(fields, |(inner_name, value)| { let new_name = format!("{name}.{inner_name}"); (new_name, value.clone()) }); @@ -227,7 +227,44 @@ impl Evaluator { self.generate_struct_witnesses(&mut struct_witnesses, &new_fields)?; ir_gen.abi_struct(name, Some(def), fields, &struct_witnesses); - struct_witnesses.values().flatten().copied().collect() + + // This is a dirty hack and should be removed in future. + // + // `struct_witnesses` is a flat map where structs are represented by multiple entries + // i.e. a struct `foo` with fields `bar` and `baz` is stored under the keys + // `foo.bar` and `foo.baz` each holding the witnesses for fields `bar` and `baz` respectively. + // + // We've then lost the information on ordering of these fields. To reconstruct this we iterate + // over `fields` recursively to calculate the proper ordering of this `BTreeMap`s keys. + // + // Ideally we wouldn't lose this information in the first place. + fn get_field_ordering(prefix: String, fields: &[(String, AbiType)]) -> Vec { + fields + .iter() + .flat_map(|(field_name, field_type)| { + let flattened_name = format!("{prefix}.{field_name}"); + if let AbiType::Struct { fields } = field_type { + get_field_ordering(flattened_name, fields) + } else { + vec![flattened_name] + } + }) + .collect() + } + let field_ordering = get_field_ordering(name.to_owned(), fields); + + // We concatenate the witness vectors in the order of the struct's fields. + // This ensures that struct fields are mapped to the correct witness indices during ABI encoding. + field_ordering + .iter() + .flat_map(|field_name| { + struct_witnesses.remove(field_name).unwrap_or_else(|| { + unreachable!( + "Expected a field named '{field_name}' in the struct pattern" + ) + }) + }) + .collect() } AbiType::String { length } => { let typ = AbiType::Integer { sign: noirc_abi::Sign::Unsigned, width: 8 }; @@ -248,7 +285,7 @@ impl Evaluator { fn generate_struct_witnesses( &mut self, struct_witnesses: &mut BTreeMap>, - fields: &BTreeMap, + fields: &[(String, AbiType)], ) -> Result<(), RuntimeErrorKind> { for (name, typ) in fields { match typ { @@ -271,11 +308,10 @@ impl Evaluator { struct_witnesses.insert(name.clone(), internal_arr_witnesses); } AbiType::Struct { fields, .. } => { - let mut new_fields: BTreeMap = BTreeMap::new(); - for (inner_name, value) in fields { - let new_name = format!("{name}.{inner_name}"); - new_fields.insert(new_name, value.clone()); - } + let new_fields = vecmap(fields, |(field_name, typ)| { + let new_name = format!("{name}.{field_name}"); + (new_name, typ.clone()) + }); self.generate_struct_witnesses(struct_witnesses, &new_fields)?; } AbiType::String { length } => { diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen.rs b/crates/noirc_evaluator/src/ssa/ssa_gen.rs index ae35855b5fa..016985dcb0f 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen.rs @@ -124,7 +124,7 @@ impl IrGenerator { &mut self, struct_name: &str, ident_def: Option, - fields: &BTreeMap, + fields: &[(String, noirc_abi::AbiType)], witnesses: &BTreeMap>, ) -> Value { let values = vecmap(fields, |(name, field_typ)| { diff --git a/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs b/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs index 55f2464dc62..ef2fb84db57 100644 --- a/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -19,7 +19,7 @@ use fm::FileId; use iter_extended::vecmap; use noirc_errors::Span; use noirc_errors::{CustomDiagnostic, FileDiagnostic}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::rc::Rc; /// Stores all of the unresolved functions in a particular file/mod @@ -346,7 +346,7 @@ fn resolve_struct_fields( krate: CrateId, unresolved: UnresolvedStruct, all_errors: &mut Vec, -) -> (Generics, BTreeMap) { +) -> (Generics, Vec<(Ident, Type)>) { let path_resolver = StandardPathResolver::new(ModuleId { local_id: unresolved.module_id, krate }); diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index 60da0b64682..5727741b1e2 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -18,7 +18,7 @@ use crate::hir_def::expr::{ HirMethodCallExpression, HirPrefixExpression, }; use crate::token::Attribute; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::rc::Rc; use crate::graph::CrateId; @@ -567,17 +567,13 @@ impl<'a> Resolver<'a> { pub fn resolve_struct_fields( mut self, unresolved: NoirStruct, - ) -> (Generics, BTreeMap, Vec) { + ) -> (Generics, Vec<(Ident, Type)>, Vec) { let generics = self.add_generics(&unresolved.generics); // Check whether the struct definition has globals in the local module and add them to the scope self.resolve_local_globals(); - let fields = unresolved - .fields - .into_iter() - .map(|(ident, typ)| (ident, self.resolve_type(typ))) - .collect(); + let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, self.resolve_type(typ))); (generics, fields, self.errors) } @@ -1094,7 +1090,7 @@ impl<'a> Resolver<'a> { ) -> Vec<(Ident, U)> { let mut ret = Vec::with_capacity(fields.len()); let mut seen_fields = HashSet::new(); - let mut unseen_fields = self.get_field_names_of_type(&struct_type); + let mut unseen_fields = struct_type.borrow().field_names(); for (field, expr) in fields { let resolved = resolve_function(self, expr); @@ -1131,10 +1127,6 @@ impl<'a> Resolver<'a> { self.interner.get_struct(type_id) } - fn get_field_names_of_type(&self, typ: &Shared) -> BTreeSet { - typ.borrow().field_names() - } - fn lookup(&mut self, path: Path) -> Result { let span = path.span(); let id = self.resolve_path(path)?; diff --git a/crates/noirc_frontend/src/hir/type_check/expr.rs b/crates/noirc_frontend/src/hir/type_check/expr.rs index 8a91ecbfde8..55a14860a1f 100644 --- a/crates/noirc_frontend/src/hir/type_check/expr.rs +++ b/crates/noirc_frontend/src/hir/type_check/expr.rs @@ -427,9 +427,10 @@ impl<'interner> TypeChecker<'interner> { // Note that we use a Vec to store the original arguments (rather than a BTreeMap) to // preserve the evaluation order of the source code. let mut args = constructor.fields; - args.sort_by_key(|arg| arg.0.clone()); + args.sort_by_key(|(name, _)| name.clone()); - let fields = typ.borrow().get_fields(&generics); + let mut fields = typ.borrow().get_fields(&generics); + fields.sort_by_key(|(name, _)| name.clone()); for ((param_name, param_type), (arg_ident, arg)) in fields.into_iter().zip(args) { // This can be false if the user provided an incorrect field count. That error should diff --git a/crates/noirc_frontend/src/hir/type_check/stmt.rs b/crates/noirc_frontend/src/hir/type_check/stmt.rs index 7bd50392404..e6ec71bfc10 100644 --- a/crates/noirc_frontend/src/hir/type_check/stmt.rs +++ b/crates/noirc_frontend/src/hir/type_check/stmt.rs @@ -81,15 +81,13 @@ impl<'interner> TypeChecker<'interner> { }); if let Type::Struct(struct_type, generics) = struct_type { - let mut pattern_fields = fields.clone(); - pattern_fields.sort_by_key(|(ident, _)| ident.clone()); let struct_type = struct_type.borrow(); - for (field_name, field_pattern) in pattern_fields { + for (field_name, field_pattern) in fields { if let Some((type_field, _)) = struct_type.get_field(&field_name.0.contents, generics) { - self.bind_pattern(&field_pattern, type_field); + self.bind_pattern(field_pattern, type_field); } } } diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index 9a6f83ddd50..133d8a79055 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -1,11 +1,11 @@ use std::{ cell::RefCell, - collections::{BTreeMap, BTreeSet, HashMap}, + collections::{BTreeSet, HashMap}, rc::Rc, }; use crate::{hir::type_check::TypeCheckError, node_interner::NodeInterner}; -use iter_extended::{btree_map, vecmap}; +use iter_extended::vecmap; use noirc_abi::AbiType; use noirc_errors::Span; @@ -112,7 +112,7 @@ pub struct StructType { /// Fields are ordered and private, they should only /// be accessed through get_field(), get_fields(), or instantiate() /// since these will handle applying generic arguments to fields as well. - fields: BTreeMap, + fields: Vec<(Ident, Type)>, pub generics: Generics, pub span: Span, @@ -141,7 +141,7 @@ impl StructType { id: StructId, name: Ident, span: Span, - fields: BTreeMap, + fields: Vec<(Ident, Type)>, generics: Generics, ) -> StructType { StructType { id, fields, name, span, generics } @@ -151,7 +151,7 @@ impl StructType { /// fields are resolved strictly after the struct itself is initially /// created. Therefore, this method is used to set the fields once they /// become known. - pub fn set_fields(&mut self, fields: BTreeMap) { + pub fn set_fields(&mut self, fields: Vec<(Ident, Type)>) { assert!(self.fields.is_empty()); self.fields = fields; } @@ -179,7 +179,7 @@ impl StructType { } /// Returns all the fields of this type, after being applied to the given generic arguments. - pub fn get_fields(&self, generic_args: &[Type]) -> BTreeMap { + pub fn get_fields(&self, generic_args: &[Type]) -> Vec<(String, Type)> { assert_eq!(self.generics.len(), generic_args.len()); let substitutions = self @@ -189,17 +189,14 @@ impl StructType { .map(|((old_id, old_var), new)| (*old_id, (old_var.clone(), new.clone()))) .collect(); - self.fields - .iter() - .map(|(name, typ)| { - let name = name.0.contents.clone(); - (name, typ.substitute(&substitutions)) - }) - .collect() + vecmap(&self.fields, |(name, typ)| { + let name = name.0.contents.clone(); + (name, typ.substitute(&substitutions)) + }) } pub fn field_names(&self) -> BTreeSet { - self.fields.keys().cloned().collect() + self.fields.iter().map(|(name, _)| name.clone()).collect() } /// True if the given index is the same index as a generic type of this struct @@ -1181,8 +1178,8 @@ impl Type { Type::Struct(def, args) => { let struct_type = def.borrow(); let fields = struct_type.get_fields(args); - let abi_map = btree_map(fields, |(name, typ)| (name, typ.as_abi_type())); - AbiType::Struct { fields: abi_map } + let fields = vecmap(fields, |(name, typ)| (name, typ.as_abi_type())); + AbiType::Struct { fields } } Type::Tuple(_) => todo!("as_abi_type not yet implemented for tuple types"), Type::TypeVariable(_) => unreachable!(), diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 3c3c602d132..3508576669f 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -228,10 +228,18 @@ impl<'interner> Monomorphizer<'interner> { } HirPattern::Struct(_, fields, _) => { let struct_field_types = unwrap_struct_type(typ); + assert_eq!(struct_field_types.len(), fields.len()); - for (name, field) in fields { - let typ = &struct_field_types[&name.0.contents]; - self.parameter(field, typ, new_params); + let mut fields = btree_map(fields, |(name, field)| (name.0.contents, field)); + + // Iterate over `struct_field_types` since `unwrap_struct_type` will always + // return the fields in the order defined by the struct type. + for (field_name, field_type) in struct_field_types { + let field = fields.remove(&field_name).unwrap_or_else(|| { + unreachable!("Expected a field named '{field_name}' in the struct pattern") + }); + + self.parameter(field, &field_type, new_params); } } } @@ -461,6 +469,8 @@ impl<'interner> Monomorphizer<'interner> { let typ = self.interner.id_type(id); let field_types = unwrap_struct_type(&typ); + let field_type_map = btree_map(&field_types, |x| x.clone()); + // Create let bindings for each field value first to preserve evaluation order before // they are reordered and packed into the resulting tuple let mut field_vars = BTreeMap::new(); @@ -468,7 +478,7 @@ impl<'interner> Monomorphizer<'interner> { for (field_name, expr_id) in constructor.fields { let new_id = self.next_local_id(); - let field_type = field_types.get(&field_name.0.contents).unwrap(); + let field_type = field_type_map.get(&field_name.0.contents).unwrap(); let typ = Self::convert_type(field_type); field_vars.insert(field_name.0.contents.clone(), (new_id, typ)); @@ -482,14 +492,21 @@ impl<'interner> Monomorphizer<'interner> { })); } - let sorted_fields = vecmap(field_vars, |(name, (id, typ))| { + // We must ensure the tuple created from the variables here matches the order + // of the fields as defined in the type. To do this, we iterate over field_types, + // rather than field_type_map which is a sorted BTreeMap. + let field_idents = vecmap(field_types, |(name, _)| { + let (id, typ) = field_vars.remove(&name).unwrap_or_else(|| { + unreachable!("Expected field {name} to be present in constructor for {typ}") + }); + let definition = Definition::Local(id); let mutable = false; ast::Expression::Ident(ast::Ident { definition, mutable, location: None, name, typ }) }); // Finally we can return the created Tuple from the new block - new_exprs.push(ast::Expression::Tuple(sorted_fields)); + new_exprs.push(ast::Expression::Tuple(field_idents)); ast::Expression::Block(new_exprs) } @@ -523,13 +540,18 @@ impl<'interner> Monomorphizer<'interner> { } HirPattern::Struct(_, patterns, _) => { let fields = unwrap_struct_type(typ); - // We map each pattern to its respective field in a BTreeMap - // Fields in struct types are ordered, and doing this map guarantees we extract the correct field index - let patterns_map = btree_map(patterns, |(ident, pattern)| { - let typ = fields[&ident.0.contents].clone(); - (ident.0.contents, (pattern, typ)) + assert_eq!(patterns.len(), fields.len()); + + let mut patterns = + btree_map(patterns, |(name, pattern)| (name.0.contents, pattern)); + + // We iterate through the type's fields to match the order defined in the struct type + let patterns_iter = fields.into_iter().map(|(field_name, field_type)| { + let pattern = patterns.remove(&field_name).unwrap(); + (pattern, field_type) }); - self.unpack_tuple_pattern(value, patterns_map.into_values()) + + self.unpack_tuple_pattern(value, patterns_iter) } } } @@ -999,7 +1021,7 @@ fn unwrap_tuple_type(typ: &HirType) -> Vec { } } -fn unwrap_struct_type(typ: &HirType) -> BTreeMap { +fn unwrap_struct_type(typ: &HirType) -> Vec<(String, HirType)> { match typ { HirType::Struct(def, args) => def.borrow().get_fields(args), HirType::TypeVariable(binding) => match &*binding.borrow() { diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index a318d361510..d4b05dfddb1 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use arena::{Arena, Index}; use fm::FileId; @@ -298,7 +298,7 @@ impl NodeInterner { type_id, typ.struct_def.name.clone(), typ.struct_def.span, - BTreeMap::new(), + Vec::new(), vecmap(&typ.struct_def.generics, |_| { // Temporary type variable ids before the struct is resolved to its actual ids. // This lets us record how many arguments the type expects so that other types