diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/hir/deserialise.cpp | 1 | ||||
-rw-r--r-- | src/hir/hir.hpp | 1 | ||||
-rw-r--r-- | src/hir/serialise.cpp | 1 | ||||
-rw-r--r-- | src/hir_conv/markings.cpp | 30 | ||||
-rw-r--r-- | src/hir_typeck/expr_cs.cpp | 44 | ||||
-rw-r--r-- | src/hir_typeck/helpers.cpp | 362 | ||||
-rw-r--r-- | src/hir_typeck/helpers.hpp | 7 |
7 files changed, 315 insertions, 131 deletions
diff --git a/src/hir/deserialise.cpp b/src/hir/deserialise.cpp index 19306a63..f4409310 100644 --- a/src/hir/deserialise.cpp +++ b/src/hir/deserialise.cpp @@ -586,6 +586,7 @@ namespace { m.dst_type = static_cast< ::HIR::TraitMarkings::DstType>( m_in.read_tag() ); m.coerce_unsized_index = m_in.read_count( ); m.unsized_field = m_in.read_count( ); + m.unsized_param = m_in.read_count(); // TODO: auto_impls return m; } diff --git a/src/hir/hir.hpp b/src/hir/hir.hpp index b7d116e3..51235c7d 100644 --- a/src/hir/hir.hpp +++ b/src/hir/hir.hpp @@ -177,6 +177,7 @@ struct TraitMarkings TraitObject, // (Trait) } dst_type; unsigned int unsized_field = ~0u; + unsigned int unsized_param = ~0u; /// `true` if there is a Copy impl bool is_copy = false; diff --git a/src/hir/serialise.cpp b/src/hir/serialise.cpp index e5264ae2..77e17dba 100644 --- a/src/hir/serialise.cpp +++ b/src/hir/serialise.cpp @@ -864,6 +864,7 @@ namespace { m_out.write_tag( static_cast<unsigned int>(m.dst_type) ); m_out.write_count( m.coerce_unsized_index ); m_out.write_count( m.unsized_field ); + m_out.write_count( m.unsized_param ); // TODO: auto_impls } diff --git a/src/hir_conv/markings.cpp b/src/hir_conv/markings.cpp index a294f47b..4550bef5 100644 --- a/src/hir_conv/markings.cpp +++ b/src/hir_conv/markings.cpp @@ -43,6 +43,33 @@ public: { str.m_markings.unsized_field = (str.m_data.is_Tuple() ? str.m_data.as_Tuple().size()-1 : str.m_data.as_Named().size()-1); } + + // Rules: + // - A type parameter must be ?Sized + // - That type parameter must only be used as part of the last field, and only once + // - If the final field isn't the parameter, it must also impl Unsize + + // HACK: Just determine what ?Sized parameter is controlling the sized-ness + if( str.m_markings.dst_type == ::HIR::TraitMarkings::DstType::Possible ) + { + auto& last_field_ty = (str.m_data.is_Tuple() ? str.m_data.as_Tuple().back().ent : str.m_data.as_Named().back().second.ent); + auto ty = ::HIR::TypeRef("", 0); + for(size_t i = 0; i < str.m_params.m_types.size(); i++) + { + const auto& param = str.m_params.m_types[i]; + auto ty = ::HIR::TypeRef(param.m_name, i); + if( !param.m_is_sized ) + { + if( visit_ty_with(last_field_ty, [&](const auto& t){ return t == ty; }) ) + { + assert(str.m_markings.unsized_param == ~0u); + str.m_markings.unsized_param = i; + } + } + } + ASSERT_BUG(Span(), str.m_markings.unsized_param != ~0u, "No unsized param for type " << ip); + str.m_markings.can_unsize = true; + } } void visit_trait(::HIR::ItemPath ip, ::HIR::Trait& tr) override @@ -212,6 +239,7 @@ public: ::HIR::TraitMarkings::DstType get_field_dst_type(const ::HIR::TypeRef& ty, const ::HIR::GenericParams& inner_def, const ::HIR::GenericParams& params_def, const ::HIR::PathParams* params) { + TRACE_FUNCTION_F("ty=" << ty); // If the type is generic, and the pointed-to parameters is ?Sized, record as needing unsize if( const auto* te = ty.m_data.opt_Generic() ) { @@ -242,7 +270,7 @@ public: // If the type is a struct, check it (recursively) if( ! te->path.m_data.is_Generic() ) { // Associated type, TODO: Check this better. - return ::HIR::TraitMarkings::DstType::Possible; + return ::HIR::TraitMarkings::DstType::None; } else if( te->binding.is_Struct() ) { const auto& params_tpl = te->path.m_data.as_Generic().m_params; diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp index 4a8f381a..1ac3b2f0 100644 --- a/src/hir_typeck/expr_cs.cpp +++ b/src/hir_typeck/expr_cs.cpp @@ -4415,14 +4415,11 @@ namespace { // Search for Unsize // - If `right`: ::core::marker::Unsize<`left`> { - const auto& lang_Unsize = context.m_crate.get_lang_item_path(sp, "unsize"); - ::HIR::PathParams pp; - pp.m_types.push_back( ty_dst.clone() ); - bool found = context.m_resolve.find_trait_impls(sp, lang_Unsize, pp, ty_src, [&](auto impl, auto cmp) { - // TODO: Allow fuzzy match if only match - return cmp == ::HIR::Compare::Equal; + auto cmp = context.m_resolve.can_unsize(sp, ty_dst, ty_src, [&](auto new_dst) { + // Equate these two types }); - if( found ) { + if(cmp == ::HIR::Compare::Equal) + { DEBUG("- Unsize " << &*node_ptr << " -> " << ty_dst); auto ty_dst_b = ::HIR::TypeRef::new_borrow(bt, ty_dst.clone()); auto ty_dst_b2 = ty_dst_b.clone(); @@ -4431,18 +4428,27 @@ namespace { return true; } - } - - if( ty_dst.m_data.is_Path() && ty_dst.m_data.as_Path().binding.is_Unbound() ) - { - } - else if( ty_src.m_data.is_Path() && ty_src.m_data.as_Path().binding.is_Unbound() ) - { - } - else if( ty_dst.compare_with_placeholders(sp, ty_src, context.m_ivars.callback_resolve_infer()) != ::HIR::Compare::Unequal ) - { - context.equate_types(sp, ty_dst, ty_src); - return true; + if(cmp == ::HIR::Compare::Unequal) + { + // No unsize possible, equate types + // - Only if they're not already fixed as unequal (that gets handled elsewhere) + if( ty_dst.m_data.is_Path() && ty_dst.m_data.as_Path().binding.is_Unbound() ) + { + } + else if( ty_src.m_data.is_Path() && ty_src.m_data.as_Path().binding.is_Unbound() ) + { + } + else if( ty_dst.compare_with_placeholders(sp, ty_src, context.m_ivars.callback_resolve_infer()) != ::HIR::Compare::Unequal ) + { + context.equate_types(sp, ty_dst, ty_src); + return true; + } + } + if(cmp == ::HIR::Compare::Fuzzy) + { + // Not sure yet + return false; + } } // Keep trying diff --git a/src/hir_typeck/helpers.cpp b/src/hir_typeck/helpers.cpp index c29bc64b..33d26184 100644 --- a/src/hir_typeck/helpers.cpp +++ b/src/hir_typeck/helpers.cpp @@ -1075,66 +1075,26 @@ bool TraitResolution::find_trait_impls(const Span& sp, } // Magic Unsize impls to trait objects - if( trait == lang_Unsize ) { + if( trait == lang_Unsize ) + { ASSERT_BUG(sp, params.m_types.size() == 1, "Unsize trait requires a single type param"); const auto& dst_ty = this->m_ivars.get_type(params.m_types[0]); - TU_IFLET( ::HIR::TypeRef::Data, dst_ty.m_data, TraitObject, e, - // Magic impl if T: ThisTrait - bool good; - - ::HIR::TypeRef::Data::Data_TraitObject tmp_e; - tmp_e.m_trait.m_path = e.m_trait.m_path.m_path; - ::HIR::Compare total_cmp = ::HIR::Compare::Equal; - if( e.m_trait.m_path.m_path == ::HIR::SimplePath() ) { - ASSERT_BUG(sp, e.m_markers.size() > 0, "TraitObject with no traits - " << dst_ty); - good = true; - } - else { - good = find_trait_impls(sp, e.m_trait.m_path.m_path, e.m_trait.m_path.m_params, ty, - [&](const auto impl, auto cmp){ - if( cmp == ::HIR::Compare::Unequal ) - return false; - total_cmp &= cmp; - tmp_e.m_trait.m_path.m_params = impl.get_trait_params(); - for(const auto& aty : e.m_trait.m_type_bounds) { - auto atyv = impl.get_type(aty.first.c_str()); - if( atyv == ::HIR::TypeRef() ) - { - // Get the trait from which this associated type comes. - // Insert a UfcsKnown path for that - auto p = ::HIR::Path( ty.clone(), e.m_trait.m_path.clone(), aty.first ); - // Run EAT - atyv = this->expand_associated_types( sp, ::HIR::TypeRef::new_path( mv$(p), {} ) ); - } - tmp_e.m_trait.m_type_bounds[aty.first] = mv$(atyv); - } - return true; - }); - } - auto cb = [&](const auto impl, auto cmp){ - if( cmp == ::HIR::Compare::Unequal ) - return false; - total_cmp &= cmp; - tmp_e.m_markers.back().m_params = impl.get_trait_params(); - return true; - }; - for(const auto& marker : e.m_markers) - { - if(!good) break; - tmp_e.m_markers.push_back( marker.m_path ); - good &= find_trait_impls(sp, marker.m_path, marker.m_params, ty, cb); - } - if( good ) { - ::HIR::PathParams real_params { ::HIR::TypeRef( ::HIR::TypeRef::Data(mv$(tmp_e)) ) }; - return callback( ImplRef(type.clone(), mv$(real_params), {}), total_cmp ); - } - else { - return false; - } - ) + if( find_trait_impls_bound(sp, trait, params, type, callback) ) + return true; - // [T;N] -> [T] is handled down with array indexing + bool rv = false; + auto cb = [&](auto new_dst) { + ::HIR::PathParams real_params { mv$(new_dst) }; + rv = callback( ImplRef(type.clone(), mv$(real_params), {}), ::HIR::Compare::Fuzzy ); + }; + auto cmp = this->can_unsize(sp, dst_ty, type, cb); + if( cmp == ::HIR::Compare::Equal ) + { + assert(!rv); + rv = callback( ImplRef(type.clone(), params.clone(), {}), ::HIR::Compare::Equal ); + } + return rv; } // Magical CoerceUnsized impls for various types @@ -1278,23 +1238,6 @@ bool TraitResolution::find_trait_impls(const Span& sp, */ return false; } - - // Unsize impl for arrays - if( trait == lang_Unsize ) - { - ASSERT_BUG(sp, params.m_types.size() == 1, ""); - const auto& dst_ty = m_ivars.get_type( params.m_types[0] ); - - TU_IFLET(::HIR::TypeRef::Data, dst_ty.m_data, Slice, e2, - auto cmp = e.inner->compare_with_placeholders(sp, *e2.inner, m_ivars.callback_resolve_infer()); - if( cmp != ::HIR::Compare::Unequal ) { - ::HIR::PathParams pp; - // - <[`array_inner`]> so it can be matched with the param by the caller - pp.m_types.push_back( ::HIR::TypeRef::new_slice(e.inner->clone()) ); - return callback( ImplRef(type.clone(), mv$(pp), {}), cmp ); - } - ) - } ) @@ -1344,44 +1287,6 @@ bool TraitResolution::find_trait_impls(const Span& sp, return rv; } - // Trait objects can unsize to a subset of their traits. - if( trait == lang_Unsize ) - { - ASSERT_BUG(sp, params.m_types.size() == 1, ""); - const auto& dst_ty = m_ivars.get_type( params.m_types[0] ); - if( ! dst_ty.m_data.is_TraitObject() ) { - // If the destination isn't a trait object, don't even bother - return false; - } - const auto& e2 = dst_ty.m_data.as_TraitObject(); - - auto cmp = ::HIR::Compare::Equal; - - // TODO: Fuzzy compare - if( e2.m_trait != e.m_trait ) { - return false; - } - // The destination must have a strict subset of marker traits. - const auto& src_markers = e.m_markers; - const auto& dst_markers = e2.m_markers; - for(const auto& mt : dst_markers) - { - // TODO: Fuzzy match - bool found = false; - for(const auto& omt : src_markers) { - if( omt == mt ) { - found = true; - break; - } - } - if( !found ) { - // Return early. - return false; - } - } - - return callback( ImplRef(&type, &e.m_trait.m_path.m_params, &e.m_trait.m_type_bounds), cmp ); - } ) TU_IFLET(::HIR::TypeRef::Data, type.m_data, ErasedType, e, @@ -3109,6 +3014,241 @@ bool TraitResolution::trait_contains_type(const Span& sp, const ::HIR::GenericPa ) ) } +// Checks if a type can unsize to another +// - Returns Compare::Equal if the unsize is possible and fully known +// - Returns Compare::Fuzzy if the unsize is possible, but still unknown. +// - Returns Compare::Unequal if the unsize is impossibe (for any reason) +// +// Closure is called `get_new_type` is true, and the unsize is possible +// +// usecases: +// - Checking for an impl as part of impl selection (return True/False/Maybe with required match for Maybe) +// - Checking for an impl as part of typeck (return True/False/Maybe with unsize possibility OR required equality) +::HIR::Compare TraitResolution::can_unsize( + const Span& sp, const ::HIR::TypeRef& dst_ty, const ::HIR::TypeRef& src_ty, + ::std::function<void(::HIR::TypeRef new_dst)>* new_type_callback, + ::std::function<void(const ::HIR::TypeRef& dst, const ::HIR::TypeRef& src)>* infer_callback + ) const +{ + TRACE_FUNCTION_F(dst_ty << " <- " << src_ty); + const auto& lang_Unsize = this->m_crate.get_lang_item_path(sp, "unsize"); + + // 1. Test for type equality + { + auto cmp = dst_ty.compare_with_placeholders(sp, src_ty, m_ivars.callback_resolve_infer()); + if( cmp == ::HIR::Compare::Equal ) + { + return ::HIR::Compare::Unequal; + } + } + + // 2. If either side is an ivar, fuzzy. + if( dst_ty.m_data.is_Infer() || src_ty.m_data.is_Infer() ) + { + // Inform the caller that these two types could unsize to each other + // - This allows the coercions code to move the coercion rule up + if( infer_callback ) + { + (*infer_callback)(dst_ty, src_ty); + } + return ::HIR::Compare::Fuzzy; + } + + { + bool found_bound = this->iterate_bounds([&](const auto& gb){ + if(!gb.is_TraitBound()) + return false; + const auto& be = gb.as_TraitBound(); + if(be.trait.m_path.m_path != lang_Unsize) + return false; + const auto& be_dst = be.trait.m_path.m_params.m_types.at(0); + + auto cmp = src_ty.compare_with_placeholders(sp, be.type, m_ivars.callback_resolve_infer()); + if(cmp == ::HIR::Compare::Unequal) return false; + + cmp &= dst_ty.compare_with_placeholders(sp, be_dst, m_ivars.callback_resolve_infer()); + if(cmp == ::HIR::Compare::Unequal) return false; + + if( cmp != ::HIR::Compare::Equal ) + { + TODO(sp, "Found bound " << dst_ty << "=" << be_dst << " <- " << src_ty << "=" << be.type); + } + return true; + }); + if( found_bound ) + { + return ::HIR::Compare::Equal; + } + } + + // Struct<..., T, ...>: Unsize<Struct<..., U, ...>> + if( dst_ty.m_data.is_Path() && src_ty.m_data.is_Path() ) + { + bool dst_is_unsizable = dst_ty.m_data.as_Path().binding.is_Struct() && dst_ty.m_data.as_Path().binding.as_Struct()->m_markings.can_unsize; + bool src_is_unsizable = src_ty.m_data.as_Path().binding.is_Struct() && src_ty.m_data.as_Path().binding.as_Struct()->m_markings.can_unsize; + if( dst_is_unsizable || src_is_unsizable ) + { + DEBUG("Struct unsize? " << dst_ty << " <- " << src_ty); + const auto& str = *dst_ty.m_data.as_Path().binding.as_Struct(); + const auto& dst_gp = dst_ty.m_data.as_Path().path.m_data.as_Generic(); + const auto& src_gp = src_ty.m_data.as_Path().path.m_data.as_Generic(); + + if( dst_gp == src_gp ) + { + DEBUG("Can't Unsize, destination and source are identical"); + return ::HIR::Compare::Unequal; + } + else if( dst_gp.m_path == src_gp.m_path ) + { + DEBUG("Checking for Unsize " << dst_gp << " <- " << src_gp); + // Structures are equal, add the requirement that the ?Sized parameter also impl Unsize + const auto& dst_inner = m_ivars.get_type( dst_gp.m_params.m_types.at(str.m_markings.unsized_param) ); + const auto& src_inner = m_ivars.get_type( src_gp.m_params.m_types.at(str.m_markings.unsized_param) ); + + auto cb = [&](auto d){ + assert(new_type_callback); + + // Re-create structure with s/d + auto dst_gp_new = dst_gp.clone(); + dst_gp_new.m_params.m_types.at(str.m_markings.unsized_param) = mv$(d); + (*new_type_callback)( ::HIR::TypeRef::new_path(mv$(dst_gp_new), &str) ); + }; + if( new_type_callback ) + { + ::std::function<void(::HIR::TypeRef)> cb_p = cb; + return this->can_unsize(sp, dst_inner, src_inner, &cb_p, infer_callback); + } + else + { + return this->can_unsize(sp, dst_inner, src_inner, nullptr, infer_callback); + } + } + else + { + DEBUG("Can't Unsize, destination and source are different structs"); + return ::HIR::Compare::Unequal; + } + } + } + + // (Trait) <- Foo + if( const auto* de = dst_ty.m_data.opt_TraitObject() ) + { + // TODO: Check if src_ty is !Sized + // - Only allowed if the source is a trait object with the same data trait and lesser bounds + + DEBUG("TraitObject unsize? " << dst_ty << " <- " << src_ty); + + // (Trait) <- (Trait+Foo) + if( const auto* se = src_ty.m_data.opt_TraitObject() ) + { + auto rv = ::HIR::Compare::Equal; + // 1. Data trait must be the same (TODO: Fuzzy) + if( de->m_trait != se->m_trait ) + { + return ::HIR::Compare::Unequal; + } + + // 2. Destination markers must be a strict subset + for(const auto& mt : de->m_markers) + { + // TODO: Fuzzy match + bool found = false; + for(const auto& omt : se->m_markers) { + if( omt == mt ) { + found = true; + break; + } + } + if( !found ) { + // Return early. + return ::HIR::Compare::Unequal; + } + } + + if( rv == ::HIR::Compare::Fuzzy && new_type_callback ) + { + // TODO: Inner type + } + return ::HIR::Compare::Equal; + } + + bool good; + ::HIR::Compare total_cmp = ::HIR::Compare::Equal; + + ::HIR::TypeRef::Data::Data_TraitObject tmp_e; + tmp_e.m_trait.m_path = de->m_trait.m_path.m_path; + + // Check data trait first. + if( de->m_trait.m_path.m_path == ::HIR::SimplePath() ) { + ASSERT_BUG(sp, de->m_markers.size() > 0, "TraitObject with no traits - " << dst_ty); + good = true; + } + else { + good = find_trait_impls(sp, de->m_trait.m_path.m_path, de->m_trait.m_path.m_params, src_ty, + [&](const auto impl, auto cmp) { + if( cmp == ::HIR::Compare::Unequal ) + return false; + total_cmp &= cmp; + tmp_e.m_trait.m_path.m_params = impl.get_trait_params(); + for(const auto& aty : de->m_trait.m_type_bounds) { + auto atyv = impl.get_type(aty.first.c_str()); + if( atyv == ::HIR::TypeRef() ) + { + // Get the trait from which this associated type comes. + // Insert a UfcsKnown path for that + auto p = ::HIR::Path( src_ty.clone(), de->m_trait.m_path.clone(), aty.first ); + // Run EAT + atyv = this->expand_associated_types( sp, ::HIR::TypeRef::new_path( mv$(p), {} ) ); + } + tmp_e.m_trait.m_type_bounds[aty.first] = mv$(atyv); + } + return true; + }); + } + + // Then markers + auto cb = [&](const auto impl, auto cmp){ + if( cmp == ::HIR::Compare::Unequal ) + return false; + total_cmp &= cmp; + tmp_e.m_markers.back().m_params = impl.get_trait_params(); + return true; + }; + for(const auto& marker : de->m_markers) + { + if(!good) break; + tmp_e.m_markers.push_back( marker.m_path ); + good &= find_trait_impls(sp, marker.m_path, marker.m_params, src_ty, cb); + } + + if( good && total_cmp == ::HIR::Compare::Fuzzy && new_type_callback ) + { + (*new_type_callback)( ::HIR::TypeRef(mv$(tmp_e)) ); + } + return total_cmp; + } + + // [T] <- [T; n] + if( const auto* de = dst_ty.m_data.opt_Slice() ) + { + if( const auto* se = src_ty.m_data.opt_Array() ) + { + DEBUG("Array unsize? " << *de->inner << " <- " << *se->inner); + auto cmp = de->inner->compare_with_placeholders(sp, *se->inner, m_ivars.callback_resolve_infer()); + // TODO: Indicate to caller that for this to be true, these two must be the same. + // - I.E. if true, equate these types + if(cmp == ::HIR::Compare::Fuzzy && new_type_callback) + { + (*new_type_callback)( ::HIR::TypeRef::new_slice( se->inner->clone() ) ); + } + return cmp; + } + } + + DEBUG("Can't unsize, no rules matched"); + return ::HIR::Compare::Unequal; +} const ::HIR::TypeRef* TraitResolution::type_is_owned_box(const Span& sp, const ::HIR::TypeRef& ty) const { TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Path, e, diff --git a/src/hir_typeck/helpers.hpp b/src/hir_typeck/helpers.hpp index a1db0af9..1f63dbf4 100644 --- a/src/hir_typeck/helpers.hpp +++ b/src/hir_typeck/helpers.hpp @@ -265,6 +265,13 @@ public: bool trait_contains_type(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::std::string& name, ::HIR::GenericPath& out_path) const; ::HIR::Compare type_is_copy(const Span& sp, const ::HIR::TypeRef& ty) const; + // If `new_type_callback` is populated, it will be called with the actual/possible dst_type + // If `infer_callback` is populated, it will be called when either side is an ivar + ::HIR::Compare can_unsize(const Span& sp, const ::HIR::TypeRef& dst_ty, const ::HIR::TypeRef& src_ty, ::std::function<void(::HIR::TypeRef new_dst)> new_type_callback) const { + return can_unsize(sp, dst_ty, src_ty, &new_type_callback); + } + ::HIR::Compare can_unsize(const Span& sp, const ::HIR::TypeRef& dst_ty, const ::HIR::TypeRef& src_ty, ::std::function<void(::HIR::TypeRef new_dst)>* new_type_callback, ::std::function<void(const ::HIR::TypeRef& dst, const ::HIR::TypeRef& src)>* infer_callback=nullptr) const; + const ::HIR::TypeRef* type_is_owned_box(const Span& sp, const ::HIR::TypeRef& ty) const; private: |