From 77de24ef742061b7da11e09a3db8b15174f97194 Mon Sep 17 00:00:00 2001 From: David Mak Date: Mon, 4 Mar 2024 23:38:52 +0800 Subject: [PATCH] core: Use BTreeMap for type variable mapping There have been multiple instances where I had the need to iterate over type variables, only to discover that the traversal order is arbitrary. This commit fixes that by adding SortedMapping, which utilizes BTreeMap internally to guarantee a traversal order. All instances of VarMap are now refactored to use this to ensure that type variables are iterated in the order of its variable ID, which should be monotonically incremented by the unifier. --- nac3artiq/src/codegen.rs | 4 +- nac3artiq/src/lib.rs | 10 ++-- nac3artiq/src/symbol_resolver.rs | 10 ++-- nac3core/src/codegen/concrete_type.rs | 6 +-- nac3core/src/codegen/expr.rs | 4 +- nac3core/src/codegen/test.rs | 5 +- nac3core/src/symbol_resolver.rs | 6 +-- nac3core/src/toplevel/builtins.rs | 19 +++---- nac3core/src/toplevel/composer.rs | 19 ++++--- nac3core/src/toplevel/helper.rs | 32 +++++------ nac3core/src/toplevel/mod.rs | 4 +- nac3core/src/toplevel/numpy.rs | 4 +- nac3core/src/toplevel/type_annotation.rs | 2 +- nac3core/src/typecheck/magic_methods.rs | 10 ++-- nac3core/src/typecheck/type_inferencer/mod.rs | 14 ++--- .../src/typecheck/type_inferencer/test.rs | 54 +++++++++---------- nac3core/src/typecheck/typedef/mod.rs | 17 ++++-- nac3core/src/typecheck/typedef/test.rs | 16 +++--- nac3standalone/src/main.rs | 9 ++-- 19 files changed, 129 insertions(+), 116 deletions(-) diff --git a/nac3artiq/src/codegen.rs b/nac3artiq/src/codegen.rs index 1b338aaf..1d8f6626 100644 --- a/nac3artiq/src/codegen.rs +++ b/nac3artiq/src/codegen.rs @@ -7,7 +7,7 @@ use nac3core::{ }, symbol_resolver::ValueEnum, toplevel::{DefinitionId, GenCall, helper::PRIMITIVE_DEF_IDS}, - typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum} + typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, VarMap} }; use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef}; @@ -667,7 +667,7 @@ pub fn attributes_writeback( default_value: None }).collect(), ret: ctx.primitives.none, - vars: HashMap::default() + vars: VarMap::default() }; let args: Vec<_> = values.into_iter().map(|(_, val)| (None, ValueEnum::Dynamic(val))).collect(); if let Err(e) = rpc_codegen_callback_fn(ctx, None, (&fun, PRIMITIVE_DEF_IDS.int32), args, generator) { diff --git a/nac3artiq/src/lib.rs b/nac3artiq/src/lib.rs index a1ae1df9..f8f029ff 100644 --- a/nac3artiq/src/lib.rs +++ b/nac3artiq/src/lib.rs @@ -16,7 +16,7 @@ use inkwell::{ use itertools::Itertools; use nac3core::codegen::{CodeGenLLVMOptions, CodeGenTargetMachineOptions, gen_func_impl}; use nac3core::toplevel::builtins::get_exn_constructor; -use nac3core::typecheck::typedef::{TypeEnum, Unifier}; +use nac3core::typecheck::typedef::{TypeEnum, Unifier, VarMap}; use nac3parser::{ ast::{ExprKind, Stmt, StmtKind, StrRef}, parser::parse_program, @@ -476,7 +476,7 @@ impl Nac3 { .unwrap(); let fun_signature = - FunSignature { args: vec![], ret: self.primitive.none, vars: HashMap::new() }; + FunSignature { args: vec![], ret: self.primitive.none, vars: VarMap::new() }; let mut store = ConcreteTypeStore::new(); let mut cache = HashMap::new(); let signature = @@ -816,7 +816,7 @@ impl Nac3 { let builtins = vec![ ( "now_mu".into(), - FunSignature { args: vec![], ret: primitive.int64, vars: HashMap::new() }, + FunSignature { args: vec![], ret: primitive.int64, vars: VarMap::new() }, Arc::new(GenCall::new(Box::new(move |ctx, _, _, _, _| { Ok(Some(time_fns.emit_now_mu(ctx))) }))), @@ -830,7 +830,7 @@ impl Nac3 { default_value: None, }], ret: primitive.none, - vars: HashMap::new(), + vars: VarMap::new(), }, Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| { let arg_ty = fun.0.args[0].ty; @@ -848,7 +848,7 @@ impl Nac3 { default_value: None, }], ret: primitive.none, - vars: HashMap::new(), + vars: VarMap::new(), }, Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| { let arg_ty = fun.0.args[0].ty; diff --git a/nac3artiq/src/symbol_resolver.rs b/nac3artiq/src/symbol_resolver.rs index 6c1130d0..ccba682a 100644 --- a/nac3artiq/src/symbol_resolver.rs +++ b/nac3artiq/src/symbol_resolver.rs @@ -10,7 +10,7 @@ use nac3core::{ }, typecheck::{ type_inferencer::PrimitiveStore, - typedef::{Type, TypeEnum, Unifier}, + typedef::{Type, TypeEnum, Unifier, VarMap}, }, }; use nac3parser::ast::{self, StrRef}; @@ -519,7 +519,7 @@ impl InnerResolver { .iter() .zip(args.iter()) .map(|((id, _), ty)| (*id, *ty)) - .collect::>() + .collect::() }; Ok(Ok((unifier.subst(origin_ty, &subst).unwrap_or(origin_ty), true))) } @@ -722,7 +722,7 @@ impl InnerResolver { assert_eq!(*id, *id_var); (*id, unifier.get_fresh_var_with_range(range, *name, *loc).0) }) - .collect::>(); + .collect::(); return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap())) } @@ -734,7 +734,7 @@ impl InnerResolver { ))) } }; - let new_var_map: HashMap<_, _> = params.iter().map(|(id, _)| (*id, ty)).collect(); + let new_var_map: VarMap = params.iter().map(|(id, _)| (*id, ty)).collect(); let res = unifier.subst(extracted_ty, &new_var_map).unwrap_or(extracted_ty); Ok(Ok(res)) } @@ -751,7 +751,7 @@ impl InnerResolver { assert_eq!(*id, *id_var); (*id, unifier.get_fresh_var_with_range(range, *name, *loc).0) }) - .collect::>(); + .collect::(); let mut instantiate_obj = || { // loop through non-function fields of the class to get the instantiated value for field in fields { diff --git a/nac3core/src/codegen/concrete_type.rs b/nac3core/src/codegen/concrete_type.rs index 77451600..30de4400 100644 --- a/nac3core/src/codegen/concrete_type.rs +++ b/nac3core/src/codegen/concrete_type.rs @@ -3,7 +3,7 @@ use crate::{ toplevel::DefinitionId, typecheck::{ type_inferencer::PrimitiveStore, - typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier}, + typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap}, }, }; @@ -274,7 +274,7 @@ impl ConcreteTypeStore { params: params .iter() .map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache))) - .collect::>(), + .collect::(), }, ConcreteTypeEnum::TFunc { args, ret, vars } => TypeEnum::TFunc(FunSignature { args: args @@ -289,7 +289,7 @@ impl ConcreteTypeStore { vars: vars .iter() .map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache))) - .collect::>(), + .collect::(), }), ConcreteTypeEnum::TLiteral { values, .. } => TypeEnum::TLiteral { values: values.clone(), diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index 360d8734..7bc16ee9 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -20,7 +20,7 @@ use crate::{ TopLevelDef, }, typecheck::{ - typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier}, + typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap}, magic_methods::{binop_name, binop_assign_name}, }, }; @@ -41,7 +41,7 @@ use super::{CodeGenerator, llvm_intrinsics::call_memcpy_generic, need_sret}; pub fn get_subst_key( unifier: &mut Unifier, obj: Option, - fun_vars: &HashMap, + fun_vars: &VarMap, filter: Option<&Vec>, ) -> String { let mut vars = obj diff --git a/nac3core/src/codegen/test.rs b/nac3core/src/codegen/test.rs index 2f6d2249..159ac2b4 100644 --- a/nac3core/src/codegen/test.rs +++ b/nac3core/src/codegen/test.rs @@ -25,6 +25,7 @@ use nac3parser::{ use parking_lot::RwLock; use std::collections::{HashMap, HashSet}; use std::sync::Arc; +use crate::typecheck::typedef::VarMap; struct Resolver { id_to_type: HashMap, @@ -111,7 +112,7 @@ fn test_primitives() { FuncArg { name: "b".into(), ty: primitives.int32, default_value: None }, ], ret: primitives.int32, - vars: HashMap::new(), + vars: VarMap::new(), }; let mut store = ConcreteTypeStore::new(); @@ -258,7 +259,7 @@ fn test_simple_call() { let signature = FunSignature { args: vec![FuncArg { name: "a".into(), ty: primitives.int32, default_value: None }], ret: primitives.int32, - vars: HashMap::new(), + vars: VarMap::new(), }; let fun_ty = unifier.add_ty(TypeEnum::TFunc(signature.clone())); let mut store = ConcreteTypeStore::new(); diff --git a/nac3core/src/symbol_resolver.rs b/nac3core/src/symbol_resolver.rs index 74a79d3f..de41ac1a 100644 --- a/nac3core/src/symbol_resolver.rs +++ b/nac3core/src/symbol_resolver.rs @@ -8,7 +8,7 @@ use crate::{ toplevel::{DefinitionId, TopLevelDef, type_annotation::TypeAnnotation}, typecheck::{ type_inferencer::PrimitiveStore, - typedef::{Type, TypeEnum, Unifier}, + typedef::{Type, TypeEnum, Unifier, VarMap}, }, }; use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue}; @@ -426,7 +426,7 @@ pub fn parse_type_annotation( Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, - params: HashMap::default(), + params: VarMap::default(), })) } else { Err(HashSet::from([ @@ -515,7 +515,7 @@ pub fn parse_type_annotation( ), ])) } - let mut subst = HashMap::new(); + let mut subst = VarMap::new(); for (var, ty) in izip!(type_vars.iter(), types.iter()) { let id = if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*var) { *id diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index 635145d9..3b90918f 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -10,6 +10,7 @@ use crate::{ symbol_resolver::SymbolValue, toplevel::helper::PRIMITIVE_DEF_IDS, toplevel::numpy::*, + typecheck::typedef::VarMap, }; use inkwell::{ attributes::{Attribute, AttributeLoc}, @@ -56,12 +57,12 @@ pub fn get_exn_constructor( let exn_type = unifier.add_ty(TypeEnum::TObj { obj_id: DefinitionId(class_id), fields: exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect(), - params: HashMap::default(), + params: VarMap::default(), }); let signature = unifier.add_ty(TypeEnum::TFunc(FunSignature { args: exn_cons_args, ret: exn_type, - vars: HashMap::default(), + vars: VarMap::default(), })); let fun_def = TopLevelDef::Function { name: format!("{name}.__init__"), @@ -100,7 +101,7 @@ pub fn get_exn_constructor( /// * `codegen_callback`: A lambda generating LLVM IR for the implementation of this function. fn create_fn_by_codegen( primitives: &mut (PrimitiveStore, Unifier), - var_map: &HashMap, + var_map: &VarMap, name: &'static str, ret_ty: Type, param_ty: &[(Type, &'static str)], @@ -136,7 +137,7 @@ fn create_fn_by_codegen( /// * `intrinsic_fn`: The fully-qualified name of the LLVM intrinsic function. fn create_fn_by_intrinsic( primitives: &mut (PrimitiveStore, Unifier), - var_map: &HashMap, + var_map: &VarMap, name: &'static str, ret_ty: Type, params: &[(Type, &'static str)], @@ -200,7 +201,7 @@ fn create_fn_by_intrinsic( /// already implied by the C ABI. fn create_fn_by_extern( primitives: &mut (PrimitiveStore, Unifier), - var_map: &HashMap, + var_map: &VarMap, name: &'static str, ret_ty: Type, params: &[(Type, &'static str)], @@ -295,7 +296,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { Some("N".into()), None, ); - let var_map: HashMap<_, _> = vec![(num_ty.1, num_ty.0)].into_iter().collect(); + let var_map: VarMap = vec![(num_ty.1, num_ty.0)].into_iter().collect(); let exception_fields = vec![ ("__name__".into(), int32, true), ("__file__".into(), string, true), @@ -1057,7 +1058,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { }, ], ret: range, - vars: HashMap::default(), + vars: VarMap::default(), })), var_id: Vec::default(), instance_to_symbol: HashMap::default(), @@ -1149,7 +1150,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature { args: vec![FuncArg { name: "s".into(), ty: string, default_value: None }], ret: string, - vars: HashMap::default(), + vars: VarMap::default(), })), var_id: Vec::default(), instance_to_symbol: HashMap::default(), @@ -1971,7 +1972,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature { args: vec![FuncArg { name: "n".into(), ty: option_ty_var, default_value: None }], ret: primitives.0.option, - vars: HashMap::from([(option_ty_var_id, option_ty_var)]), + vars: VarMap::from([(option_ty_var_id, option_ty_var)]), })), var_id: vec![option_ty_var_id], instance_to_symbol: HashMap::default(), diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 448e05a9..c51edaeb 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -4,7 +4,10 @@ use std::rc::Rc; use crate::{ codegen::{expr::get_subst_key, stmt::exn_constructor}, symbol_resolver::SymbolValue, - typecheck::type_inferencer::{FunctionData, Inferencer}, + typecheck::{ + type_inferencer::{FunctionData, Inferencer}, + typedef::VarMap, + }, }; use super::*; @@ -844,7 +847,7 @@ impl TopLevelComposer { let resolver = resolver.unwrap(); let resolver = &**resolver; - let mut function_var_map: HashMap = HashMap::new(); + let mut function_var_map = VarMap::new(); let arg_types = { // make sure no duplicate parameter let mut defined_parameter_name: HashSet<_> = HashSet::new(); @@ -1082,7 +1085,7 @@ impl TopLevelComposer { let (method_dummy_ty, method_id) = Self::get_class_method_def_info(class_methods_def, *name)?; - let mut method_var_map: HashMap = HashMap::new(); + let mut method_var_map = VarMap::new(); let arg_types: Vec = { // check method parameters cannot have same name @@ -1574,7 +1577,7 @@ impl TopLevelComposer { }, ], ret: self_type, - vars: HashMap::default(), + vars: VarMap::default(), })); let cons_fun = TopLevelDef::Function { name: format!("{}.{}", class_name, "__init__"), @@ -1598,7 +1601,7 @@ impl TopLevelComposer { // get the class constructor type correct let (contor_args, contor_type_vars) = { let mut constructor_args: Vec = Vec::new(); - let mut type_vars: HashMap = HashMap::new(); + let mut type_vars = VarMap::new(); for (name, func_sig, id) in methods { if *name == init_str_id { init_id = Some(*id); @@ -1749,13 +1752,13 @@ impl TopLevelComposer { }) .multi_cartesian_product() .collect_vec(); - let mut result: Vec> = Vec::default(); + let mut result: Vec = Vec::default(); for comb in var_combs { result.push(vars.keys().copied().zip(comb).collect()); } // NOTE: if is empty, means no type var, append a empty subst, ok to do this? if result.is_empty() { - result.push(HashMap::new()); + result.push(VarMap::new()); } (result, no_ranges) }; @@ -1795,7 +1798,7 @@ impl TopLevelComposer { None } }) - .collect::>() + .collect::() }; unifier.subst(self_type, &subst_for_self).unwrap_or(self_type) }) diff --git a/nac3core/src/toplevel/helper.rs b/nac3core/src/toplevel/helper.rs index c07c0ab4..679b6c1c 100644 --- a/nac3core/src/toplevel/helper.rs +++ b/nac3core/src/toplevel/helper.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use crate::symbol_resolver::SymbolValue; -use crate::typecheck::typedef::Mapping; +use crate::typecheck::typedef::{Mapping, VarMap}; use nac3parser::ast::{Constant, Location}; use super::*; @@ -112,37 +112,37 @@ impl TopLevelComposer { let int32 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.int32, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let int64 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.int64, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let float = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.float, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let bool = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.bool, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let none = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.none, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let range = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.range, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let str = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.str, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let exception = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.exception, @@ -159,29 +159,29 @@ impl TopLevelComposer { ] .into_iter() .collect::>(), - params: HashMap::new(), + params: VarMap::new(), }); let uint32 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.uint32, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let uint64 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.uint64, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let option_type_var = unifier.get_fresh_var(Some("option_type_var".into()), None); let is_some_type_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature { args: vec![], ret: bool, - vars: HashMap::from([(option_type_var.1, option_type_var.0)]), + vars: VarMap::from([(option_type_var.1, option_type_var.0)]), })); let unwrap_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature { args: vec![], ret: option_type_var.0, - vars: HashMap::from([(option_type_var.1, option_type_var.0)]), + vars: VarMap::from([(option_type_var.1, option_type_var.0)]), })); let option = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.option, @@ -192,7 +192,7 @@ impl TopLevelComposer { ] .into_iter() .collect::>(), - params: HashMap::from([(option_type_var.1, option_type_var.0)]), + params: VarMap::from([(option_type_var.1, option_type_var.0)]), }); let size_t_ty = match size_t { @@ -206,10 +206,10 @@ impl TopLevelComposer { let ndarray = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.ndarray, fields: Mapping::new(), - params: Mapping::from([ + params: VarMap::from([ (ndarray_dtype_tvar.1, ndarray_dtype_tvar.0), (ndarray_ndims_tvar.1, ndarray_ndims_tvar.0), - ]) + ]), }); let primitives = PrimitiveStore { diff --git a/nac3core/src/toplevel/mod.rs b/nac3core/src/toplevel/mod.rs index c204819a..1209f51f 100644 --- a/nac3core/src/toplevel/mod.rs +++ b/nac3core/src/toplevel/mod.rs @@ -8,7 +8,7 @@ use std::{ use super::codegen::CodeGenContext; use super::typecheck::type_inferencer::PrimitiveStore; -use super::typecheck::typedef::{FunSignature, FuncArg, SharedUnifier, Type, TypeEnum, Unifier}; +use super::typecheck::typedef::{FunSignature, FuncArg, SharedUnifier, Type, TypeEnum, Unifier, VarMap}; use crate::{ codegen::CodeGenerator, symbol_resolver::{SymbolResolver, ValueEnum}, @@ -76,7 +76,7 @@ impl Debug for GenCall { pub struct FunInstance { pub body: Arc>>>, pub calls: Arc>, - pub subst: HashMap, + pub subst: VarMap, pub unifier_id: usize, } diff --git a/nac3core/src/toplevel/numpy.rs b/nac3core/src/toplevel/numpy.rs index 2f60930a..bc992eff 100644 --- a/nac3core/src/toplevel/numpy.rs +++ b/nac3core/src/toplevel/numpy.rs @@ -19,7 +19,7 @@ use crate::{ toplevel::{DefinitionId, helper::PRIMITIVE_DEF_IDS}, typecheck::{ type_inferencer::PrimitiveStore, - typedef::{FunSignature, Mapping, Type, TypeEnum, Unifier}, + typedef::{FunSignature, Type, TypeEnum, Unifier, VarMap}, }, }; @@ -48,7 +48,7 @@ pub fn make_ndarray_ty( .collect_vec(); debug_assert_eq!(tvar_ids.len(), 2); - let mut tvar_subst = Mapping::new(); + let mut tvar_subst = VarMap::new(); if let Some(dtype) = dtype { tvar_subst.insert(tvar_ids[0], dtype); } diff --git a/nac3core/src/toplevel/type_annotation.rs b/nac3core/src/toplevel/type_annotation.rs index 2086cfe5..ddcf9469 100644 --- a/nac3core/src/toplevel/type_annotation.rs +++ b/nac3core/src/toplevel/type_annotation.rs @@ -415,7 +415,7 @@ pub fn get_type_from_type_annotation_kinds( let subst = { // check for compatible range // TODO: if allow type var to be applied(now this disallowed in the parse_to_type_annotation), need more check - let mut result: HashMap = HashMap::new(); + let mut result = VarMap::new(); for (tvar, p) in type_vars.iter().zip(param_ty) { match unifier.get_ty(*tvar).as_ref() { TypeEnum::TVar { id, range, fields: None, name, loc, is_const_generic: false } => { diff --git a/nac3core/src/typecheck/magic_methods.rs b/nac3core/src/typecheck/magic_methods.rs index 9d92c08c..d25fff5a 100644 --- a/nac3core/src/typecheck/magic_methods.rs +++ b/nac3core/src/typecheck/magic_methods.rs @@ -1,6 +1,6 @@ use crate::typecheck::{ type_inferencer::*, - typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier}, + typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap}, }; use nac3parser::ast::StrRef; use nac3parser::ast::{Cmpop, Operator, Unaryop}; @@ -102,9 +102,9 @@ pub fn impl_binop( }; let function_vars = if let Some(var_id) = other_var_id { - vec![(var_id, other_ty)].into_iter().collect::>() + vec![(var_id, other_ty)].into_iter().collect::() } else { - HashMap::new() + VarMap::new() }; for op in ops { @@ -149,7 +149,7 @@ pub fn impl_unaryop(unifier: &mut Unifier, ty: Type, ret_ty: Type, ops: &[Unaryo ( unifier.add_ty(TypeEnum::TFunc(FunSignature { ret: ret_ty, - vars: HashMap::new(), + vars: VarMap::new(), args: vec![], })), false, @@ -173,7 +173,7 @@ pub fn impl_cmpop( ( unifier.add_ty(TypeEnum::TFunc(FunSignature { ret: store.bool, - vars: HashMap::new(), + vars: VarMap::new(), args: vec![FuncArg { ty: other_ty, default_value: None, diff --git a/nac3core/src/typecheck/type_inferencer/mod.rs b/nac3core/src/typecheck/type_inferencer/mod.rs index e3790566..a6b72bba 100644 --- a/nac3core/src/typecheck/type_inferencer/mod.rs +++ b/nac3core/src/typecheck/type_inferencer/mod.rs @@ -3,7 +3,7 @@ use std::convert::{From, TryInto}; use std::iter::once; use std::{cell::RefCell, sync::Arc}; -use super::typedef::{Call, FunSignature, FuncArg, RecordField, Type, TypeEnum, Unifier}; +use super::typedef::{Call, FunSignature, FuncArg, RecordField, Type, TypeEnum, Unifier, VarMap}; use super::{magic_methods::*, typedef::CallId}; use crate::{ symbol_resolver::{SymbolResolver, SymbolValue}, @@ -425,13 +425,13 @@ impl<'a> Fold<()> for Inferencer<'a> { || self.unifier.get_dummy_var().0, |var| var.custom.unwrap(), ), - vars: HashMap::default(), + vars: VarMap::default(), }); let enter = self.unifier.add_ty(enter); let exit = TypeEnum::TFunc(FunSignature { args: vec![], ret: self.unifier.get_dummy_var().0, - vars: HashMap::default(), + vars: VarMap::default(), }); let exit = self.unifier.add_ty(exit); let mut fields = HashMap::new(); @@ -503,7 +503,7 @@ impl<'a> Fold<()> for Inferencer<'a> { assert_eq!(*id, *id_var); (*id, self.unifier.get_fresh_var_with_range(range, *name, *loc).0) }) - .collect::>(); + .collect::(); Some(self.unifier.subst(self.primitives.option, &var_map).unwrap()) } else { unreachable!("must be tobj") @@ -704,7 +704,7 @@ impl<'a> Inferencer<'a> { .map(|(k, ty)| FuncArg { name: *k, ty: *ty, default_value: None }) .collect(), ret, - vars: HashMap::default(), + vars: VarMap::default(), }; let body = new_context.fold_expr(body)?; new_context.unify(fun.ret, body.custom.unwrap(), &location)?; @@ -939,7 +939,7 @@ impl<'a> Inferencer<'a> { }, ], ret, - vars: HashMap::new(), + vars: VarMap::new(), })); return Ok(Some(Located { @@ -996,7 +996,7 @@ impl<'a> Inferencer<'a> { }, ], ret, - vars: HashMap::new(), + vars: VarMap::new(), })); return Ok(Some(Located { diff --git a/nac3core/src/typecheck/type_inferencer/test.rs b/nac3core/src/typecheck/type_inferencer/test.rs index 44747b7d..8c72cfb1 100644 --- a/nac3core/src/typecheck/type_inferencer/test.rs +++ b/nac3core/src/typecheck/type_inferencer/test.rs @@ -75,70 +75,70 @@ impl TestEnvironment { let int32 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.int32, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); with_fields(&mut unifier, int32, |unifier, fields| { let add_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature { args: vec![FuncArg { name: "other".into(), ty: int32, default_value: None }], ret: int32, - vars: HashMap::new(), + vars: VarMap::new(), })); fields.insert("__add__".into(), (add_ty, false)); }); let int64 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.int64, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let float = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.float, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let bool = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.bool, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let none = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.none, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let range = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.range, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let str = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.str, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let exception = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.exception, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let uint32 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.uint32, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let uint64 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.uint64, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let option = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.option, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let ndarray = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.ndarray, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let primitives = PrimitiveStore { int32, @@ -208,70 +208,70 @@ impl TestEnvironment { let int32 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.int32, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); with_fields(&mut unifier, int32, |unifier, fields| { let add_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature { args: vec![FuncArg { name: "other".into(), ty: int32, default_value: None }], ret: int32, - vars: HashMap::new(), + vars: VarMap::new(), })); fields.insert("__add__".into(), (add_ty, false)); }); let int64 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.int64, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let float = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.float, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let bool = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.bool, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let none = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.none, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let range = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.range, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let str = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.str, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let exception = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.exception, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let uint32 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.uint32, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let uint64 = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.uint64, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let option = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.option, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); let ndarray = unifier.add_ty(TypeEnum::TObj { obj_id: PRIMITIVE_DEF_IDS.ndarray, fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }); identifier_mapping.insert("None".into(), none); for (i, name) in ["int32", "int64", "float", "bool", "none", "range", "str", "Exception"] @@ -318,7 +318,7 @@ impl TestEnvironment { let foo_ty = unifier.add_ty(TypeEnum::TObj { obj_id: DefinitionId(defs + 1), fields: [("a".into(), (v0, true))].iter().cloned().collect::>(), - params: [(id, v0)].iter().cloned().collect::>(), + params: [(id, v0)].iter().cloned().collect::(), }); top_level_defs.push( RwLock::new(TopLevelDef::Class { diff --git a/nac3core/src/typecheck/typedef/mod.rs b/nac3core/src/typecheck/typedef/mod.rs index 5cb99b90..c3c1c756 100644 --- a/nac3core/src/typecheck/typedef/mod.rs +++ b/nac3core/src/typecheck/typedef/mod.rs @@ -1,5 +1,5 @@ use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::fmt::Display; use std::rc::Rc; use std::sync::{Arc, Mutex}; @@ -25,7 +25,14 @@ pub type Type = UnificationKey; pub struct CallId(pub(super) usize); pub type Mapping = HashMap; -type VarMap = Mapping; + +/// A [`Mapping`] sorted by its key. +/// +/// This type is recommended for mappings that should be stored and/or iterated by its sorted key. +pub type SortedMapping = BTreeMap; + +/// A [`BTreeMap`] storing the mapping between type variable ID and [unifier type][`Type`]. +pub type VarMap = SortedMapping; #[derive(Clone)] pub struct Call { @@ -1276,12 +1283,12 @@ impl Unifier { fn subst_map( &mut self, - map: &Mapping, + map: &SortedMapping, mapping: &VarMap, cache: &mut HashMap>, - ) -> Option> + ) -> Option> where - K: std::hash::Hash + Eq + Clone, + K: Ord + Eq + Clone, { let mut map2 = None; for (k, v) in map { diff --git a/nac3core/src/typecheck/typedef/test.rs b/nac3core/src/typecheck/typedef/test.rs index 4ebf2e1a..24eece9f 100644 --- a/nac3core/src/typecheck/typedef/test.rs +++ b/nac3core/src/typecheck/typedef/test.rs @@ -45,9 +45,9 @@ impl Unifier { } } - fn map_eq(&mut self, map1: &Mapping, map2: &Mapping) -> bool + fn map_eq(&mut self, map1: &SortedMapping, map2: &SortedMapping) -> bool where - K: std::hash::Hash + Eq + Clone, + K: Ord + Eq + Clone, { if map1.len() != map2.len() { return false; @@ -91,7 +91,7 @@ impl TestEnvironment { unifier.add_ty(TypeEnum::TObj { obj_id: DefinitionId(0), fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }), ); type_mapping.insert( @@ -99,7 +99,7 @@ impl TestEnvironment { unifier.add_ty(TypeEnum::TObj { obj_id: DefinitionId(1), fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }), ); type_mapping.insert( @@ -107,7 +107,7 @@ impl TestEnvironment { unifier.add_ty(TypeEnum::TObj { obj_id: DefinitionId(2), fields: HashMap::new(), - params: HashMap::new(), + params: VarMap::new(), }), ); let (v0, id) = unifier.get_dummy_var(); @@ -116,7 +116,7 @@ impl TestEnvironment { unifier.add_ty(TypeEnum::TObj { obj_id: DefinitionId(3), fields: [("a".into(), (v0, true))].iter().cloned().collect::>(), - params: [(id, v0)].iter().cloned().collect::>(), + params: [(id, v0)].iter().cloned().collect::(), }), ); @@ -363,7 +363,7 @@ fn test_virtual() { let fun = env.unifier.add_ty(TypeEnum::TFunc(FunSignature { args: vec![], ret: int, - vars: HashMap::new(), + vars: VarMap::new(), })); let bar = env.unifier.add_ty(TypeEnum::TObj { obj_id: DefinitionId(5), @@ -371,7 +371,7 @@ fn test_virtual() { .iter() .cloned() .collect::>(), - params: HashMap::new(), + params: VarMap::new(), }); let v0 = env.unifier.get_dummy_var().0; let v1 = env.unifier.get_dummy_var().0; diff --git a/nac3standalone/src/main.rs b/nac3standalone/src/main.rs index 8973cf31..f11acd19 100644 --- a/nac3standalone/src/main.rs +++ b/nac3standalone/src/main.rs @@ -17,12 +17,14 @@ use nac3core::{ }, symbol_resolver::SymbolResolver, toplevel::{ - composer::TopLevelComposer, helper::parse_parameter_default_value, type_annotation::*, + composer::{ComposerConfig, TopLevelComposer}, + helper::parse_parameter_default_value, + type_annotation::*, TopLevelDef, }, typecheck::{ type_inferencer::PrimitiveStore, - typedef::{FunSignature, Type, Unifier}, + typedef::{FunSignature, Type, Unifier, VarMap}, }, }; use nac3parser::{ @@ -32,7 +34,6 @@ use nac3parser::{ mod basic_symbol_resolver; use basic_symbol_resolver::*; -use nac3core::toplevel::composer::ComposerConfig; /// Command-line argument parser definition. #[derive(Parser)] @@ -345,7 +346,7 @@ fn main() { } } - let signature = FunSignature { args: vec![], ret: primitive.int32, vars: HashMap::new() }; + let signature = FunSignature { args: vec![], ret: primitive.int32, vars: VarMap::new() }; let mut store = ConcreteTypeStore::new(); let mut cache = HashMap::new(); let signature = store.from_signature(&mut composer.unifier, &primitive, &signature, &mut cache);