diff options
author | John Hodge <tpg@mutabah.net> | 2016-12-10 12:42:06 +0800 |
---|---|---|
committer | John Hodge <tpg@mutabah.net> | 2016-12-10 12:42:06 +0800 |
commit | edd6791bb8e16b2d05c57b732c9185af5409b6c0 (patch) | |
tree | 23d4b0e091110ab2736d27994b6b93fffe3f86c6 /src | |
parent | 8b212379fbb7b26c55555b04394bdf232ab02d66 (diff) | |
download | mrust-edd6791bb8e16b2d05c57b732c9185af5409b6c0.tar.gz |
MIR - More CoerceUnsized/Unsize hackery
Diffstat (limited to 'src')
-rw-r--r-- | src/hir_conv/markings.cpp | 37 | ||||
-rw-r--r-- | src/mir/cleanup.cpp | 178 |
2 files changed, 162 insertions, 53 deletions
diff --git a/src/hir_conv/markings.cpp b/src/hir_conv/markings.cpp index 555b38bb..7f2189dd 100644 --- a/src/hir_conv/markings.cpp +++ b/src/hir_conv/markings.cpp @@ -35,6 +35,37 @@ public: void visit_struct(::HIR::ItemPath ip, ::HIR::Struct& str) override { ::HIR::Visitor::visit_struct(ip, str); + + TU_MATCHA( (str.m_data), (se), + (Unit, + ), + (Tuple, + ), + (Named, + // Check the last field in the struct. + // - If it is Sized, leave as-is (struct is marked as Sized) + // - If it is known unsized, record the type + // - If it is a ?Sized parameter, mark as possible and record index for MIR + + // TODO: Ensure that only the last field is ?Sized + if( se.size() > 0 ) + { + const auto& last_field = se.back().second.ent; + // TODO: Recurse into path types + + // If the type is generic, and the pointed-to parameters is ?Sized, record as needing unsize + if( last_field.m_data.is_Generic() ) + { + const auto& te = last_field.m_data.as_Generic(); + + if( str.m_params.m_types.at(te.binding).m_is_sized == false ) + { + str.m_markings.unsized_field = se.size() - 1; + } + } + } + ) + ) } void visit_trait_impl(const ::HIR::SimplePath& trait_path, ::HIR::TraitImpl& impl) override @@ -59,10 +90,12 @@ public: ::HIR::TraitMarkings& markings = *const_cast<::HIR::TraitMarkings*>(markings_ptr); if( trait_path == m_lang_Unsize ) { DEBUG("Type " << impl.m_type << " can Unsize"); - markings.can_unsize = true; - // Determine which field is the one that does the unsize + ERROR(sp, E0000, "Unsize shouldn't be manually implemented"); } else if( trait_path == m_lang_CoerceUnsized ) { + if( markings_ptr->coerce_unsized_index != ~0u ) + ERROR(sp, E0000, "CoerceUnsized can only be implemented once per struct"); + DEBUG("Type " << impl.m_type << " can Coerce"); if( impl.m_trait_args.m_types.size() != 1 ) ERROR(sp, E0000, "Unexpected number of arguments for CoerceUnsized"); diff --git a/src/mir/cleanup.cpp b/src/mir/cleanup.cpp index 95b95f6e..439f8f91 100644 --- a/src/mir/cleanup.cpp +++ b/src/mir/cleanup.cpp @@ -143,88 +143,164 @@ const ::HIR::Literal* MIR_Cleanup_GetConstant(const Span& sp, const StaticTraitR return ::MIR::LValue::make_Field({ box$(::MIR::LValue::make_Deref({ box$(vtable_lv) })), vtable_idx }); } -::MIR::RValue MIR_Cleanup_Unsize(const ::MIR::TypeResolve& state, MirMutator& mutator, const ::HIR::TypeRef& dst_ty, const ::HIR::TypeRef& src_ty_inner, ::MIR::LValue ptr_value) +bool MIR_Cleanup_Unsize_GetMetadata(const ::MIR::TypeResolve& state, MirMutator& mutator, + const ::HIR::TypeRef& dst_ty, const ::HIR::TypeRef& src_ty, const ::MIR::LValue& ptr_value, + ::MIR::RValue& out_meta_val, ::HIR::TypeRef& out_meta_ty, bool& out_src_is_dst + ) { - const auto& dst_ty_inner = (dst_ty.m_data.is_Borrow() ? *dst_ty.m_data.as_Borrow().inner : *dst_ty.m_data.as_Pointer().inner); - - TU_MATCH_DEF( ::HIR::TypeRef::Data, (dst_ty_inner.m_data), (die), + TU_MATCH_DEF(::HIR::TypeRef::Data, (dst_ty.m_data), (de), ( - // Dunno? - MIR_TODO(state, "Unsize to pointer to " << dst_ty_inner); + MIR_TODO(state, "Obtain metadata converting to " << dst_ty); ), (Generic, - // Emit a cast rvalue - return ::MIR::RValue::make_Cast({ mv$(ptr_value), dst_ty.clone() }); + // TODO: What should be returned to indicate "no conversion" + return false; + ), + (Path, + // Source must be Path and Unsize + if( de.binding.is_Opaque() ) + return false; + + MIR_ASSERT(state, src_ty.m_data.is_Path(), "Unsize to path from non-path - " << src_ty); + const auto& se = src_ty.m_data.as_Path(); + MIR_ASSERT(state, de.binding.tag() == se.binding.tag(), "Unsize between mismatched types - " << dst_ty << " and " << src_ty); + MIR_ASSERT(state, de.binding.is_Struct(), "Unsize to non-struct - " << dst_ty); + MIR_ASSERT(state, de.binding.as_Struct() == se.binding.as_Struct(), "Unsize between mismatched types - " << dst_ty << " and " << src_ty); + const auto& str = *de.binding.as_Struct(); + MIR_ASSERT(state, str.m_markings.unsized_field != ~0u, "Unsize on type that doesn't implement have a ?Sized field - " << dst_ty); + + auto monomorph_cb_d = monomorphise_type_get_cb(state.sp, nullptr, &de.path.m_data.as_Generic().m_params, nullptr); + auto monomorph_cb_s = monomorphise_type_get_cb(state.sp, nullptr, &se.path.m_data.as_Generic().m_params, nullptr); + + // Return GetMetadata on the inner type + TU_MATCHA( (str.m_data), (se), + (Unit, + MIR_BUG(state, "Unit-like struct Unsize is impossible - " << src_ty); + ), + (Tuple, + const auto& ty_tpl = se.at( str.m_markings.unsized_field ).ent; + auto ty_d = monomorphise_type_with(state.sp, ty_tpl, monomorph_cb_d, false); + auto ty_s = monomorphise_type_with(state.sp, ty_tpl, monomorph_cb_s, false); + + return MIR_Cleanup_Unsize_GetMetadata(state, mutator, ty_d, ty_s, ptr_value, out_meta_val,out_meta_ty,out_src_is_dst); + ), + (Named, + const auto& ty_tpl = se.at( str.m_markings.unsized_field ).second.ent; + auto ty_d = monomorphise_type_with(state.sp, ty_tpl, monomorph_cb_d, false); + auto ty_s = monomorphise_type_with(state.sp, ty_tpl, monomorph_cb_s, false); + + return MIR_Cleanup_Unsize_GetMetadata(state, mutator, ty_d, ty_s, ptr_value, out_meta_val,out_meta_ty,out_src_is_dst); + ) + ) + throw ""; ), (Slice, - if( src_ty_inner.m_data.is_Array() ) + // Source must be an array (or generic) + if( src_ty.m_data.is_Array() ) { - const auto& in_array = src_ty_inner.m_data.as_Array(); - auto size_lval = mutator.in_temporary( ::HIR::TypeRef(::HIR::CoreType::Usize), ::MIR::Constant( static_cast<uint64_t>(in_array.size_val) ) ); - return ::MIR::RValue::make_MakeDst({ mv$(ptr_value), mv$(size_lval) }); + const auto& in_array = src_ty.m_data.as_Array(); + out_meta_ty = ::HIR::CoreType::Usize; + out_meta_val = ::MIR::Constant( static_cast<uint64_t>(in_array.size_val) ); + return true; } - else if( src_ty_inner.m_data.is_Generic() || (src_ty_inner.m_data.is_Path() && src_ty_inner.m_data.as_Path().binding.is_Opaque()) ) + else if( src_ty.m_data.is_Generic() || (src_ty.m_data.is_Path() && src_ty.m_data.as_Path().binding.is_Opaque()) ) { // HACK: FixedSizeArray uses `A: Unsize<[T]>` which will lead to the above code not working (as the size isn't known). // - Maybe _Meta on the `&A` would work as a stopgap (since A: Sized, it won't collide with &[T] or similar) - auto size_lval = mutator.in_temporary( ::HIR::TypeRef(::HIR::CoreType::Usize), ::MIR::RValue::make_DstMeta({ ptr_value.clone() }) ); - return ::MIR::RValue::make_MakeDst({ mv$(ptr_value), mv$(size_lval) }); + + return false; + + //out_meta_ty = ::HIR::CoreType::Usize; + //out_meta_val = ::MIR::RValue::make_DstMeta({ ptr_value.clone() }); + //return true; } else { - MIR_BUG(state, "Unsize to slice from non-array - " << src_ty_inner); + MIR_BUG(state, "Unsize to slice from non-array - " << src_ty); } ), (TraitObject, - auto unit_ptr = ::HIR::TypeRef::new_pointer(::HIR::BorrowType::Shared, ::HIR::TypeRef::new_unit()); - // If the data trait hasn't changed, re-create the DST - if( src_ty_inner.m_data.is_TraitObject() ) + + auto ty_unit_ptr = ::HIR::TypeRef::new_pointer(::HIR::BorrowType::Shared, ::HIR::TypeRef::new_unit()); + + // No data trait, vtable is a null unit pointer. + // - Shouldn't the vtable be just unit? + // - Codegen assumes it's a pointer. + if( de.m_trait.m_path.m_path == ::HIR::SimplePath() ) { - auto inner_ptr_lval = mutator.in_temporary( unit_ptr.clone(), ::MIR::RValue::make_DstPtr({ ptr_value.clone() }) ); - auto vtable_lval = mutator.in_temporary( unit_ptr.clone(), ::MIR::RValue::make_DstMeta({ ptr_value.clone() }) ); - return ::MIR::RValue::make_MakeDst({ mv$(inner_ptr_lval), mv$(vtable_lval) }); + auto null_lval = mutator.in_temporary( ::HIR::CoreType::Usize, ::MIR::Constant::make_Uint(0u) ); + out_meta_ty = ty_unit_ptr.clone(); + out_meta_val = ::MIR::RValue::make_Cast({ mv$(null_lval), mv$(ty_unit_ptr) }); } else { - // Obtain the vtable if the destination is a trait object vtable exists as an unnamable associated type - ::MIR::LValue vtable_lval; - if( die.m_trait.m_path.m_path == ::HIR::SimplePath() ) + const auto& trait_path = de.m_trait; + const auto& trait = *de.m_trait.m_trait_ptr; + + // Obtain vtable type `::"path"::to::Trait#vtable` + auto vtable_ty_spath = trait_path.m_path.m_path; + vtable_ty_spath.m_components.back() += "#vtable"; + const auto& vtable_ref = state.m_crate.get_struct_by_path(state.sp, vtable_ty_spath); + // Copy the param set from the trait in the trait object + ::HIR::PathParams vtable_params = trait_path.m_path.m_params.clone(); + // - Include associated types + for(const auto& ty_b : trait_path.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_type = ::HIR::TypeRef( ::HIR::GenericPath(vtable_ty_spath, mv$(vtable_params)), &vtable_ref ); + + out_meta_ty = ::HIR::TypeRef::new_pointer(::HIR::BorrowType::Shared, mv$(vtable_type)); + + // If the data trait hasn't changed, return the vtable pointer + if( src_ty.m_data.is_TraitObject() ) { - auto null_lval = mutator.in_temporary( ::HIR::CoreType::Usize, ::MIR::Constant::make_Uint(0u) ); - auto unit_ptr_2 = unit_ptr.clone(); - vtable_lval = mutator.in_temporary( mv$(unit_ptr), ::MIR::RValue::make_Cast({ mv$(null_lval), mv$(unit_ptr_2) }) ); + out_src_is_dst = true; + out_meta_val = ::MIR::RValue::make_DstMeta({ ptr_value.clone() }); } else { - const auto& trait = *die.m_trait.m_trait_ptr; - - auto vtable_ty_spath = die.m_trait.m_path.m_path; - vtable_ty_spath.m_components.back() += "#vtable"; - const auto& vtable_ref = state.m_crate.get_struct_by_path(state.sp, vtable_ty_spath); - // Copy the param set from the trait in the trait object - ::HIR::PathParams vtable_params = die.m_trait.m_path.m_params.clone(); - // - Include associated types on bound - for(const auto& ty_b : die.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_type = ::HIR::TypeRef( ::HIR::GenericPath(vtable_ty_spath, mv$(vtable_params)), &vtable_ref ); - - ::HIR::Path vtable { src_ty_inner.clone(), die.m_trait.m_path.clone(), "#vtable" }; - vtable_lval = mutator.in_temporary( - ::HIR::TypeRef::new_pointer(::HIR::BorrowType::Shared, mv$(vtable_type)), - ::MIR::RValue( ::MIR::Constant::make_ItemAddr(mv$(vtable)) ) - ); - + ::HIR::Path vtable { src_ty.clone(), trait_path.m_path.clone(), "#vtable" }; + out_meta_val = ::MIR::RValue( ::MIR::Constant::make_ItemAddr(mv$(vtable)) ); } - return ::MIR::RValue::make_MakeDst({ mv$(ptr_value), mv$(vtable_lval) }); } + return true; ) ) } +::MIR::RValue MIR_Cleanup_Unsize(const ::MIR::TypeResolve& state, MirMutator& mutator, const ::HIR::TypeRef& dst_ty, const ::HIR::TypeRef& src_ty_inner, ::MIR::LValue ptr_value) +{ + const auto& dst_ty_inner = (dst_ty.m_data.is_Borrow() ? *dst_ty.m_data.as_Borrow().inner : *dst_ty.m_data.as_Pointer().inner); + + ::HIR::TypeRef meta_type; + ::MIR::RValue meta_value; + bool source_is_dst = false; + if( MIR_Cleanup_Unsize_GetMetadata(state, mutator, dst_ty_inner, src_ty_inner, ptr_value, meta_value, meta_type, source_is_dst) ) + { + // TODO: There is a case where the source is already a fat pointer. In that case the pointer of the new DST must be the source DST pointer + auto meta_lval = mutator.in_temporary( mv$(meta_type), mv$(meta_value) ); + if( source_is_dst ) + { + auto ty_unit_ptr = ::HIR::TypeRef::new_pointer(::HIR::BorrowType::Shared, ::HIR::TypeRef::new_unit()); + auto thin_ptr_lval = mutator.in_temporary( mv$(ty_unit_ptr), ::MIR::RValue::make_DstPtr({ mv$(ptr_value) }) ); + + return ::MIR::RValue::make_MakeDst({ mv$(thin_ptr_lval), mv$(meta_lval) }); + } + else + { + return ::MIR::RValue::make_MakeDst({ mv$(ptr_value), mv$(meta_lval) }); + } + } + else + { + // Emit a cast rvalue, as something is still generic. + return ::MIR::RValue::make_Cast({ mv$(ptr_value), dst_ty.clone() }); + } +} + ::MIR::RValue MIR_Cleanup_CoerceUnsized(const ::MIR::TypeResolve& state, MirMutator& mutator, const ::HIR::TypeRef& dst_ty, const ::HIR::TypeRef& src_ty, ::MIR::LValue value) { TRACE_FUNCTION_F(dst_ty << " <- " << src_ty << " ( " << value << " )"); |