From 56bcfd8cb05e12d2faace1efdcae701493958323 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 7 Dec 2022 23:09:42 +0100 Subject: [PATCH] Allow using a generic function which cross the ingot bound --- crates/analyzer/src/namespace/items.rs | 4 ++ crates/codegen/src/db/queries/cgu.rs | 72 ++++++++++++++++------- crates/codegen/src/db/queries/function.rs | 4 +- crates/codegen/src/yul/isel/function.rs | 1 + crates/mir/src/db/queries/function.rs | 11 ++-- crates/mir/src/lower/function.rs | 2 +- crates/mir/src/lower/types.rs | 6 +- 7 files changed, 69 insertions(+), 31 deletions(-) diff --git a/crates/analyzer/src/namespace/items.rs b/crates/analyzer/src/namespace/items.rs index c654a1d3a0..8a0c25339f 100644 --- a/crates/analyzer/src/namespace/items.rs +++ b/crates/analyzer/src/namespace/items.rs @@ -1211,6 +1211,10 @@ impl FunctionSigId { self.data(db).module } + pub fn ingot(&self, db: &dyn AnalyzerDb) -> IngotId { + self.module(db).ingot(db) + } + pub fn is_contract_func(self, db: &dyn AnalyzerDb) -> bool { matches! {self.parent(db), Item::Type(TypeDef::Contract(_))} } diff --git a/crates/codegen/src/db/queries/cgu.rs b/crates/codegen/src/db/queries/cgu.rs index 5a290ed9bb..8402b96c1e 100644 --- a/crates/codegen/src/db/queries/cgu.rs +++ b/crates/codegen/src/db/queries/cgu.rs @@ -14,7 +14,7 @@ //! function call. If generic function calls are found, monomorphizes them and //! add them to the worklist. //! -//! 4. Add the current function to the CGU of the module. +//! 4. Add the current function to the CGU of the module. //! //! 5. Repeat steps 2-4 until the worklist is empty. @@ -28,6 +28,7 @@ use fe_mir::ir::{ body_cursor::{BodyCursor, CursorLocation}, function::BodyDataStore, inst::InstKind, + types::TypeParamDef, FunctionBody, FunctionParam, FunctionSigId, FunctionSignature, InstId, TypeId, TypeKind, ValueId, }; @@ -114,7 +115,7 @@ impl<'db> CguFunctionBuilder<'db> { { if callee.is_generic(self.db.upcast()) { let (mono_sig, mono_body) = - self.monomorphize_func(&cursor.body().store, inst); + self.monomorphize_func(sig, &cursor.body().store, inst); // Rewrite the callee to the mono function. cursor.body_mut().store.rewrite_callee(inst, mono_sig); @@ -136,10 +137,11 @@ impl<'db> CguFunctionBuilder<'db> { fn monomorphize_func( &self, + caller: FunctionSigId, store: &BodyDataStore, inst: InstId, ) -> (FunctionSigId, FunctionBody) { - let InstKind::Call {func: callee, args, ..} = &store.inst_data(inst).kind else { + let InstKind::Call {func: callee, args, generic_type, ..} = &store.inst_data(inst).kind else { panic!("expected a call instruction"); }; debug_assert!(callee.is_generic(self.db.upcast())); @@ -147,7 +149,7 @@ impl<'db> CguFunctionBuilder<'db> { let callee = *callee; let subst = self.get_subst(store, callee, args); - let mono_sig = self.monomorphize_sig(callee, &subst); + let mono_sig = self.monomorphize_sig(caller, callee, &subst, generic_type); let mono_body = self.monomorphize_body(callee, &subst); (self.db.mir_intern_function(mono_sig.into()), mono_body) @@ -155,10 +157,12 @@ impl<'db> CguFunctionBuilder<'db> { fn monomorphize_sig( &self, - sig: FunctionSigId, + caller: FunctionSigId, + callee: FunctionSigId, subst: &FxHashMap, + generic_type: &Option, ) -> FunctionSignature { - let params = sig + let params = callee .data(self.db.upcast()) .params .iter() @@ -176,19 +180,25 @@ impl<'db> CguFunctionBuilder<'db> { }) .collect(); - let return_type = sig.return_type(self.db.upcast()).clone(); - let module_id = sig.module(self.db.upcast()); - let linkage = sig.linkage(self.db.upcast()); - let analyzer_id = self.resolve_function(sig.analyzer_sig(self.db.upcast()), subst); + let return_type = callee.return_type(self.db.upcast()); + // If the callee and caller are in different ingots, we need to generate a + // function in the caller's module. + let module_id = if callee.ingot(self.db.upcast()) != caller.ingot(self.db.upcast()) { + caller.module(self.db.upcast()) + } else { + callee.module(self.db.upcast()) + }; + let linkage = callee.linkage(self.db.upcast()); + let analyzer_id = + self.resolve_function(callee.analyzer_sig(self.db.upcast()), subst, generic_type); FunctionSignature { - name: self.mono_func_name(analyzer_id, subst), + name: self.mono_func_name(caller, analyzer_id, subst), params, return_type, module_id, analyzer_id, linkage, - has_self: sig.has_self(self.db.upcast()), } } @@ -204,12 +214,9 @@ impl<'db> CguFunctionBuilder<'db> { let mut body = (*self.db.mir_lowered_func_body(func_id)).clone(); for value in body.store.values_mut() { - match &value.ty().data(self.db.upcast()).kind { - TypeKind::TypeParam(def) => { - let subst_ty = subst[&def.name]; - *value.ty_mut() = subst_ty; - } - _ => {} + if let TypeKind::TypeParam(def) = &value.ty().data(self.db.upcast()).kind { + let subst_ty = subst[&def.name]; + *value.ty_mut() = subst_ty; } } @@ -222,21 +229,46 @@ impl<'db> CguFunctionBuilder<'db> { &self, callee: AnalyzerFuncSigId, subst: &FxHashMap, + generic_type: &Option, ) -> AnalyzerFuncSigId { let trait_id = match callee.parent(self.db.upcast()) { Item::Trait(id) => id, _ => return callee, }; - todo!() + let concrete_type = subst[&generic_type.as_ref().unwrap().name]; + let impl_ = concrete_type + .analyzer_ty(self.db.upcast()) + .unwrap() + .get_impl_for(self.db.upcast(), trait_id) + .unwrap(); + + let resolved = impl_ + .function(self.db.upcast(), &callee.name(self.db.upcast())) + .expect("missing function"); + resolved.sig(self.db.upcast()) } fn mono_func_name( &self, + caller: FunctionSigId, callee: AnalyzerFuncSigId, subst: &FxHashMap, ) -> SmolStr { - todo!() + let callee_ingot = callee.ingot(self.db.upcast()); + let mut func_name = if caller.ingot(self.db.upcast()) != callee_ingot { + format!( + "{}${}", + callee_ingot.name(self.db.upcast()), + callee.name(self.db.upcast()) + ) + } else { + callee.name(self.db.upcast()).to_string() + }; + for ty in subst.values() { + func_name.push_str(&format!("${}", ty.as_string(self.db.upcast()))) + } + func_name.into() } fn get_subst( diff --git a/crates/codegen/src/db/queries/function.rs b/crates/codegen/src/db/queries/function.rs index 532e1e8ff4..7a74b679a6 100644 --- a/crates/codegen/src/db/queries/function.rs +++ b/crates/codegen/src/db/queries/function.rs @@ -28,6 +28,8 @@ pub fn legalized_body(db: &dyn CodegenDb, func: FunctionId) -> Rc pub fn symbol_name(db: &dyn CodegenDb, function: FunctionSigId) -> Rc { let module = function.data(db.upcast()).module_id; let module_name = module.name(db.upcast()); + let ingot = module.ingot(db.upcast()); + let ingot_name = ingot.name(db.upcast()); let analyzer_func = function.analyzer_sig(db.upcast()); let func_name = format!("{}", analyzer_func.name(db.upcast()),); @@ -48,7 +50,7 @@ pub fn symbol_name(db: &dyn CodegenDb, function: FunctionSigId) -> Rc { _ => func_name, }; - format!("{}${}", module_name, func_name).into() + format!("{}${}${}", ingot_name, module_name, func_name).into() } fn safe_name(db: &dyn CodegenDb, ty: TypeId) -> SmolStr { diff --git a/crates/codegen/src/yul/isel/function.rs b/crates/codegen/src/yul/isel/function.rs index dbbd427cb4..560a013538 100644 --- a/crates/codegen/src/yul/isel/function.rs +++ b/crates/codegen/src/yul/isel/function.rs @@ -316,6 +316,7 @@ impl<'db, 'a> FuncLowerHelper<'db, 'a> { func, args, call_type, + .. } => { let args: Vec<_> = args.iter().map(|arg| self.value_expr(*arg)).collect(); let result = match call_type { diff --git a/crates/mir/src/db/queries/function.rs b/crates/mir/src/db/queries/function.rs index 2927cf2a04..18fdc89bf5 100644 --- a/crates/mir/src/db/queries/function.rs +++ b/crates/mir/src/db/queries/function.rs @@ -62,8 +62,11 @@ impl ir::FunctionSigId { } pub fn module(self, db: &dyn MirDb) -> analyzer_items::ModuleId { - let analyzer_func = self.analyzer_sig(db); - analyzer_func.module(db.upcast()) + self.analyzer_sig(db).module(db.upcast()) + } + + pub fn ingot(self, db: &dyn MirDb) -> analyzer_items::IngotId { + self.analyzer_sig(db).ingot(db.upcast()) } pub fn is_contract_init(self, db: &dyn MirDb) -> bool { @@ -74,10 +77,6 @@ impl ir::FunctionSigId { self.data(db).name.clone() } - pub fn has_self(self, db: &dyn MirDb) -> bool { - self.data(db).has_self - } - /// Returns `class_name::fn_name` if a function is a method else `fn_name`. pub fn debug_name(self, db: &dyn MirDb) -> SmolStr { let analyzer_func = self.analyzer_sig(db); diff --git a/crates/mir/src/lower/function.rs b/crates/mir/src/lower/function.rs index fad32b5463..7316879242 100644 --- a/crates/mir/src/lower/function.rs +++ b/crates/mir/src/lower/function.rs @@ -66,7 +66,7 @@ pub fn lower_func_signature(db: &dyn MirDb, func: analyzer_items::FunctionSigId) }; let sig = FunctionSignature { - name: func.name(db.upcast()).clone(), + name: func.name(db.upcast()), params, return_type: Some(return_type), module_id: func.module(db.upcast()), diff --git a/crates/mir/src/lower/types.rs b/crates/mir/src/lower/types.rs index 3be9e32731..76762a8442 100644 --- a/crates/mir/src/lower/types.rs +++ b/crates/mir/src/lower/types.rs @@ -31,9 +31,9 @@ pub fn lower_type(db: &dyn MirDb, analyzer_ty: analyzer_types::TypeId) -> TypeId analyzer_types::Type::SelfContract(contract) => lower_contract(db, contract), analyzer_types::Type::Struct(struct_) => lower_struct(db, struct_), analyzer_types::Type::Enum(enum_) => lower_enum(db, enum_), - analyzer_types::Type::Generic(generic) => TypeKind::TypeParam(TypeParamDef { - name: generic.name.clone(), - }), + analyzer_types::Type::Generic(generic) => { + TypeKind::TypeParam(TypeParamDef { name: generic.name }) + } }; intern_type(db, ty_kind, Some(analyzer_ty.deref(db.upcast())))