diff options
-rw-r--r-- | src/hir/deserialise.cpp | 16 | ||||
-rw-r--r-- | src/hir/hir.hpp | 2 | ||||
-rw-r--r-- | src/hir/serialise.cpp | 16 | ||||
-rw-r--r-- | src/hir_expand/vtable.cpp | 178 | ||||
-rw-r--r-- | src/mir/cleanup.cpp | 122 | ||||
-rw-r--r-- | src/mir/helpers.hpp | 2 |
6 files changed, 213 insertions, 123 deletions
diff --git a/src/hir/deserialise.cpp b/src/hir/deserialise.cpp index 45876d88..348b016c 100644 --- a/src/hir/deserialise.cpp +++ b/src/hir/deserialise.cpp @@ -60,6 +60,20 @@ namespace { } return rv; } + template<typename V> + ::std::unordered_multimap< ::std::string,V> deserialise_strummap() + { + size_t n = m_in.read_count(); + ::std::unordered_multimap< ::std::string, V> rv; + //rv.reserve(n); + for(size_t i = 0; i < n; i ++) + { + auto s = m_in.read_string(); + DEBUG("- " << s); + rv.insert( ::std::make_pair( mv$(s), D<V>::des(*this) ) ); + } + return rv; + } template<typename T> ::std::vector<T> deserialise_vec() @@ -871,7 +885,7 @@ namespace { rv.m_is_marker = m_in.read_bool(); rv.m_types = deserialise_strumap< ::HIR::AssociatedType>(); rv.m_values = deserialise_strumap< ::HIR::TraitValueItem>(); - rv.m_value_indexes = deserialise_strumap< unsigned int>(); + rv.m_value_indexes = deserialise_strummap< ::std::pair<unsigned int, ::HIR::GenericPath> >(); rv.m_type_indexes = deserialise_strumap< unsigned int>(); return rv; } diff --git a/src/hir/hir.hpp b/src/hir/hir.hpp index 20cac608..ac1748c0 100644 --- a/src/hir/hir.hpp +++ b/src/hir/hir.hpp @@ -248,7 +248,7 @@ struct Trait ::std::unordered_map< ::std::string, TraitValueItem > m_values; // Indexes into the vtable for each present method and value - ::std::unordered_map< ::std::string, unsigned int > m_value_indexes; + ::std::unordered_multimap< ::std::string, ::std::pair<unsigned int,::HIR::GenericPath> > m_value_indexes; // Indexes in the vtable parameter list for each associated type ::std::unordered_map< ::std::string, unsigned int > m_type_indexes; diff --git a/src/hir/serialise.cpp b/src/hir/serialise.cpp index 33f8123f..35acaa93 100644 --- a/src/hir/serialise.cpp +++ b/src/hir/serialise.cpp @@ -40,6 +40,16 @@ namespace { serialise(v.second); } } + template<typename V> + void serialise_strmap(const ::std::unordered_multimap< ::std::string,V>& map) + { + m_out.write_count(map.size()); + for(const auto& v : map) { + DEBUG("- " << v.first); + m_out.write_string(v.first); + serialise(v.second); + } + } template<typename T> void serialise_vec(const ::std::vector<T>& vec) { @@ -62,6 +72,11 @@ namespace { m_out.write_string(e.first); serialise(e.second); } + template<typename T> + void serialise(const ::std::pair<unsigned int, T>& e) { + m_out.write_count(e.first); + serialise(e.second); + } void serialise_type(const ::HIR::TypeRef& ty) { @@ -150,6 +165,7 @@ namespace { serialise_simplepath(path.m_path); serialise_pathparams(path.m_params); } + void serialise(const ::HIR::GenericPath& path) { serialise_genericpath(path); } void serialise_traitpath(const ::HIR::TraitPath& path) { serialise_genericpath(path.m_path); diff --git a/src/hir_expand/vtable.cpp b/src/hir_expand/vtable.cpp index ea578237..baf5a016 100644 --- a/src/hir_expand/vtable.cpp +++ b/src/hir_expand/vtable.cpp @@ -88,84 +88,113 @@ namespace { visitor.add_types_from_trait(tr); auto args = mv$(visitor.params); - auto clone_cb = [&](const auto& t, auto& o) { - if(t.m_data.is_Path() && t.m_data.as_Path().path.m_data.is_UfcsKnown()) { - const auto& pe = t.m_data.as_Path().path.m_data.as_UfcsKnown(); - DEBUG("t=" << t); - if( *pe.type == ::HIR::TypeRef("Self", 0xFFFF) /*&& pe.trait == trait_path*/ && tr.m_type_indexes.count(pe.item) ) { - // Replace with a new type param, need to know the index of it - o = ::HIR::TypeRef("a#"+pe.item, tr.m_type_indexes.at(pe.item)); - return true; + struct VtableConstruct { + const OuterVisitor* m_outer; + ::HIR::Trait* trait_ptr; + ::HIR::t_struct_fields fields; + + bool add_ents_from_trait(const ::HIR::Trait& tr, const ::HIR::GenericPath& trait_path) + { + TRACE_FUNCTION_F(trait_path); + auto clone_cb = [&](const auto& t, auto& o) { + if(t.m_data.is_Path() && t.m_data.as_Path().path.m_data.is_UfcsKnown()) { + const auto& pe = t.m_data.as_Path().path.m_data.as_UfcsKnown(); + DEBUG("t=" << t); + if( *pe.type == ::HIR::TypeRef("Self", 0xFFFF) /*&& pe.trait == trait_path*/ && tr.m_type_indexes.count(pe.item) ) { + // Replace with a new type param, need to know the index of it + o = ::HIR::TypeRef("a#"+pe.item, tr.m_type_indexes.at(pe.item)); + return true; + } + } + return false; + }; + auto clone_self_cb = [](const auto& t, auto&o) { + if( t == ::HIR::TypeRef("Self", 0xFFFF) ) { + o = ::HIR::TypeRef::new_unit(); + return true; + } + return false; + }; + for(auto& vi : tr.m_values) + { + TU_MATCHA( (vi.second), (ve), + (Function, + if( ve.m_receiver == ::HIR::Function::Receiver::Free ) { + DEBUG("- '" << vi.first << "' Skip free function"); // ? + continue ; + } + if( ::std::any_of(ve.m_params.m_bounds.begin(), ve.m_params.m_bounds.end(), [&](const auto& b){ + return b.is_TraitBound() + && b.as_TraitBound().type == ::HIR::TypeRef("Self", 0xFFFF) + && b.as_TraitBound().trait.m_path.m_path == m_outer->m_lang_Sized; + }) ) + { + DEBUG("- '" << vi.first << "' Skip where `Self: Sized`"); + continue ; + } + if( ve.m_params.m_types.size() > 0 ) { + DEBUG("- '" << vi.first << "' NOT object safe (generic), not creating vtable"); + return false; + } + + ::HIR::FunctionType ft; + ft.is_unsafe = ve.m_unsafe; + ft.m_abi = ve.m_abi; + ft.m_rettype = box$( clone_ty_with(sp, ve.m_return, clone_cb) ); + ft.m_arg_types.reserve( ve.m_args.size() ); + ft.m_arg_types.push_back( clone_ty_with(sp, ve.m_args[0].second, clone_self_cb) ); + for(unsigned int i = 1; i < ve.m_args.size(); i ++) + ft.m_arg_types.push_back( clone_ty_with(sp, ve.m_args[i].second, clone_cb) ); + // Clear the first argument (the receiver) + ::HIR::TypeRef fcn_type( mv$(ft) ); + + // Detect use of `Self` and don't create the vtable if there is. + if( visit_ty_with(fcn_type, [&](const auto& t){ return (t == ::HIR::TypeRef("Self", 0xFFFF)); }) ) + { + DEBUG("- '" << vi.first << "' NOT object safe (Self), not creating vtable - " << fcn_type); + return false; + } + + trait_ptr->m_value_indexes.insert( ::std::make_pair( + vi.first, + ::std::make_pair(fields.size(), trait_path.clone()) + ) ); + DEBUG("- '" << vi.first << "' is @" << fields.size()); + fields.push_back( ::std::make_pair( + vi.first, + ::HIR::VisEnt< ::HIR::TypeRef> { true, mv$(fcn_type) } + ) ); + ), + (Static, + if( vi.first != "#vtable" ) + { + TODO(Span(), "Associated static in vtable"); + } + ), + (Constant, + //TODO(Span(), "Associated const in vtable"); + ) + ) } - } - return false; - }; - auto clone_self_cb = [](const auto& t, auto&o) { - if( t == ::HIR::TypeRef("Self", 0xFFFF) ) { - o = ::HIR::TypeRef::new_unit(); + for(const auto& st : tr.m_parent_traits) { + ::HIR::TypeRef self("Self", 0xFFFF); + auto st_gp = monomorphise_genericpath_with(sp, st.m_path, monomorphise_type_get_cb(sp, &self, &trait_path.m_params, nullptr), false); + // NOTE: Doesn't trigger non-object-safe + add_ents_from_trait(*st.m_trait_ptr, st_gp); + } + // TODO: Iterate supertraits from bounds too return true; } - return false; - }; + }; - ::HIR::t_struct_fields fields; - for(auto& vi : tr.m_values) + VtableConstruct vtc { this, &tr, {} }; + if( ! vtc.add_ents_from_trait(tr, trait_path) ) { - TU_MATCHA( (vi.second), (ve), - (Function, - if( ve.m_receiver == ::HIR::Function::Receiver::Free ) { - DEBUG("- '" << vi.first << "' Skip free function"); // ? - continue ; - } - if( ::std::any_of(ve.m_params.m_bounds.begin(), ve.m_params.m_bounds.end(), [&](const auto& b){ - return b.is_TraitBound() && b.as_TraitBound().type == ::HIR::TypeRef("Self", 0xFFFF) && b.as_TraitBound().trait.m_path.m_path == m_lang_Sized; - }) ) - { - DEBUG("- '" << vi.first << "' Skip where `Self: Sized`"); - continue ; - } - if( ve.m_params.m_types.size() > 0 ) { - DEBUG("- '" << vi.first << "' NOT object safe (generic), not creating vtable"); - tr.m_value_indexes.clear(); - tr.m_type_indexes.clear(); - return ; - } - - ::HIR::FunctionType ft; - ft.is_unsafe = ve.m_unsafe; - ft.m_abi = ve.m_abi; - ft.m_rettype = box$( clone_ty_with(sp, ve.m_return, clone_cb) ); - ft.m_arg_types.reserve( ve.m_args.size() ); - ft.m_arg_types.push_back( clone_ty_with(sp, ve.m_args[0].second, clone_self_cb) ); - for(unsigned int i = 1; i < ve.m_args.size(); i ++) - ft.m_arg_types.push_back( clone_ty_with(sp, ve.m_args[i].second, clone_cb) ); - // Clear the first argument (the receiver) - ::HIR::TypeRef fcn_type( mv$(ft) ); - - // Detect use of `Self` and don't create the vtable if there is. - if( visit_ty_with(fcn_type, [&](const auto& t){ return (t == ::HIR::TypeRef("Self", 0xFFFF)); }) ) - { - DEBUG("- '" << vi.first << "' NOT object safe (Self), not creating vtable - " << fcn_type); - tr.m_value_indexes.clear(); - tr.m_type_indexes.clear(); - return ; - } - - tr.m_value_indexes[vi.first] = fields.size(); - DEBUG("- '" << vi.first << "' is @" << fields.size()); - fields.push_back( ::std::make_pair( - vi.first, - ::HIR::VisEnt< ::HIR::TypeRef> { true, mv$(fcn_type) } - ) ); - ), - (Static, - TODO(Span(), "Associated static in vtable"); - ), - (Constant, - //TODO(Span(), "Associated const in vtable"); - ) - ) + tr.m_value_indexes.clear(); + tr.m_type_indexes.clear(); + return ; } + auto fields = mv$(vtc.fields); ::HIR::PathParams params; { @@ -207,6 +236,7 @@ namespace { const auto& tr = m_crate.get_trait_by_path(sp, trait_path); if(tr.m_value_indexes.size() > 0) { + auto monomorph_cb_trait = monomorphise_type_get_cb(sp, &impl.m_type, &impl.m_trait_args, nullptr); auto trait_gpath = ::HIR::GenericPath(trait_path, impl.m_trait_args.clone()); ::std::vector< ::HIR::Literal> vals; @@ -214,7 +244,9 @@ namespace { for(const auto& m : tr.m_value_indexes) { //ASSERT_BUG(sp, tr.m_values.at(m.first).is_Function(), "TODO: Handle generating vtables with non-function items"); - vals.at(m.second) = ::HIR::Literal::make_BorrowOf( ::HIR::Path(impl.m_type.clone(), trait_gpath.clone(), m.first) ); + DEBUG("- " << m.second.first << " = " << m.second.second << " :: " << m.first); + auto gpath = monomorphise_genericpath_with(sp, m.second.second, monomorph_cb_trait, false); + vals.at(m.second.first) = ::HIR::Literal::make_BorrowOf( ::HIR::Path(impl.m_type.clone(), mv$(gpath), m.first) ); } auto vtable_sp = trait_path; diff --git a/src/mir/cleanup.cpp b/src/mir/cleanup.cpp index bcbeb167..a0d9e0fb 100644 --- a/src/mir/cleanup.cpp +++ b/src/mir/cleanup.cpp @@ -35,6 +35,71 @@ const ::HIR::Literal* MIR_Cleanup_GetConstant(const Span& sp, const StaticTraitR return nullptr; } +::MIR::Terminator MIR_Cleanup_Virtualize( + const Span& sp, const ::MIR::TypeResolve& state, ::MIR::Function& fcn, + ::MIR::BasicBlock& block, ::MIR::Terminator::Data_CallPath& e, + const ::HIR::TypeRef::Data::Data_TraitObject& te, const ::HIR::Path::Data::Data_UfcsKnown& pe + ) +{ + assert( te.m_trait.m_trait_ptr ); + const auto& trait = *te.m_trait.m_trait_ptr; + + // 1. Get the vtable index for this function + auto it = trait.m_value_indexes.find( pe.item ); + while( it != trait.m_value_indexes.end() ) + { + DEBUG("- " << it->second.second); + if( it->second.second.m_path == pe.trait.m_path ) + { + // TODO: Match generics using match_test_generics comparing to the trait args + break ; + } + ++ it; + } + if( it == trait.m_value_indexes.end() || it->first != pe.item ) + BUG(sp, "Calling method '" << pe.item << "' from " << pe.trait << " through " << te.m_trait.m_path << " which isn't in the vtable"); + unsigned int vtable_idx = it->second.first; + + // 2. Load from the vtable + auto vtable_ty_spath = pe.trait.m_path; + vtable_ty_spath.m_components.back() += "#vtable"; + const auto& vtable_ref = state.m_resolve.m_crate.get_struct_by_path(sp, vtable_ty_spath); + // Copy the param set from the trait in the trait object + ::HIR::PathParams vtable_params = te.m_trait.m_path.m_params.clone(); + // - Include associated types on bound + for(const auto& ty_b : te.m_trait.m_type_bounds) { + auto idx = trait.m_type_indexes.at(ty_b.first); + if(vtable_params.m_types.size() <= idx) + vtable_params.m_types.resize(idx+1); + vtable_params.m_types[idx] = ty_b.second.clone(); + } + auto vtable_ty = ::HIR::TypeRef::new_pointer( + ::HIR::BorrowType::Shared, + ::HIR::TypeRef( ::HIR::GenericPath(vtable_ty_spath, mv$(vtable_params)), &vtable_ref ) + ); + + // Allocate a temporary for the vtable pointer itself + auto vtable_lv = ::MIR::LValue::make_Temporary({ static_cast<unsigned int>(fcn.temporaries.size()) }); + fcn.temporaries.push_back( mv$(vtable_ty) ); + // - Load the vtable and store it + auto vtable_rval = ::MIR::RValue::make_DstMeta({ ::MIR::LValue::make_Deref({ box$(e.args.front().clone()) }) }); + block.statements.push_back( ::MIR::Statement::make_Assign({ vtable_lv.clone(), mv$(vtable_rval) }) ); + + auto ptr_rval = ::MIR::RValue::make_DstPtr({ ::MIR::LValue::make_Deref({ box$(e.args.front().clone()) }) }); + auto ptr_lv = ::MIR::LValue::make_Temporary({ static_cast<unsigned int>(fcn.temporaries.size()) }); + fcn.temporaries.push_back( ::HIR::TypeRef::new_pointer(::HIR::BorrowType::Shared, ::HIR::TypeRef::new_unit()) ); + block.statements.push_back( ::MIR::Statement::make_Assign({ ptr_lv.clone(), mv$(ptr_rval) }) ); + e.args.front() = mv$(ptr_lv); + + // Update the terminator with the new information. + auto vtable_fcn = ::MIR::LValue::make_Field({ box$(::MIR::LValue::make_Deref({ box$(vtable_lv) })), vtable_idx }); + return ::MIR::Terminator::make_CallValue({ + e.ret_block, e.panic_block, + mv$(e.ret_val), mv$(vtable_fcn), + mv$(e.args) + }); +} + void MIR_Cleanup(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type) { Span sp; @@ -142,55 +207,16 @@ void MIR_Cleanup(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, const auto& pe = e.fcn_path.m_data.as_UfcsKnown(); const auto& te = pe.type->m_data.as_TraitObject(); // TODO: What if the method is from a supertrait? - if( pe.trait == te.m_trait.m_path ) + + if( te.m_trait.m_path == pe.trait || resolve.find_named_trait_in_trait( + sp, pe.trait.m_path, pe.trait.m_params, + *te.m_trait.m_trait_ptr, te.m_trait.m_path.m_path, te.m_trait.m_path.m_params, + *pe.type, + [](const auto&, auto){} + ) + ) { - assert( te.m_trait.m_trait_ptr ); - const auto& trait = *te.m_trait.m_trait_ptr; - - // 1. Get the vtable index for this function - if( trait.m_value_indexes.count(pe.item) == 0 ) - BUG(sp, "Calling method '" << pe.item << "' of " << pe.trait << " which isn't in the vtable"); - unsigned int vtable_idx = trait.m_value_indexes.at( pe.item ); - - // 2. Load from the vtable - auto vtable_ty_spath = pe.trait.m_path; - vtable_ty_spath.m_components.back() += "#vtable"; - const auto& vtable_ref = resolve.m_crate.get_struct_by_path(sp, vtable_ty_spath); - // Copy the param set from the trait in the trait object - ::HIR::PathParams vtable_params = te.m_trait.m_path.m_params.clone(); - // - Include associated types on bound - for(const auto& ty_b : te.m_trait.m_type_bounds) { - auto idx = trait.m_type_indexes.at(ty_b.first); - if(vtable_params.m_types.size() <= idx) - vtable_params.m_types.resize(idx+1); - vtable_params.m_types[idx] = ty_b.second.clone(); - } - auto vtable_ty = ::HIR::TypeRef::new_pointer( - ::HIR::BorrowType::Shared, - ::HIR::TypeRef( ::HIR::GenericPath(vtable_ty_spath, mv$(vtable_params)), &vtable_ref ) - ); - - // Allocate a temporary for the vtable pointer itself - auto vtable_lv = ::MIR::LValue::make_Temporary({ static_cast<unsigned int>(fcn.temporaries.size()) }); - fcn.temporaries.push_back( mv$(vtable_ty) ); - // - Load the vtable and store it - auto vtable_rval = ::MIR::RValue::make_DstMeta({ ::MIR::LValue::make_Deref({ box$(e.args.front().clone()) }) }); - block.statements.push_back( ::MIR::Statement::make_Assign({ vtable_lv.clone(), mv$(vtable_rval) }) ); - - auto ptr_rval = ::MIR::RValue::make_DstPtr({ ::MIR::LValue::make_Deref({ box$(e.args.front().clone()) }) }); - auto ptr_lv = ::MIR::LValue::make_Temporary({ static_cast<unsigned int>(fcn.temporaries.size()) }); - fcn.temporaries.push_back( ::HIR::TypeRef::new_pointer(::HIR::BorrowType::Shared, ::HIR::TypeRef::new_unit()) ); - block.statements.push_back( ::MIR::Statement::make_Assign({ ptr_lv.clone(), mv$(ptr_rval) }) ); - e.args.front() = mv$(ptr_lv); - - // Update the terminator with the new information. - auto vtable_fcn = ::MIR::LValue::make_Field({ box$(::MIR::LValue::make_Deref({ box$(vtable_lv) })), vtable_idx }); - auto new_term = ::MIR::Terminator::make_CallValue({ - e.ret_block, e.panic_block, - mv$(e.ret_val), mv$(vtable_fcn), - mv$(e.args) - }); - + auto new_term = MIR_Cleanup_Virtualize(sp, state, fcn, block, e, te, pe); block.terminator = mv$(new_term); } } diff --git a/src/mir/helpers.hpp b/src/mir/helpers.hpp index 60deccad..2a4f81d5 100644 --- a/src/mir/helpers.hpp +++ b/src/mir/helpers.hpp @@ -39,8 +39,10 @@ private: const unsigned int STMT_TERM = ~0u; const Span& sp; +public: const ::StaticTraitResolve& m_resolve; const ::HIR::Crate& m_crate; +private: ::FmtLambda m_path; const ::HIR::TypeRef& m_ret_type; const args_t& m_args; |