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 1b338aaf3..1d8f66268 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 a1ae1df93..f8f029ff0 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 6c1130d01..ccba682a9 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 774516006..30de4400c 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 360d8734e..7bc16ee97 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 2f6d22497..159ac2b47 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 74a79d3fa..de41ac1a0 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 635145d9c..3b90918fb 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 448e05a9f..c51edaebf 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 c07c0ab4e..679b6c1c6 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 c204819aa..1209f51ff 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 2f60930a0..bc992eff8 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 2086cfe57..ddcf94697 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 9d92c08c6..d25fff5a8 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 e3790566e..a6b72bba8 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 44747b7d3..8c72cfb13 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 5cb99b90b..c3c1c756a 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 4ebf2e1a8..24eece9fd 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 8973cf31b..f11acd196 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);