diff options
author | John Hodge (bugs) <tpg@mutabah.net> | 2017-06-04 21:23:24 +0800 |
---|---|---|
committer | John Hodge (bugs) <tpg@mutabah.net> | 2017-06-04 21:23:24 +0800 |
commit | 83dbb728f62306d2e43b2688dd0f2d320fd5b038 (patch) | |
tree | a064267bdf8d0455ed725140abfcbed3e04b2d4a /src/hir_typeck | |
parent | 0b9fd0014c8f32ecf299dae2ad1811dfb484af46 (diff) | |
parent | f19c75571c48588fb3816e8eb5b96f03474fbdf5 (diff) | |
download | mrust-83dbb728f62306d2e43b2688dd0f2d320fd5b038.tar.gz |
Merge branch 'master' of https://github.com/thepowersgang/mrustc
Diffstat (limited to 'src/hir_typeck')
-rw-r--r-- | src/hir_typeck/expr_check.cpp | 72 | ||||
-rw-r--r-- | src/hir_typeck/expr_cs.cpp | 146 | ||||
-rw-r--r-- | src/hir_typeck/helpers.cpp | 112 | ||||
-rw-r--r-- | src/hir_typeck/helpers.hpp | 2 | ||||
-rw-r--r-- | src/hir_typeck/static.cpp | 237 | ||||
-rw-r--r-- | src/hir_typeck/static.hpp | 2 |
6 files changed, 514 insertions, 57 deletions
diff --git a/src/hir_typeck/expr_check.cpp b/src/hir_typeck/expr_check.cpp index 1b4317a6..fe67865f 100644 --- a/src/hir_typeck/expr_check.cpp +++ b/src/hir_typeck/expr_check.cpp @@ -22,12 +22,15 @@ namespace { const ::HIR::TypeRef& ret_type; ::std::vector< const ::HIR::TypeRef*> closure_ret_types; + ::HIR::SimplePath m_lang_Index; + public: ExprVisitor_Validate(const StaticTraitResolve& res, const t_args& args, const ::HIR::TypeRef& ret_type): m_resolve(res), //m_args(args), ret_type(ret_type) { + m_lang_Index = m_resolve.m_crate.get_lang_item_path_opt("index"); } void visit_root(::HIR::ExprPtr& node_ptr) @@ -255,8 +258,7 @@ namespace { { TRACE_FUNCTION_F(&node << " ... [ ... ]"); check_associated_type(node.span(), - node.m_res_type, - this->get_lang_item_path(node.span(), "index"), { node.m_index->m_res_type.clone() }, node.m_value->m_res_type, "Target" + node.m_res_type, m_lang_Index, { node.m_index->m_res_type.clone() }, node.m_value->m_res_type, "Target" ); node.m_value->visit( *this ); @@ -333,7 +335,10 @@ namespace { const auto& src_ty = node.m_value->m_res_type; const auto& dst_ty = node.m_res_type; - if( src_ty.m_data.is_Borrow() && dst_ty.m_data.is_Borrow() ) + if( src_ty == dst_ty ) + { + } + else if( src_ty.m_data.is_Borrow() && dst_ty.m_data.is_Borrow() ) { const auto& se = src_ty.m_data.as_Borrow(); const auto& de = dst_ty.m_data.as_Borrow(); @@ -363,10 +368,20 @@ namespace { void visit(::HIR::ExprNode_Deref& node) override { TRACE_FUNCTION_F(&node << " *..."); - check_associated_type(node.span(), - node.m_res_type, - this->get_lang_item_path(node.span(), "deref"), {}, node.m_value->m_res_type, "Target" - ); + const auto& ty = node.m_value->m_res_type; + + if( ty.m_data.is_Pointer() ) { + check_types_equal(node.span(), node.m_res_type, *ty.m_data.as_Pointer().inner); + } + else if( ty.m_data.is_Borrow() ) { + check_types_equal(node.span(), node.m_res_type, *ty.m_data.as_Borrow().inner); + } + else { + check_associated_type(node.span(), + node.m_res_type, + this->get_lang_item_path(node.span(), "deref"), {}, node.m_value->m_res_type, "Target" + ); + } node.m_value->visit( *this ); } @@ -473,6 +488,14 @@ namespace { TODO(sp, "Union in StructLiteral"); ), (Struct, + if( e->m_data.is_Unit() ) + { + ASSERT_BUG(node.span(), node.m_values.size() == 0, "Values provided for unit-like struct"); + ASSERT_BUG(node.span(), ! node.m_base_value, "Values provided for unit-like struct"); + return ; + } + + ASSERT_BUG(node.span(), e->m_data.is_Named(), "StructLiteral not pointing to a braced struct, instead " << e->m_data.tag_str() << " - " << ty); fields_ptr = &e->m_data.as_Named(); ) ) @@ -704,6 +727,9 @@ namespace { cache.m_monomorph_cb = mv$(monomorph_cb); // Bounds + for(size_t i = 0; i < cache.m_fcn_params->m_types.size(); i ++) + { + } for(const auto& bound : cache.m_fcn_params->m_bounds) { TU_MATCH(::HIR::GenericBound, (bound), (be), @@ -713,7 +739,10 @@ namespace { ), (TraitBound, auto real_type = monomorphise_type_with(sp, be.type, cache.m_monomorph_cb); + m_resolve.expand_associated_types(sp, real_type); auto real_trait = monomorphise_genericpath_with(sp, be.trait.m_path, cache.m_monomorph_cb, false); + for(auto& t : real_trait.m_params.m_types) + m_resolve.expand_associated_types(sp, t); DEBUG("Bound " << be.type << ": " << be.trait); DEBUG("= (" << real_type << ": " << real_trait << ")"); const auto& trait_params = real_trait.m_params; @@ -728,6 +757,7 @@ namespace { ASSERT_BUG(sp, has_ty, "Type " << assoc.first << " not found in chain of " << real_trait); auto other_ty = monomorphise_type_with(sp, assoc.second, cache.m_monomorph_cb, true); + m_resolve.expand_associated_types(sp, other_ty); check_associated_type(sp, other_ty, type_trait_path.m_path, type_trait_path.m_params, real_type, assoc.first.c_str()); } @@ -992,10 +1022,29 @@ namespace { const ::HIR::SimplePath& trait, const ::HIR::PathParams& params, const ::HIR::TypeRef& ity, const char* name ) const { - // TODO: Actually check. - #if 0 - bool found = m_resolve.find_impl(sp, trait, ¶ms, ity, [&](auto impl, bool fuzzy){ - + if( trait == m_lang_Index && ity.m_data.is_Array() ) { + if(name) + { + if( res != *ity.m_data.as_Array().inner ) { + ERROR(sp, E0000, "Associated type on " << trait << params << " for " << ity << " doesn't match - " << res << " != " << *ity.m_data.as_Array().inner); + } + } + return ; + } + bool found = m_resolve.find_impl(sp, trait, ¶ms, ity, [&](auto impl, bool fuzzy) { + if( name ) + { + auto atyv = impl.get_type(name); + m_resolve.expand_associated_types(sp, atyv); + if( atyv == ::HIR::TypeRef() ) + { + // TODO: Check that `res` is <ity as trait>::name + } + else if( res != atyv ) + { + ERROR(sp, E0000, "Associated type on " << trait << params << " for " << ity << " doesn't match - " << res << " != " << atyv); + } + } return true; }); @@ -1003,7 +1052,6 @@ namespace { { ERROR(sp, E0000, "Cannot find an impl of " << trait << params << " for " << ity); } - #endif } const ::HIR::SimplePath& get_lang_item_path(const Span& sp, const char* name) const diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp index 2388d078..eb922414 100644 --- a/src/hir_typeck/expr_cs.cpp +++ b/src/hir_typeck/expr_cs.cpp @@ -1061,6 +1061,7 @@ namespace { for( auto& val : node.m_args ) { this->context.add_ivars( val->m_res_type ); } + this->context.m_ivars.add_ivars_params(node.m_path.m_params); // - Create ivars in path, and set result type const auto ty = this->get_structenum_ty(node.span(), node.m_is_struct, node.m_path); @@ -1131,6 +1132,7 @@ namespace { void visit(::HIR::ExprNode_StructLiteral& node) override { TRACE_FUNCTION_F(&node << " " << node.m_path << "{...} [" << (node.m_is_struct ? "struct" : "enum") << "]"); + this->add_ivars_generic_path(node.span(), node.m_path); for( auto& val : node.m_values ) { this->context.add_ivars( val.second->m_res_type ); } @@ -1155,6 +1157,12 @@ namespace { const auto& enm = *e; auto it = ::std::find_if(enm.m_variants.begin(), enm.m_variants.end(), [&](const auto&v)->auto{ return v.first == var_name; }); assert(it != enm.m_variants.end()); + if( it->second.is_Unit() || it->second.is_Value() || it->second.is_Tuple() ) { + ASSERT_BUG(node.span(), node.m_values.size() == 0, "Values provided for unit-like variant"); + ASSERT_BUG(node.span(), ! node.m_base_value, "Values provided for unit-like variant"); + return ; + } + ASSERT_BUG(node.span(), it->second.is_Struct(), "_StructLiteral for non-struct variant - " << node.m_path); fields_ptr = &it->second.as_Struct(); generics = &enm.m_params; ), @@ -1162,6 +1170,18 @@ namespace { TODO(node.span(), "StructLiteral of a union - " << ty); ), (Struct, + if( e->m_data.is_Unit() || e->m_data.is_Tuple() ) + { + ASSERT_BUG(node.span(), node.m_values.size() == 0, "Values provided for unit-like struct"); + + if( node.m_base_value ) { + auto _ = this->push_inner_coerce_scoped(false); + node.m_base_value->visit( *this ); + } + return ; + } + + ASSERT_BUG(node.span(), e->m_data.is_Named(), "StructLiteral not pointing to a braced struct, instead " << e->m_data.tag_str() << " - " << ty); fields_ptr = &e->m_data.as_Named(); generics = &e->m_params; ) @@ -1208,7 +1228,7 @@ namespace { } des_ty = &des_ty_cache; } - this->equate_types_inner_coerce(node.span(), *des_ty, val.second); + this->context.equate_types_coerce(node.span(), *des_ty, val.second); } // Convert bounds on the type into rules @@ -3390,7 +3410,8 @@ void Context::add_binding(const Span& sp, ::HIR::Pattern& pat, const ::HIR::Type context.equate_types(sp, type, ::HIR::TypeRef::new_borrow( ::HIR::BorrowType::Shared, ::HIR::TypeRef(::HIR::CoreType::Str) )); ), (ByteString, - context.equate_types(sp, type, ::HIR::TypeRef::new_borrow( ::HIR::BorrowType::Shared, ::HIR::TypeRef::new_slice(::HIR::CoreType::U8) )); + // NOTE: Matches both &[u8] and &[u8; N], so doesn't provide type information + // TODO: Check the type. ), (Named, // TODO: Get type of the value and equate it @@ -3730,10 +3751,14 @@ void Context::add_binding(const Span& sp, ::HIR::Pattern& pat, const ::HIR::Type this->add_ivars_params( e.path.m_params ); this->equate_types( sp, type, ::HIR::TypeRef::new_path(e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)) ); + if( e.sub_patterns.empty() ) + return ; + assert(e.binding); const auto& str = *e.binding; + // - assert check from earlier pass - assert( str.m_data.is_Named() ); + ASSERT_BUG(sp, str.m_data.is_Named(), "Struct pattern on non-Named struct"); const auto& sd = str.m_data.as_Named(); const auto& params = e.path.m_params; @@ -3806,6 +3831,9 @@ void Context::add_binding(const Span& sp, ::HIR::Pattern& pat, const ::HIR::Type this->equate_types( sp, type, ::HIR::TypeRef::new_path(mv$(path), ::HIR::TypeRef::TypePathBinding(e.binding_ptr)) ); } + if( e.sub_patterns.empty() ) + return ; + assert(e.binding_ptr); const auto& enm = *e.binding_ptr; const auto& var = enm.m_variants[e.binding_idx].second; @@ -4123,22 +4151,35 @@ namespace { //} context.possible_equate_type_unsize_to(r_e.index, ty_dst); context.possible_equate_type_unsize_from(l_e.index, ty_src); - DEBUG("- Infer, add possibility"); + DEBUG("- Both infer, add possibility"); return false; } // If the source is '_', we can't know yet TU_IFLET(::HIR::TypeRef::Data, ty_src.m_data, Infer, r_e, - // TODO: If the source is a literal, and the destination isn't a TraitObject, equate. - // - Except if it's known to be a primitive - //if( r_e.ty_class != ::HIR::InferClass::None ) { - // context.equate_types(sp, ty_dst, ty_src); - // return true; - //} - context.possible_equate_type_unsize_to(r_e.index, ty_dst); - DEBUG("- Infer, add possibility"); - return false; + // No avaliable information, add a possible unsize + if( r_e.ty_class == ::HIR::InferClass::None || r_e.ty_class == ::HIR::InferClass::Diverge ) + { + // Possibility + context.possible_equate_type_unsize_to(r_e.index, ty_dst); + DEBUG("- Infer, add possibility"); + return false; + } + // Destination is infer, fall through to next TU_IFLET + else if( ty_dst.m_data.is_Infer() ) + { + } + // Destination is a TraitObject, fall through to doing an impl search + else if( ty_dst.m_data.is_TraitObject() ) + { + } + // Otherwise, they have to be equal + else + { + context.equate_types(sp, ty_dst, ty_src); + return true; + } ) TU_IFLET(::HIR::TypeRef::Data, ty_dst.m_data, Infer, l_e, @@ -4155,8 +4196,16 @@ namespace { return true; } + // If the source can't unsize, equate + if( const auto* te = ty_src.m_data.opt_Slice() ) + { + (void)te; + context.equate_types(sp, ty_dst, ty_src); + return true; + } + context.possible_equate_type_unsize_from(l_e.index, ty_src); - DEBUG("- Infer, add possibility"); + DEBUG("- Dest infer, add possibility"); return false; ) @@ -4702,12 +4751,15 @@ namespace { } // TODO: Can this can unsize as well as convert to raw? + // - It _can_ unsize, TODO: context.equate_types(sp, *l_e.inner, *s_e.inner); // Add downcast auto span = node_ptr->span(); node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Cast( mv$(span), mv$(node_ptr), ty_dst.clone() )); node_ptr->m_res_type = ty_dst.clone(); + // TODO: Add a coerce of src->&dst_inner + context.m_ivars.mark_change(); return true; ) @@ -4908,8 +4960,28 @@ namespace { else { // No equivalence added } - // - Fall through and search for the impl - DEBUG("- Unsize, no ivar equivalence"); + + // TODO: If this was a compiler-inserted bound (from a coercion rule), then do deref checks +#if 0 + { + ::HIR::TypeRef tmp_ty; + const ::HIR::TypeRef* ty_ptr = &src_ty; + while( (ty_ptr = context.m_resolve.autoderef(sp, *ty_ptr, tmp_ty)) ) + { + const auto& cur_ty = context.m_ivars.get_type(*ty_ptr); + if( cur_ty.m_data.is_Infer() ) { + break; + } + auto cmp = dst_ty.compare_with_placeholders(sp, cur_ty, context.m_ivars.callback_resolve_infer()); + if( cmp != ::HIR::Compare::Unequal ) + { + // TODO: This is a deref coercion, so what can actually be done? + TODO(sp, "Handle Unsize with deref - " << src_ty << " -> " << dst_ty); + } + } + } +#endif + DEBUG("- Unsize, no deref or ivar"); } if( v.trait == context.m_crate.get_lang_item_path(sp, "coerce_unsized") ) { @@ -4974,7 +5046,7 @@ namespace { // > This makes `let v: usize = !0;` work without special cases auto cmp2 = v.left_ty.compare_with_placeholders(sp, out_ty, context.m_ivars.callback_resolve_infer()); if( cmp2 == ::HIR::Compare::Unequal ) { - DEBUG("- (fail) known result can't match (" << context.m_ivars.fmt_type(v.left_ty) << " and " << context.m_ivars.fmt_type(out_ty) << ")"); + DEBUG("[check_associated] - (fail) known result can't match (" << context.m_ivars.fmt_type(v.left_ty) << " and " << context.m_ivars.fmt_type(out_ty) << ")"); return false; } // if solid or fuzzy, leave as-is @@ -4993,9 +5065,10 @@ namespace { } else { count += 1; - DEBUG("- (possible) " << impl); + DEBUG("[check_associated] - (possible) " << impl); if( possible_impl_ty == ::HIR::TypeRef() ) { + DEBUG("[check_associated] First - " << impl); possible_impl_ty = impl.get_impl_type(); possible_params = impl.get_trait_params(); best_impl = mv$(impl); @@ -5006,27 +5079,33 @@ namespace { // NOTE: `overlaps_with` (should be) reflective else if( impl.overlaps_with(best_impl) ) { - DEBUG("- overlaps with " << best_impl); + DEBUG("[check_associated] - Overlaps with existing - " << best_impl); + // if not more specific than the existing best, ignore. if( ! impl.more_specific_than(best_impl) ) { + DEBUG("[check_associated] - Less specific than existing"); + // NOTE: This picks the _least_ specific impl possible_impl_ty = impl.get_impl_type(); possible_params = impl.get_trait_params(); best_impl = mv$(impl); count -= 1; } + // If the existing best is not more specific than the new one, use the new one else if( ! best_impl.more_specific_than(impl) ) { - // Ignore + DEBUG("[check_associated] - More specific than existing - " << impl); count -= 1; } else { - DEBUG("> Neither is more specific. Error?"); + // Supposedly, `more_specific_than` should be reflexive... + DEBUG("[check_associated] > Neither is more specific. Error?"); } } else { // Disjoint impls. + DEBUG("[check_associated] Disjoint impl -" << impl); } #endif @@ -5602,6 +5681,31 @@ namespace { // TODO: Monomorphise this type replacing mentions of the current ivar with the replacement? +#if 0 // NOTE: The following shouldn't happen + if( bound.trait == context.m_crate.get_lang_item_path(sp, "unsize") /* && bound.is_from_coerce*/ ) + { + bool possible = false; + + ::HIR::TypeRef tmp_ty; + const ::HIR::TypeRef* ty_ptr = &new_ty; + while( (ty_ptr = context.m_resolve.autoderef(sp, *ty_ptr, tmp_ty)) ) + { + const auto& cur_ty = context.get_type(*ty_ptr); + if( cur_ty.m_data.is_Infer() ) { + break; + } + auto cmp = ty_l.compare_with_placeholders(sp, cur_ty, context.m_ivars.callback_resolve_infer()); + if( cmp != ::HIR::Compare::Unequal ) + { + possible = true; + break; + } + } + if( possible ) + continue ; + } +#endif + // Search for any trait impl that could match this, bool has = context.m_resolve.find_trait_impls(sp, bound.trait, bound.params, new_ty, [&](const auto , auto){return true;}); if( !has ) { diff --git a/src/hir_typeck/helpers.cpp b/src/hir_typeck/helpers.cpp index a1c64bec..a3fe9b1c 100644 --- a/src/hir_typeck/helpers.cpp +++ b/src/hir_typeck/helpers.cpp @@ -469,6 +469,8 @@ void HMTypeInferrence::add_ivars(::HIR::TypeRef& type) (TraitObject, // Iterate all paths this->add_ivars_params(e.m_trait.m_path.m_params); + for(auto& aty : e.m_trait.m_type_bounds) + this->add_ivars(aty.second); for(auto& marker : e.m_markers) this->add_ivars_params(marker.m_params); ), @@ -1079,25 +1081,13 @@ bool TraitResolution::find_trait_impls(const Span& sp, const auto& trait_indexmut = this->m_crate.get_lang_item_path(sp, "index_mut"); if( trait == lang_Sized ) { - TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (e), - ( - // Any unknown - it's sized - ), - (Primitive, - if( e == ::HIR::CoreType::Str ) - return false; - ), - (Slice, - return false; - ), - (Path, - // ... TODO (Search the innards or bounds) - ), - (TraitObject, + auto cmp = type_is_sized(sp, type); + if( cmp != ::HIR::Compare::Unequal ) { + return callback( ImplRef(&type, &null_params, &null_assoc), cmp ); + } + else { return false; - ) - ) - return callback( ImplRef(&type, &null_params, &null_assoc), ::HIR::Compare::Equal ); + } } if( trait == lang_Copy ) { @@ -2155,7 +2145,7 @@ bool TraitResolution::find_trait_impls_bound(const Span& sp, const ::HIR::Simple if( e.trait.m_path.m_path == trait ) { // Check against `params` - DEBUG("Checking " << params << " vs " << b_params); + DEBUG("[find_trait_impls_bound] Checking params " << params << " vs " << b_params); auto ord = cmp; ord &= this->compare_pp(sp, b_params, params); if( ord == ::HIR::Compare::Unequal ) @@ -2163,6 +2153,7 @@ bool TraitResolution::find_trait_impls_bound(const Span& sp, const ::HIR::Simple if( ord == ::HIR::Compare::Fuzzy ) { DEBUG("Fuzzy match"); } + DEBUG("[find_trait_impls_bound] Match " << b); // Hand off to the closure, and return true if it does // TODO: The type bounds are only the types that are specified. if( callback( ImplRef(&e.type, &e.trait.m_path.m_params, &e.trait.m_type_bounds), ord) ) { @@ -2814,6 +2805,23 @@ bool TraitResolution::find_trait_impls_crate(const Span& sp, ) } + for(size_t i = 0; i < impl_params_def.m_types.size(); i ++) + { + if( impl_params_def.m_types.at(i).m_is_sized ) + { + if( impl_params[i] ) { + auto cmp = type_is_sized(sp, *impl_params[i]); + if( cmp == ::HIR::Compare::Unequal ) + { + return ::HIR::Compare::Unequal; + } + } + else { + // TODO: Set match to fuzzy? + } + } + } + return match; } @@ -2897,6 +2905,66 @@ bool TraitResolution::trait_contains_type(const Span& sp, const ::HIR::GenericPa return false; } +::HIR::Compare TraitResolution::type_is_sized(const Span& sp, const ::HIR::TypeRef& type) const +{ + TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (e), + ( + // Any unknown - it's sized + ), + (Infer, + switch(e.ty_class) + { + case ::HIR::InferClass::Integer: + case ::HIR::InferClass::Float: + return ::HIR::Compare::Equal; + default: + return ::HIR::Compare::Fuzzy; + } + ), + (Primitive, + if( e == ::HIR::CoreType::Str ) + return ::HIR::Compare::Unequal; + ), + (Slice, + return ::HIR::Compare::Unequal; + ), + (Path, + // ... TODO (Search the innards or bounds) + TU_MATCHA( (e.binding), (pb), + (Unbound, + // + ), + (Opaque, + // TODO: Check bounds + ), + (Enum, + // HAS to be Sized + ), + (Union, + // Pretty sure unions are Sized + ), + (Struct, + // Possibly not sized + switch( pb->m_markings.dst_type ) + { + case ::HIR::TraitMarkings::DstType::None: + break; + case ::HIR::TraitMarkings::DstType::Possible: + // TODO: Check sized-ness of the unsized param/field + break; + case ::HIR::TraitMarkings::DstType::Slice: + case ::HIR::TraitMarkings::DstType::TraitObject: + return ::HIR::Compare::Unequal; + } + ) + ) + ), + (TraitObject, + return ::HIR::Compare::Unequal; + ) + ) + return ::HIR::Compare::Equal; +} ::HIR::Compare TraitResolution::type_is_copy(const Span& sp, const ::HIR::TypeRef& ty) const { const auto& type = this->m_ivars.get_type(ty); @@ -3284,6 +3352,12 @@ const ::HIR::TypeRef* TraitResolution::autoderef(const Span& sp, const ::HIR::Ty else { bool succ = this->find_trait_impls(sp, this->m_crate.get_lang_item_path(sp, "deref"), ::HIR::PathParams {}, ty, [&](auto impls, auto match) { tmp_type = impls.get_type("Target"); + if( tmp_type == ::HIR::TypeRef() ) + { + tmp_type = ::HIR::Path( ty.clone(), this->m_crate.get_lang_item_path(sp, "deref"), "Target" ); + tmp_type.m_data.as_Path().binding = ::HIR::TypeRef::TypePathBinding::make_Opaque({}); + } + DEBUG("Deref " << ty << " into " << tmp_type); return true; }); if( succ ) { diff --git a/src/hir_typeck/helpers.hpp b/src/hir_typeck/helpers.hpp index b7e6ca38..d715905c 100644 --- a/src/hir_typeck/helpers.hpp +++ b/src/hir_typeck/helpers.hpp @@ -265,7 +265,9 @@ public: bool trait_contains_method(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::HIR::TypeRef& self, const ::std::string& name, AllowedReceivers ar, ::HIR::GenericPath& out_path) const; 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_sized(const Span& sp, const ::HIR::TypeRef& ty) 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 { diff --git a/src/hir_typeck/static.cpp b/src/hir_typeck/static.cpp index 8baa2557..d8b692c9 100644 --- a/src/hir_typeck/static.cpp +++ b/src/hir_typeck/static.cpp @@ -109,11 +109,13 @@ bool StaticTraitResolve::find_impl( return found_cb( ImplRef(&type, &null_params, &null_assoc), false ); } } - //else if( trait_path == m_lang_Unsize ) { - // if( true ) { - // return found_cb( ImplRef(&type, &null_params, &null_assoc), false ); - // } - //} + else if( trait_path == m_lang_Unsize ) { + ASSERT_BUG(sp, trait_params, "TODO: Support no params for Unzie"); + const auto& dst_ty = trait_params->m_types.at(0); + if( this->can_unsize(sp, dst_ty, type) ) { + return found_cb( ImplRef(&type, trait_params, &null_assoc), false ); + } + } } // --- MAGIC IMPLS --- @@ -1204,6 +1206,43 @@ bool StaticTraitResolve::iterate_bounds( ::std::function<bool(const ::HIR::Gener } return false; } + + +bool StaticTraitResolve::iterate_aty_bounds(const Span& sp, const ::HIR::Path::Data::Data_UfcsKnown& pe, ::std::function<bool(const ::HIR::TraitPath&)> cb) const +{ + const auto& trait_ref = m_crate.get_trait_by_path(sp, pe.trait.m_path); + ASSERT_BUG(sp, trait_ref.m_types.count( pe.item ) != 0, "Trait " << pe.trait.m_path << " doesn't contain an associated type " << pe.item); + const auto& aty_def = trait_ref.m_types.find(pe.item)->second; + + for(const auto& bound : aty_def.m_trait_bounds) + { + if( cb(bound) ) + return true; + } + // Search `<Self as Trait>::Name` bounds on the trait itself + for(const auto& bound : trait_ref.m_params.m_bounds) + { + if( ! bound.is_TraitBound() ) continue ; + const auto& be = bound.as_TraitBound(); + + if( ! be.type.m_data.is_Path() ) continue ; + if( ! be.type.m_data.as_Path().binding.is_Opaque() ) continue ; + + const auto& be_type_pe = be.type.m_data.as_Path().path.m_data.as_UfcsKnown(); + if( *be_type_pe.type != ::HIR::TypeRef("Self", 0xFFFF) ) + continue ; + if( be_type_pe.trait.m_path != pe.trait.m_path ) + continue ; + if( be_type_pe.item != pe.item ) + continue ; + + if( cb(be.trait) ) + return true; + } + + return false; +} + // ------------------------------------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------------------------------------- @@ -1435,6 +1474,194 @@ bool StaticTraitResolve::type_is_sized(const Span& sp, const ::HIR::TypeRef& ty) throw ""; } +bool StaticTraitResolve::can_unsize(const Span& sp, const ::HIR::TypeRef& dst_ty, const ::HIR::TypeRef& src_ty) const +{ + TRACE_FUNCTION_F(dst_ty << " <- " << src_ty); + + ASSERT_BUG(sp, !dst_ty.m_data.is_Infer(), "_ seen after inferrence - " << dst_ty); + ASSERT_BUG(sp, !src_ty.m_data.is_Infer(), "_ seen after inferrence - " << src_ty); + + { + //ASSERT_BUG(sp, dst_ty != src_ty, "Equal types for can_unsize - " << dst_ty << " <-" << src_ty ); + if( dst_ty == src_ty ) + return true; + } + + { + 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 != m_lang_Unsize) + return false; + const auto& be_dst = be.trait.m_path.m_params.m_types.at(0); + + if( src_ty != be.type ) return false; + if( dst_ty != be_dst ) return false; + return true; + }); + if( found_bound ) + { + return ::HIR::Compare::Equal; + } + } + + // Associated types, check the bounds in the trait. + if( src_ty.m_data.is_Path() && src_ty.m_data.as_Path().path.m_data.is_UfcsKnown() ) + { + const auto& pe = src_ty.m_data.as_Path().path.m_data.as_UfcsKnown(); + auto monomorph_cb = monomorphise_type_get_cb(sp, &*pe.type, &pe.trait.m_params, nullptr, nullptr); + auto found_bound = this->iterate_aty_bounds(sp, pe, [&](const ::HIR::TraitPath& bound) { + if( bound.m_path.m_path != m_lang_Unsize ) + return false; + const auto& be_dst_tpl = bound.m_path.m_params.m_types.at(0); + ::HIR::TypeRef tmp_ty; + const auto& be_dst = (monomorphise_type_needed(be_dst_tpl) ? tmp_ty = monomorphise_type_with(sp, be_dst_tpl, monomorph_cb) : be_dst_tpl); + + if( dst_ty != be_dst ) return false; + return true; + }); + if( found_bound ) + { + return true; + } + } + + // 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 false; + } + 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 = dst_gp.m_params.m_types.at(str.m_markings.unsized_param); + const auto& src_inner = src_gp.m_params.m_types.at(str.m_markings.unsized_param); + return this->can_unsize(sp, dst_inner, src_inner); + } + else + { + DEBUG("Can't Unsize, destination and source are different structs"); + return false; + } + } + } + + // (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() ) + { + // 1. Data trait must be the same + 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) + { + bool found = false; + for(const auto& omt : se->m_markers) { + if( omt == mt ) { + found = true; + break; + } + } + if( !found ) { + // Return early. + return false; + } + } + + return true; + } + + bool good; + + ::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 = false; + find_impl(sp, de->m_trait.m_path.m_path, de->m_trait.m_path.m_params, src_ty, + [&](const auto impl, auto fuzz) { + //ASSERT_BUG(sp, !fuzz, "Fuzzy match in can_unsize - " << dst_ty << " <- " << src_ty << " - " << impl); + good = true; + 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 = ::HIR::TypeRef::new_path( mv$(p), {} ); + } + this->expand_associated_types(sp, atyv); + if( aty.second != atyv ) { + good = false; + DEBUG("ATY " << aty.first << " mismatch - " << aty.second << " != " << atyv); + } + } + return true; + }); + } + + // Then markers + auto cb = [&](const auto impl, auto ){ + 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 &= this->find_impl(sp, marker.m_path, marker.m_params, src_ty, cb); + } + + return good; + } + + // [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); + return *se->inner == *de->inner; + } + } + + DEBUG("Can't unsize, no rules matched"); + return ::HIR::Compare::Unequal; + +} + bool StaticTraitResolve::type_needs_drop_glue(const Span& sp, const ::HIR::TypeRef& ty) const { // If `T: Copy`, then it can't need drop glue diff --git a/src/hir_typeck/static.hpp b/src/hir_typeck/static.hpp index 16302218..4c094218 100644 --- a/src/hir_typeck/static.hpp +++ b/src/hir_typeck/static.hpp @@ -170,6 +170,7 @@ public: ) const; /// 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; + bool iterate_aty_bounds(const Span& sp, const ::HIR::Path::Data::Data_UfcsKnown& pe, ::std::function<bool(const ::HIR::TraitPath&)> cb) const; // -------------- @@ -177,6 +178,7 @@ public: // ------------- bool type_is_copy(const Span& sp, const ::HIR::TypeRef& ty) const; bool type_is_sized(const Span& sp, const ::HIR::TypeRef& ty) const; + bool can_unsize(const Span& sp, const ::HIR::TypeRef& dst, const ::HIR::TypeRef& src) const; /// Returns `true` if the passed type either implements Drop, or contains a type that implements Drop bool type_needs_drop_glue(const Span& sp, const ::HIR::TypeRef& ty) const; |