diff options
-rw-r--r-- | src/hir_typeck/expr_cs.cpp | 75 | ||||
-rw-r--r-- | src/hir_typeck/helpers.cpp | 51 |
2 files changed, 116 insertions, 10 deletions
diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp index af42bc07..6ced24c8 100644 --- a/src/hir_typeck/expr_cs.cpp +++ b/src/hir_typeck/expr_cs.cpp @@ -3413,15 +3413,16 @@ namespace { ::HIR::PathParams pp; pp.m_types.push_back( ty_dst.clone() ); - // PROBLEM: In the desugaring of `box`, it's required that the container type propagate backwards through the coercion point - // - However, there's still a coercion point that needs to apply to the contained type. - // - `let foo: Box<[i32]> = box [1, 2, 3];` - // - Special-casing `Box` here could work (assuming that Box can only coerce to Box, and applying unsizing coercion rules to the inner) + // PROBLEM: This can false-negative leading to the types being falsely equated. - //bool fuzzy_match = false; + bool fuzzy_match = false; bool found = context.m_resolve.find_trait_impls(sp, lang_CoerceUnsized, pp, ty_src, [&](auto impl, auto cmp) { - // TODO: Allow fuzzy match if it's the only matching possibility DEBUG("[check_coerce] cmp=" << cmp << ", impl=" << impl); + // TODO: Allow fuzzy match if it's the only matching possibility? + // - Recorded for now to know if a later pass is a good idea + if( cmp == ::HIR::Compare::Fuzzy ) { + fuzzy_match = true; + } return cmp == ::HIR::Compare::Equal; }); if( found ) @@ -3432,12 +3433,74 @@ namespace { node_ptr = NEWNODE( ty_dst.clone(), span, _Unsize, mv$(node_ptr), ty_dst.clone() ); return true; } + if( fuzzy_match ) + { + const auto& span = sp; + // TODO: Apply ivar possibilities between the parameters in `ty_src` and `ty_dst` + // - Ideally, this equivalence would be done using the parameter known to be the pointer target + if( ty_dst.m_data.is_Path() && ty_src.m_data.is_Path() ) + { + const auto& dp = ty_dst.m_data.as_Path().path; + const auto& sp = ty_src.m_data.as_Path().path; + if( dp.m_data.is_Generic() && sp.m_data.is_Generic() ) + { + const auto& dpe = dp.m_data.as_Generic(); + const auto& spe = sp.m_data.as_Generic(); + if( dpe.m_path == spe.m_path && dpe.m_params.m_types.size() > 0 ) + { + ASSERT_BUG(node_ptr->span(), dpe.m_params.m_types.size() == spe.m_params.m_types.size(), + "Mismatch in type param count - `" << dpe.m_params << "` and `" << spe.m_params << "`"); + for(unsigned int i = 0; i < dpe.m_params.m_types.size(); i ++) + { + const auto& dt2 = context.get_type(dpe.m_params.m_types[i]); + const auto& st2 = context.get_type(spe.m_params.m_types[i]); + + // Ideally, this would share code with other sections + TU_IFLET( ::HIR::TypeRef::Data, st2.m_data, Infer, se, + // TODO: Update for InferClass::Diverge ? + if( se.ty_class != ::HIR::InferClass::None ) { + context.equate_types(span, dt2, st2); + } + else { + TU_IFLET(::HIR::TypeRef::Data, dt2.m_data, Infer, de, + context.possible_equate_type_to(se.index, dt2); + context.possible_equate_type_from(de.index, st2); + ) + else { + context.possible_equate_type_to(se.index, dt2); + } + } + ) + else TU_IFLET(::HIR::TypeRef::Data, dt2.m_data, Infer, de, + // TODO: Update for InferClass::Diverge ? + if( de.ty_class != ::HIR::InferClass::None ) { + context.equate_types(span, dt2, st2); + } + else { + context.possible_equate_type_from(de.index, st2); + } + ) + else { + // No equivalence added + } + } + } + } + } + #if 1 + DEBUG("- CoerceUnsize possible, trying later"); + return false; + #else + DEBUG("- CoerceUnsize possible?"); + #endif + } } // 1. Check that the source type can coerce TU_MATCH( ::HIR::TypeRef::Data, (ty_src.m_data), (e), (Infer, // If this ivar is of a primitive, equate (as primitives never coerce) + // TODO: InferClass::Diverge if( e.ty_class != ::HIR::InferClass::None ) { context.equate_types(sp, ty_dst, ty_src); return true; diff --git a/src/hir_typeck/helpers.cpp b/src/hir_typeck/helpers.cpp index b35d6438..bc020ef5 100644 --- a/src/hir_typeck/helpers.cpp +++ b/src/hir_typeck/helpers.cpp @@ -964,6 +964,27 @@ bool TraitResolution::find_trait_impls(const Span& sp, return false; } } + + // Magical CoerceUnsized impls for various types + if( trait == this->m_crate.get_lang_item_path(sp, "coerce_unsized") ) { + const auto& dst_ty = params.m_types.at(0); + // - `*mut T => *const T` + TU_IFLET( ::HIR::TypeRef::Data, type.m_data, Pointer, e, + TU_IFLET( ::HIR::TypeRef::Data, dst_ty.m_data, Pointer, de, + if( de.type < e.type ) { + auto cmp = e.inner->compare_with_placeholders(sp, *de.inner, this->m_ivars.callback_resolve_infer()); + if( cmp != ::HIR::Compare::Unequal ) + { + ::HIR::PathParams pp; + pp.m_types.push_back( dst_ty.clone() ); + if( callback( ImplRef(type.clone(), mv$(pp), {}), cmp ) ) { + return true; + } + } + } + ) + ) + } const auto& trait_fn = this->m_crate.get_lang_item_path(sp, "fn"); const auto& trait_fn_mut = this->m_crate.get_lang_item_path(sp, "fn_mut"); @@ -1943,7 +1964,7 @@ bool TraitResolution::find_trait_impls_crate(const Span& sp, (TypeLifetime, ), (TraitBound, - DEBUG("Check bound " << be.type << " : " << be.trait); + DEBUG("[find_trait_impls_crate] Check bound " << be.type << " : " << be.trait); auto real_type = monomorphise_type_with(sp, be.type, monomorph, false); auto real_trait = monomorphise_traitpath_with(sp, be.trait, monomorph, false); real_type = this->expand_associated_types(sp, mv$(real_type)); @@ -1954,8 +1975,10 @@ bool TraitResolution::find_trait_impls_crate(const Span& sp, ab.second = this->expand_associated_types(sp, mv$(ab.second)); } const auto& real_trait_path = real_trait.m_path; - DEBUG("- " << real_type << " : " << real_trait); + DEBUG("[find_trait_impls_crate] - bound mono " << real_type << " : " << real_trait); + bool found_fuzzy_match = false; auto rv = this->find_trait_impls(sp, real_trait_path.m_path, real_trait_path.m_params, real_type, [&](auto impl, auto impl_cmp) { + auto cmp = impl_cmp; for(const auto& assoc_bound : real_trait.m_type_bounds) { ::HIR::TypeRef tmp; const ::HIR::TypeRef* ty_p; @@ -1986,12 +2009,32 @@ bool TraitResolution::find_trait_impls_crate(const Span& sp, // TODO: When a fuzzy match is encountered on a conditional bound, returning `false` can lead to an false negative (and a compile error) // BUT, returning `true` could lead to it being selected. (Is this a problem, should a later validation pass check?) DEBUG("[find_trait_impls_crate] Fuzzy match assoc bound between " << ty << " and " << assoc_bound.second); + cmp = ::HIR::Compare::Fuzzy; continue ; } } - return true; + DEBUG("impl_cmp = " << impl_cmp << ", cmp = " << cmp); + if( cmp == ::HIR::Compare::Fuzzy ) { + found_fuzzy_match = true; + } + // If the match isn't a concrete equal, return false (to keep searching) + return (cmp == ::HIR::Compare::Equal); }); - if( !rv ) { + if( rv ) { + DEBUG("- Bound " << real_type << " : " << real_trait_path << " matched"); + } + else if( found_fuzzy_match ) { + DEBUG("- Bound " << real_type << " : " << real_trait_path << " fuzzed"); + match = ::HIR::Compare::Fuzzy; + } + #if 1 + // TODO: This causes typeck errors in libcore + else if( real_type.m_data.is_Infer() && real_type.m_data.as_Infer().ty_class == ::HIR::InferClass::None ) { + DEBUG("- Bound " << real_type << " : " << real_trait_path << " full infer type - make result fuzzy"); + match = ::HIR::Compare::Fuzzy; + } + #endif + else { DEBUG("- Bound " << real_type << " : " << real_trait_path << " failed"); return false; } |