summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Hodge <tpg@ucc.asn.au>2017-08-18 09:39:52 +0800
committerJohn Hodge <tpg@ucc.asn.au>2017-08-18 09:39:52 +0800
commit7ee9fc53ab6caa32261496cb76eebda40fbfe000 (patch)
tree45eaa56967f93259fd4146fc37f6077c272aa8d5 /src
parent6498c9b315cda96114072a0fdb3dc86f8b1232fe (diff)
downloadmrust-7ee9fc53ab6caa32261496cb76eebda40fbfe000.tar.gz
HIR Typecheck - Refactor coerce/unsize handling
Diffstat (limited to 'src')
-rw-r--r--src/hir/type.hpp14
-rw-r--r--src/hir_typeck/expr_cs.cpp1132
-rw-r--r--src/hir_typeck/helpers.cpp5
-rw-r--r--src/include/tagged_union.hpp4
4 files changed, 584 insertions, 571 deletions
diff --git a/src/hir/type.hpp b/src/hir/type.hpp
index cc0894d2..e3aeec43 100644
--- a/src/hir/type.hpp
+++ b/src/hir/type.hpp
@@ -127,6 +127,20 @@ public:
(Infer, struct {
unsigned int index;
InferClass ty_class;
+
+ /// Returns true if the ivar is a literal
+ bool is_lit() const {
+ switch(this->ty_class)
+ {
+ case InferClass::None:
+ case InferClass::Diverge:
+ return false;
+ case InferClass::Integer:
+ case InferClass::Float:
+ return true;
+ }
+ throw "";
+ }
}),
(Diverge, struct {}),
(Primitive, ::HIR::CoreType),
diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp
index 5303c432..cd86ca2e 100644
--- a/src/hir_typeck/expr_cs.cpp
+++ b/src/hir_typeck/expr_cs.cpp
@@ -4090,121 +4090,120 @@ namespace {
context.m_ivars.mark_change();
}
- bool check_coerce_borrow(Context& context, ::HIR::BorrowType bt, const ::HIR::TypeRef& inner_l, const ::HIR::TypeRef& inner_r, ::HIR::ExprNodeP& node_ptr)
- {
- const auto& sp = node_ptr->span();
+ enum CoerceResult {
+ Unknown, // Coercion still unknown.
+ Equality, // Types should be equated
+ Custom, // An op was emitted, and rule is complete
+ Unsize, // Emits an _Unsize op
+ };
- const auto& ty_dst = context.m_ivars.get_type(inner_l);
- const auto& ty_src = context.m_ivars.get_type(inner_r);
+ // TODO: Add a (two?) callback(s) that handle type equalities (and possible equalities) so this function doesn't have to mutate the context
+ CoerceResult check_unsize_tys(Context& context, const Span& sp, const ::HIR::TypeRef& dst_raw, const ::HIR::TypeRef& src_raw, ::HIR::ExprNodeP* node_ptr_ptr=nullptr)
+ {
+ const auto& dst = context.m_ivars.get_type(dst_raw);
+ const auto& src = context.m_ivars.get_type(src_raw);
+ TRACE_FUNCTION_F("dst=" << dst << ", src=" << src);
// If the types are already equal, no operation is required
- if( context.m_ivars.types_equal(ty_dst, ty_src) ) {
- return true;
+ if( context.m_ivars.types_equal(dst, src) ) {
+ DEBUG("Equal");
+ return CoerceResult::Equality;
}
-
- // If either side (or both) are ivars, then coercion can't be known yet - but they could be equal
- // TODO: Fix and deduplicate the following code for InferClass::Diverge
- if( ty_src.m_data.is_Infer() && ty_dst.m_data.is_Infer() ) {
- const auto& r_e = ty_src.m_data.as_Infer();
- const auto& l_e = ty_dst.m_data.as_Infer();
-
- // TODO: Commented out - &-ptrs can infer to trait objects, and &-ptrs can infer from deref coercion
- //if( r_e.ty_class != ::HIR::InferClass::None ) {
- // context.equate_types(sp, ty_dst, ty_src);
- // return true;
- //}
- //if( l_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);
- context.possible_equate_type_unsize_from(l_e.index, ty_src);
- DEBUG("- Both infer, add possibility");
- return false;
+ // Impossibilities
+ if( src.m_data.is_Slice() )
+ {
+ // [T] can't unsize to anything
+ DEBUG("Slice can't unsize");
+ return CoerceResult::Equality;
}
- // If the source is '_', we can't know yet
- TU_IFLET(::HIR::TypeRef::Data, ty_src.m_data, Infer, r_e,
-
- // No avaliable information, add a possible unsize
- if( r_e.ty_class == ::HIR::InferClass::None || r_e.ty_class == ::HIR::InferClass::Diverge )
+ // Handle ivars specially
+ if(dst.m_data.is_Infer() && src.m_data.is_Infer())
+ {
+ // If both are literals, equate
+ if( dst.m_data.as_Infer().is_lit() && src.m_data.as_Infer().is_lit() )
{
- // Possibility
- context.possible_equate_type_unsize_to(r_e.index, ty_dst);
- DEBUG("- Infer, add possibility");
- return false;
+ DEBUG("Literal ivars");
+ return CoerceResult::Equality;
}
- // Destination is infer, fall through to next TU_IFLET
- else if( ty_dst.m_data.is_Infer() )
+ context.possible_equate_type_coerce_to(src.m_data.as_Infer().index, dst);
+ context.possible_equate_type_coerce_from(dst.m_data.as_Infer().index, src);
+ DEBUG("Both ivars");
+ return CoerceResult::Unknown;
+ }
+ else if(const auto* dep = dst.m_data.opt_Infer())
+ {
+ // Literal from a primtive has to be equal
+ if( dep->is_lit() && src.m_data.is_Primitive() )
+ {
+ DEBUG("Literal with primitive");
+ return CoerceResult::Equality;
+ }
+ context.possible_equate_type_coerce_from(dep->index, src);
+ DEBUG("Dst ivar");
+ return CoerceResult::Unknown;
+ }
+ else if(const auto* sep = src.m_data.opt_Infer())
+ {
+ if(sep->is_lit() && !dst.m_data.is_TraitObject())
{
+ // Literal to anything other than a trait object must be an equality
+ DEBUG("Literal with primitive");
+ return CoerceResult::Equality;
}
- // Destination is a TraitObject, fall through to doing an impl search
- else if( ty_dst.m_data.is_TraitObject() )
+ context.possible_equate_type_coerce_to(sep->index, dst);
+ DEBUG("Src ivar");
+ return CoerceResult::Unknown;
+ }
+ else
+ {
+ // Neither side is an ivar, keep going.
+ }
+
+ // Array unsize (quicker than going into deref search)
+ if(dst.m_data.is_Slice() && src.m_data.is_Array())
+ {
+ context.equate_types(sp, *dst.m_data.as_Slice().inner, *src.m_data.as_Array().inner);
+ if(node_ptr_ptr)
{
+ // TODO: Insert deref (instead of leading to a _Unsize op)
}
- // Otherwise, they have to be equal
else
{
- context.equate_types(sp, ty_dst, ty_src);
- return true;
+ // Just return Unsize
}
- )
-
- TU_IFLET(::HIR::TypeRef::Data, ty_dst.m_data, Infer, l_e,
+ DEBUG("Array => Slice");
+ return CoerceResult::Unsize;
+ }
- // If the destination is known to be a primitive (integer or float) and the source is a primitive
- // - Equate.
- // - NOTE: The source can't be something that could deref coerce into the literal.
- if( l_e.ty_class == ::HIR::InferClass::Integer && ty_src.m_data.is_Primitive() ) {
- context.equate_types(sp, ty_dst, ty_src);
- return true;
+ // Shortcut: Types that can't deref coerce (and can't coerce here because the target isn't a TraitObject)
+ if( ! dst.m_data.is_TraitObject() )
+ {
+ if( src.m_data.is_Generic() )
+ {
}
- if( l_e.ty_class == ::HIR::InferClass::Float && ty_src.m_data.is_Primitive() ) {
- context.equate_types(sp, ty_dst, ty_src);
- return true;
+ else if( src.m_data.is_Path() )
+ {
}
-
- // If the source can't unsize, equate
- if( const auto* te = ty_src.m_data.opt_Slice() )
+ else if( src.m_data.is_Borrow() )
{
- (void)te;
- context.equate_types(sp, ty_dst, ty_src);
- return true;
}
-
- context.possible_equate_type_unsize_from(l_e.index, ty_src);
- DEBUG("- Dest infer, add possibility");
- return false;
- )
-
- // Fast hack for slices (avoids going via the Deref impl search)
- if( ty_dst.m_data.is_Slice() && !ty_src.m_data.is_Slice() )
- {
- const auto& dst_slice = ty_dst.m_data.as_Slice();
- TU_IFLET(::HIR::TypeRef::Data, ty_src.m_data, Array, src_array,
- context.equate_types(sp, *dst_slice.inner, *src_array.inner);
-
- auto ty_dst_b = ::HIR::TypeRef::new_borrow(bt, ty_dst.clone());
- auto ty_dst_b2 = ty_dst_b.clone();
- auto span = node_ptr->span();
- node_ptr = NEWNODE( mv$(ty_dst_b), span, _Unsize, mv$(node_ptr), mv$(ty_dst_b2) );
-
- context.m_ivars.mark_change();
- return true;
- )
else
{
- // Apply deref coercions
+ DEBUG("Target isn't a trait object, and sources can't Deref");
+ return CoerceResult::Equality;
}
}
// Deref coercions
// - If right can be dereferenced to left
- DEBUG("-- Deref coercions");
+ if(node_ptr_ptr)
{
+ DEBUG("-- Deref coercions");
+ auto& node_ptr = *node_ptr_ptr;
::HIR::TypeRef tmp_ty;
- const ::HIR::TypeRef* out_ty_p = &ty_src;
+ const ::HIR::TypeRef* out_ty_p = &src;
unsigned int count = 0;
::std::vector< ::HIR::TypeRef> types;
while( (out_ty_p = context.m_resolve.autoderef(sp, *out_ty_p, tmp_ty)) )
@@ -4219,22 +4218,25 @@ namespace {
types.push_back( out_ty.clone() );
- if( context.m_ivars.types_equal(ty_dst, out_ty) == false ) {
- // Check equivalence
-
- if( ty_dst.m_data.tag() == out_ty.m_data.tag() ) {
- TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty_dst.m_data, out_ty.m_data), (d_e, s_e),
+ // Types aren't equal
+ if( context.m_ivars.types_equal(dst, out_ty) == false )
+ {
+ // Check if they can be considered equivalent.
+ // - E.g. a fuzzy match, or both are slices/arrays
+ if( dst.m_data.tag() == out_ty.m_data.tag() )
+ {
+ TU_MATCH_DEF( ::HIR::TypeRef::Data, (dst.m_data, out_ty.m_data), (d_e, s_e),
(
- if( ty_dst .compare_with_placeholders(sp, out_ty, context.m_ivars.callback_resolve_infer()) == ::HIR::Compare::Unequal ) {
+ if( dst .compare_with_placeholders(sp, out_ty, context.m_ivars.callback_resolve_infer()) == ::HIR::Compare::Unequal ) {
DEBUG("Same tag, but not fuzzy match");
continue ;
}
- DEBUG("Same tag and fuzzy match - assuming " << ty_dst << " == " << out_ty);
- context.equate_types(sp, ty_dst, out_ty);
+ DEBUG("Same tag and fuzzy match - assuming " << dst << " == " << out_ty);
+ context.equate_types(sp, dst, out_ty);
),
(Slice,
// Equate!
- context.equate_types(sp, ty_dst, out_ty);
+ context.equate_types(sp, dst, out_ty);
// - Fall through
)
)
@@ -4260,243 +4262,282 @@ namespace {
}
});
- return true;
+ return CoerceResult::Custom;
}
// Either ran out of deref, or hit a _
}
- // Desination coercions (Trait objects)
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty_dst.m_data), (e),
- (
- ),
- (TraitObject,
- const auto& trait = e.m_trait.m_path;
- ImplRef best_impl;
- unsigned int count = 0;
- // Check for trait impl
- bool found = context.m_resolve.find_trait_impls(sp, trait.m_path, trait.m_params, ty_src, [&](auto impl, auto cmp) {
- DEBUG("TraitObject coerce from - cmp="<<cmp<<", " << impl);
- count ++;
- best_impl = mv$(impl);
- return cmp == ::HIR::Compare::Equal;
- });
- if( count == 0 ) {
- // TODO: Get a better idea of when there won't ever be an applicable impl
- if( !context.m_ivars.type_contains_ivars(ty_src) ) {
- ERROR(sp, E0000, "The trait " << e.m_trait << " is not implemented for " << ty_src);
- }
- DEBUG("No impl, but there may eventaully be one");
- return false;
- }
- if( !found )
+ // Trait objects
+ if(const auto* dep = dst.m_data.opt_TraitObject())
+ {
+ if(const auto* sep = src.m_data.opt_TraitObject())
{
- if(count > 1)
- {
- DEBUG("Defer as there are multiple applicable impls");
- return false;
- }
+ DEBUG("TraitObject => TraitObject");
+ // Ensure that the trait list in the destination is a strict subset of the source
- if( best_impl.has_magic_params() ) {
- DEBUG("Defer as there were magic parameters");
- return false;
+ // TODO: Equate these two trait paths
+ if( dep->m_trait.m_path.m_path != sep->m_trait.m_path.m_path )
+ {
+ // Trait mismatch!
+ return CoerceResult::Equality;
}
-
- // TODO: Get a better way of equating these that doesn't require getting copies of the impl's types
- context.equate_types(sp, ty_src, best_impl.get_impl_type());
- auto args = best_impl.get_trait_params();
- assert(trait.m_params.m_types.size() == args.m_types.size());
- for(unsigned int i = 0; i < trait.m_params.m_types.size(); i ++)
+ const auto& tys_d = dep->m_trait.m_path.m_params.m_types;
+ const auto& tys_s = sep->m_trait.m_path.m_params.m_types;
+ for(size_t i = 0; i < tys_d.size(); i ++)
{
- context.equate_types(sp, trait.m_params.m_types[i], args.m_types[i]);
+ context.equate_types(sp, tys_d[i], tys_s.at(i));
}
- }
- for(const auto& marker : e.m_markers)
- {
- bool found = context.m_resolve.find_trait_impls(sp, marker.m_path, marker.m_params, ty_src, [&](auto impl, auto cmp) {
- DEBUG("TraitObject coerce from - cmp="<<cmp<<", " << impl);
- return cmp == ::HIR::Compare::Equal;
- });
- // TODO: Allow fuzz and equate same as above?
- if( !found ) {
- // TODO: Get a better idea of when there won't ever be an applicable impl
- if( !context.m_ivars.type_contains_ivars(ty_src) ) {
- ERROR(sp, E0000, "The trait " << marker << " is not implemented for " << ty_src);
+ // 2. Destination markers must be a strict subset
+ for(const auto& mt : dep->m_markers)
+ {
+ // TODO: Fuzzy match
+ bool found = false;
+ for(const auto& omt : sep->m_markers) {
+ if( omt == mt ) {
+ found = true;
+ break;
+ }
+ }
+ if( !found ) {
+ // Return early.
+ return CoerceResult::Equality;
}
- return false;
}
+
+ return CoerceResult::Unsize;
}
+ else
+ {
+ const auto& trait = dep->m_trait.m_path;
- // Add _Unsize operator
- auto ty_dst_b = ::HIR::TypeRef::new_borrow(bt, ty_dst.clone());
- auto ty_dst_b2 = ty_dst_b.clone();
- auto span = node_ptr->span();
- node_ptr = NEWNODE( mv$(ty_dst_b), span, _Unsize, mv$(node_ptr), mv$(ty_dst_b2) );
+ ImplRef best_impl;
+ unsigned int count = 0;
+ // Check for trait impl
+ if( trait.m_path != ::HIR::SimplePath() )
+ {
+ bool found = context.m_resolve.find_trait_impls(sp, trait.m_path, trait.m_params, src, [&](auto impl, auto cmp) {
+ DEBUG("TraitObject coerce from - cmp="<<cmp<<", " << impl);
+ count ++;
+ best_impl = mv$(impl);
+ return cmp == ::HIR::Compare::Equal;
+ });
+ if( count == 0 )
+ {
+ // TODO: Get a better idea of when there won't ever be an applicable impl
+ if( !context.m_ivars.type_contains_ivars(src) ) {
+ ERROR(sp, E0000, "The trait " << dep->m_trait << " is not implemented for " << src);
+ }
+ DEBUG("No impl, but there may eventaully be one");
+ return CoerceResult::Unknown;
+ }
+ if( !found )
+ {
+ if(count > 1)
+ {
+ DEBUG("Defer as there are multiple applicable impls");
+ return CoerceResult::Unknown;
+ }
- return true;
- )
- )
+ if( best_impl.has_magic_params() ) {
+ DEBUG("Defer as there were magic parameters");
+ return CoerceResult::Unknown;
+ }
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty_src.m_data), (e),
- (
- ),
- (Slice,
- // NOTE: These can't even coerce to a TraitObject because of pointer size problems
- context.equate_types(sp, ty_dst, ty_src);
- return true;
- // ),
- //(TraitObject,
- // // TODO: Could a trait object coerce and lose a trait?
- // context.equate_types(sp, ty_dst, ty_src);
- // return true;
- )
- )
+ // TODO: Get a better way of equating these that doesn't require getting copies of the impl's types
+ context.equate_types(sp, src, best_impl.get_impl_type());
+ auto args = best_impl.get_trait_params();
+ assert(trait.m_params.m_types.size() == args.m_types.size());
+ for(unsigned int i = 0; i < trait.m_params.m_types.size(); i ++)
+ {
+ context.equate_types(sp, trait.m_params.m_types[i], args.m_types[i]);
+ }
+ }
+ }
- // Search for Unsize
- // - If `right`: ::core::marker::Unsize<`left`>
- DEBUG("-- Unsize trait");
- {
- auto cmp = context.m_resolve.can_unsize(sp, ty_dst, ty_src, [&](auto new_dst) {
- // Equate these two types
- });
- 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();
- auto span = node_ptr->span();
- node_ptr = NEWNODE( mv$(ty_dst_b), span, _Unsize, mv$(node_ptr), mv$(ty_dst_b2) );
+ for(const auto& marker : dep->m_markers)
+ {
+ bool found = context.m_resolve.find_trait_impls(sp, marker.m_path, marker.m_params, src, [&](auto impl, auto cmp) {
+ DEBUG("TraitObject coerce from - cmp="<<cmp<<", " << impl);
+ return cmp == ::HIR::Compare::Equal;
+ });
+ // TODO: Allow fuzz and equate same as above?
+ if( !found )
+ {
+ // TODO: Get a better idea of when there won't ever be an applicable impl
+ if( !context.m_ivars.type_contains_ivars(src) ) {
+ ERROR(sp, E0000, "The trait " << marker << " is not implemented for " << src);
+ }
+ return CoerceResult::Unknown;
+ }
+ }
- return true;
+ // Add _Unsize operator
+ return CoerceResult::Unsize;
}
- if(cmp == ::HIR::Compare::Unequal)
+ }
+
+ // Find an Unsize impl?
+ struct H {
+ static bool type_is_bounded(const ::HIR::TypeRef& ty)
{
- // 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() )
- {
+ if( ty.m_data.is_Generic() ) {
+ return true;
}
- 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);
+ else if( TU_TEST1(ty.m_data, Path, .binding.is_Opaque()) ) {
return true;
}
+ else {
+ return false;
+ }
}
- if(cmp == ::HIR::Compare::Fuzzy)
+ };
+ if( H::type_is_bounded(src) )
+ {
+ const auto& lang_Unsize = context.m_crate.get_lang_item_path(sp, "unsize");
+
+ ::HIR::PathParams pp { dst.clone() };
+ bool found = context.m_resolve.find_trait_impls(sp, lang_Unsize, pp, src, [](auto , auto){ return true; });
+ if( found )
{
- // Not sure yet
- return false;
+ return CoerceResult::Unsize;
+ }
+ else
+ {
+ // TODO: Fuzzy?
+ //this->context.equate_types(sp, *e.inner, *s_e.inner);
}
}
- // Keep trying
- // TODO: If both types are fully known, then error.
- return false;
- }
- bool check_coerce(Context& context, const Context::Coercion& v)
- {
- ::HIR::ExprNodeP& node_ptr = *v.right_node_ptr;
- const auto& sp = node_ptr->span();
- const auto& ty_dst = context.m_ivars.get_type(v.left_ty);
- const auto& ty_src = context.m_ivars.get_type(node_ptr->m_res_type);
- TRACE_FUNCTION_F(v << " - " << ty_dst << " := " << ty_src);
-
- if( context.m_ivars.types_equal(ty_dst, ty_src) ) {
- DEBUG("Equal");
- return true;
- }
-
- const auto& lang_CoerceUnsized = context.m_crate.get_lang_item_path(sp, "coerce_unsized");
-
- struct H {
- // Check if a path type has or could have a CoerceUnsized impl
- static bool type_has_coerce_path(const Context& context, const ::HIR::TypeRef& ty, bool inner=false) {
- TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Path, e,
- TU_MATCHA( (e.binding), (pbe),
+ // Path types
+ if( src.m_data.tag() == dst.m_data.tag() )
+ {
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (src.m_data, dst.m_data), (se, de),
+ (
+ return CoerceResult::Equality;
+ ),
+ (Path,
+ if( se.binding.tag() == de.binding.tag() )
+ {
+ TU_MATCHA( (se.binding, de.binding), (sbe, dbe),
(Unbound,
+ // Don't care
),
(Opaque,
- // Assume true (could store something in the generic block)
- return true;
+ // Handled above in bounded
),
- (Struct,
- //return pbe->m_struct_markings.coerce_unsized_index != ~0u;
- switch( pbe->m_struct_markings.coerce_unsized )
+ (Enum,
+ // Must be equal
+ if( sbe == dbe )
{
- case ::HIR::StructMarkings::Coerce::None:
- return false;
- case ::HIR::StructMarkings::Coerce::Pointer:
- // If this contains a pointer (or something that acts like one), then assume it always coerces
- return true;
- case ::HIR::StructMarkings::Coerce::Passthrough:
- // TODO: If this generic over a CoerceUnsized type, then check if that type is CoerceUnsized
- return type_has_coerce_path( context, context.m_ivars.get_type(e.path.m_data.as_Generic().m_params.m_types.at( pbe->m_struct_markings.coerce_param)), true );
+ return CoerceResult::Equality;
}
),
(Union,
+ if( sbe == dbe )
+ {
+ // Must be equal
+ return CoerceResult::Equality;
+ }
),
- (Enum,
+ (Struct,
+ if( sbe == dbe )
+ {
+ const auto& sm = sbe->m_struct_markings;
+ if( sm.dst_type == ::HIR::StructMarkings::DstType::Possible )
+ {
+ const auto& isrc = se.path.m_data.as_Generic().m_params.m_types.at(sm.unsized_param);
+ const auto& idst = de.path.m_data.as_Generic().m_params.m_types.at(sm.unsized_param);
+ return check_unsize_tys(context, sp, isrc, idst, nullptr);
+ }
+ else
+ {
+ // Must be equal
+ return CoerceResult::Equality;
+ }
+ }
)
)
+ }
)
- else if( inner ) {
- if( ty.m_data.is_Pointer() || ty.m_data.is_Borrow() ) {
- return true;
- }
- else if( ty.m_data.is_Infer() ) {
- return true;
- }
+ )
+ }
+
+ DEBUG("Reached end of check_unsize_tys, return Unknown");
+ // TODO: Determine if this unsizing could ever happen.
+ return CoerceResult::Unknown;
+ }
+
+ /// Checks if two types can be a valid coercion
+ //
+ // General rules:
+ // - CoerceUnsized generics/associated types can only involve generics/associated types
+ // - CoerceUnsized structs only go between themselves (and either recurse or unsize a parameter)
+ // - No other path can implement CoerceUnsized
+ // - Pointers do unsizing (and maybe casting)
+ // - All other types equate
+ CoerceResult check_coerce_tys(Context& context, const Span& sp, const ::HIR::TypeRef& dst, const ::HIR::TypeRef& src, ::HIR::ExprNodeP* node_ptr_ptr=nullptr)
+ {
+ TRACE_FUNCTION_F(dst << " := " << src);
+ // If the types are equal, then return equality
+ if( context.m_ivars.types_equal(dst, src) ) {
+ return CoerceResult::Equality;
+ }
+ // If either side is a literal, then can't Coerce
+ if( TU_TEST1(dst.m_data, Infer, .is_lit()) ) {
+ return CoerceResult::Equality;
+ }
+ if( TU_TEST1(src.m_data, Infer, .is_lit()) ) {
+ return CoerceResult::Equality;
+ }
+ // If both sides are `_`, then can't know about coerce yet
+ if( dst.m_data.is_Infer() && src.m_data.is_Infer() ) {
+ // Add possibilities both ways
+ context.possible_equate_type_coerce_to(src.m_data.as_Infer().index, dst);
+ context.possible_equate_type_coerce_from(dst.m_data.as_Infer().index, src);
+ return CoerceResult::Unknown;
+ }
+
+ struct H {
+ static bool type_is_bounded(const ::HIR::TypeRef& ty)
+ {
+ if( ty.m_data.is_Generic() ) {
+ return true;
+ }
+ else if( TU_TEST1(ty.m_data, Path, .binding.is_Opaque()) ) {
+ return true;
+ }
+ else {
+ return false;
}
- return false;
}
-
static ::HIR::TypeRef make_pruned(Context& context, const ::HIR::TypeRef& ty)
{
const auto& binding = ty.m_data.as_Path().binding;
const auto& sm = binding.as_Struct()->m_struct_markings;
::HIR::GenericPath gp = ty.m_data.as_Path().path.m_data.as_Generic().clone();
- if( sm.coerce_param == ~0u ) {
- for(size_t i = 0; i < gp.m_params.m_types.size(); i ++ )
- gp.m_params.m_types.at(i) = context.m_ivars.new_ivar_tr();
- }
- else {
- gp.m_params.m_types.at(sm.coerce_param) = context.m_ivars.new_ivar_tr();
- }
+ assert(sm.coerce_param != ~0u);
+ gp.m_params.m_types.at(sm.coerce_param) = context.m_ivars.new_ivar_tr();
return ::HIR::TypeRef::new_path(mv$(gp), binding.as_Struct());
}
};
-
- // Coercing to a CoerceUnsized type can only happen from something of the same generic type
- // - Create a copy of the known type, but with the `Coerce` parameter set to a new ivar
- if( ty_src.m_data.is_Infer() && H::type_has_coerce_path(context, ty_dst) && ty_dst.m_data.as_Path().binding.is_Struct() )
- {
- DEBUG("Create ivar filled version of " << ty_dst);
- context.equate_types(sp, ty_src, H::make_pruned(context, ty_dst));
- return false;
- }
- if( ty_dst.m_data.is_Infer() && H::type_has_coerce_path(context, ty_src) && ty_src.m_data.as_Path().binding.is_Struct() )
- {
- DEBUG("Create ivar filled version of " << ty_src);
- context.equate_types(sp, ty_dst, H::make_pruned(context, ty_src));
- return false;
- }
-
- // CoerceUnsized trait
- // - Only valid for generic or path destination types
- if( ty_dst.m_data.is_Generic() || H::type_has_coerce_path(context, ty_dst) )
+ // A CoerceUnsized generic/aty/erased on one side
+ // - If other side is an ivar, do a possible equality and return Unknown
+ // - If impl is found, emit _Unsize
+ // - Else, equate and return
+ // TODO: Should ErasedType be counted here? probably not.
+ if( H::type_is_bounded(src) || H::type_is_bounded(dst) )
{
+ const auto& lang_CoerceUnsized = context.m_crate.get_lang_item_path(sp, "coerce_unsized");
// `CoerceUnsized<U> for T` means `T -> U`
- ::HIR::PathParams pp { ty_dst.clone() };
+ ::HIR::PathParams pp { dst.clone() };
// PROBLEM: This can false-negative leading to the types being falsely equated.
bool fuzzy_match = false;
ImplRef best_impl;
- bool found = context.m_resolve.find_trait_impls(sp, lang_CoerceUnsized, pp, ty_src, [&](auto impl, auto cmp)->bool {
+ bool found = context.m_resolve.find_trait_impls(sp, lang_CoerceUnsized, pp, src, [&](auto impl, auto cmp)->bool {
DEBUG("[check_coerce] cmp=" << cmp << ", impl=" << impl);
// TODO: Allow fuzzy match if it's the only matching possibility?
// - Recorded for now to know if there could be a matching impl later
@@ -4514,351 +4555,300 @@ namespace {
// - Concretely found - emit the _Unsize op and remove this rule
if( found )
{
- DEBUG("- NEWNODE _Unsize " << &*node_ptr << " -> " << ty_dst);
-
- auto span = node_ptr->span();
- node_ptr = NEWNODE( ty_dst.clone(), span, _Unsize, mv$(node_ptr), ty_dst.clone() );
- return true;
+ return CoerceResult::Unsize;
}
if( fuzzy_match )
{
DEBUG("- best_impl = " << best_impl);
- // Fuzzy match - Insert a CoerceUnsized bound and emit the _Unsize op
- // - This could end up being a no-op _Unsize, and there's special logic in check_associated to handle `T: CoerceUnsized<T>` and `T: Unsize<T>`
- context.add_trait_bound(sp, ty_src, lang_CoerceUnsized, mv$(pp));
- node_ptr = NEWNODE( ty_dst.clone(), sp, _Unsize, mv$(node_ptr), ty_dst.clone() );
- return true;
+ return CoerceResult::Unknown;
}
DEBUG("- No CoerceUnsized impl found");
}
- // 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;
+ // CoerceUnsized struct paths
+ // - If one side is an ivar, create a type-pruned version of the other
+ // - Recurse/unsize inner value
+ if( src.m_data.is_Infer() && TU_TEST2(dst.m_data, Path, .binding, Struct, ->m_struct_markings.coerce_unsized != ::HIR::StructMarkings::Coerce::None) )
+ {
+ auto new_src = H::make_pruned(context, dst);
+ context.equate_types(sp, src, new_src);
+ // TODO: Avoid needless loop return
+ return CoerceResult::Unknown;
+ }
+ if( dst.m_data.is_Infer() && TU_TEST2(src.m_data, Path, .binding, Struct, ->m_struct_markings.coerce_unsized != ::HIR::StructMarkings::Coerce::None) )
+ {
+ auto new_dst = H::make_pruned(context, src);
+ context.equate_types(sp, dst, new_dst);
+ // TODO: Avoid needless loop return
+ return CoerceResult::Unknown;
+ }
+ if( TU_TEST1(dst.m_data, Path, .binding.is_Struct()) && TU_TEST1(src.m_data, Path, .binding.is_Struct()) )
+ {
+ const auto& spbe = src.m_data.as_Path().binding.as_Struct();
+ const auto& dpbe = dst.m_data.as_Path().binding.as_Struct();
+ if( spbe != dpbe )
+ {
+ // TODO: Error here? (equality in caller will cause an error)
+ return CoerceResult::Equality;
+ }
+ const auto& sm = spbe->m_struct_markings;
+ // Has to be equal?
+ if( sm.coerce_unsized == ::HIR::StructMarkings::Coerce::None )
+ return CoerceResult::Equality;
+ const auto& idst = dst.m_data.as_Path().path.m_data.as_Generic().m_params.m_types.at(sm.coerce_param);
+ const auto& isrc = src.m_data.as_Path().path.m_data.as_Generic().m_params.m_types.at(sm.coerce_param);
+ switch( sm.coerce_unsized )
+ {
+ case ::HIR::StructMarkings::Coerce::None:
+ throw "";
+ case ::HIR::StructMarkings::Coerce::Passthrough:
+ DEBUG("Passthough CoerceUnsized");
+ return check_coerce_tys(context, sp, idst, isrc, nullptr);
+ case ::HIR::StructMarkings::Coerce::Pointer:
+ DEBUG("Pointer CoerceUnsized");
+ return check_unsize_tys(context, sp, idst, isrc, nullptr);
}
+ }
- TU_IFLET(::HIR::TypeRef::Data, ty_dst.m_data, Infer, e2,
-
- if( e2.ty_class != ::HIR::InferClass::None ) {
- context.equate_types(sp, ty_dst, ty_src);
- return true;
- }
-
- DEBUG("Both infer, add possibilities");
- context.possible_equate_type_coerce_to(e.index, ty_dst);
- context.possible_equate_type_coerce_from(e2.index, ty_src);
- return false;
- )
- context.possible_equate_type_coerce_to(e.index, ty_dst);
- DEBUG("Source infer, add possibility and continue");
- ),
- (Diverge,
- return true;
- ),
- (Primitive,
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
+ // Any other type, check for pointer
+ // - If not a pointer, return Equality
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (src.m_data), (se),
+ (
+ // NOTE: ! should be handled above or in caller?
+ return CoerceResult::Equality;
),
- (Path,
- // If there is an impl of CoerceUnsized<_> for this, don't equate (just return and wait for a while)
- if( H::type_has_coerce_path(context, ty_src) ) {
- // - Fall through and check the destination.
+ (Infer,
+ // If the other side isn't a pointer, equate
+ ASSERT_BUG(sp, ! dst.m_data.is_Infer(), "Already handled?");
+ if( dst.m_data.is_Pointer() || dst.m_data.is_Borrow() )
+ {
+ context.possible_equate_type_coerce_to(se.index, dst);
+ return CoerceResult::Unknown;
}
- else {
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
+ else
+ {
+ return CoerceResult::Equality;
}
),
- (Generic,
- // TODO: CoerceUnsized bound?
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
- ),
- (TraitObject,
- // Raw trait objects shouldn't even be encountered here?...
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
- ),
- (ErasedType,
- context.equate_types(sp, ty_dst, ty_src);
- return true;
- ),
- (Array,
- // Raw [T; n] doesn't coerce, only borrows do
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
- ),
- (Slice,
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
- ),
- (Tuple,
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
- ),
- (Borrow,
- // Borrows can have unsizing and deref coercions applied
- ),
(Pointer,
- // Pointers coerce to similar pointers of higher restriction
- if( e.type == ::HIR::BorrowType::Shared ) {
- // *const is the bottom of the tree, it doesn't coerce to anything
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
- }
- ),
- (Function,
- ),
- (Closure,
- // TODO: Can closures coerce to anything?
- // - (eventually maybe fn() if they don't capture, but that's not rustc yet)
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
- )
- )
-
- // 2. Check target type is a valid coercion
- // - Otherwise - Force equality
- TU_MATCH( ::HIR::TypeRef::Data, (ty_dst.m_data), (l_e),
- (Infer,
- // If this ivar is of a primitive, equate (as primitives never coerce)
- // TODO: Update for InferClass::Diverge ?
- if( l_e.ty_class != ::HIR::InferClass::None ) {
- context.equate_types(sp, ty_dst, ty_src);
- return true;
- }
-
- context.possible_equate_type_coerce_from(l_e.index, ty_src);
- DEBUG("- Infer, add possibility");
- return false;
- ),
- (Diverge,
- return true;
- ),
- (Primitive,
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
- ),
- (Path,
- if( H::type_has_coerce_path(context, ty_dst) && ty_src.m_data.is_Infer() ) {
- // If this type is coercble, and the source is _ (which means that the first check would have silently failed)
- // - Keep the rule around until the ivar is known
- // NOTE: This assumes that a type can only coerce to a variant of itself (which is usually true)
- DEBUG("Can coerce and source is _, keep rule");
- return false;
+ if( const auto* dep = dst.m_data.opt_Infer() )
+ {
+ context.possible_equate_type_coerce_from(dep->index, src);
+ return CoerceResult::Unknown;
}
- else if( l_e.binding.is_Unbound() ) {
- DEBUG("Unbound path, keep rule");
- return false;
+ if( ! dst.m_data.is_Pointer() )
+ {
+ // TODO: Error here? (leave to caller)
+ return CoerceResult::Equality;
}
- else {
- context.equate_types(sp, ty_dst, ty_src);
- return true;
+ // Pointers coerce to similar pointers of higher restriction
+ if( se.type == ::HIR::BorrowType::Shared )
+ {
+ // *const is the bottom of the tree, it doesn't coerce to anything
+ return CoerceResult::Equality;
}
- ),
- (Generic,
- // TODO: Determine if this generic has any CoerceUnsized impls possible.
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
- ),
- (TraitObject,
- context.equate_types(sp, ty_dst, ty_src);
- return true;
- ),
- (ErasedType,
- context.equate_types(sp, ty_dst, ty_src);
- return true;
- ),
- (Array,
- context.equate_types(sp, ty_dst, ty_src);
- return true;
- ),
- (Slice,
- context.equate_types(sp, ty_dst, ty_src);
- return true;
- ),
- (Tuple,
- context.equate_types(sp, ty_dst, ty_src);
- return true;
- ),
- (Borrow,
- TU_IFLET(::HIR::TypeRef::Data, ty_src.m_data, Borrow, r_e,
- // If using `&mut T` where `&const T` is expected - insert a reborrow (&*)
- // TODO: &move reborrowing rules?
- //if( l_e.type < r_e.type ) {
- if( l_e.type == ::HIR::BorrowType::Shared && r_e.type == ::HIR::BorrowType::Unique ) {
-
- // > Goes from `ty_src` -> `*ty_src` -> `&`l_e.type` `*ty_src`
- const auto& inner_ty = *r_e.inner;
- auto dst_bt = l_e.type;
- auto new_type = ::HIR::TypeRef::new_borrow(dst_bt, inner_ty.clone());
-
- // If the coercion is of a block, do the reborrow on the last node of the block
- // - Cleans up the dumped MIR and prevents needing a reborrow elsewhere.
- ::HIR::ExprNodeP* npp = &node_ptr;
- while( auto* p = dynamic_cast< ::HIR::ExprNode_Block*>(&**npp) )
- {
- DEBUG("- Propagate to the last node of a _Block");
- ASSERT_BUG( p->span(), context.m_ivars.types_equal(p->m_res_type, p->m_value_node->m_res_type),
- "Block and result mismatch - " << context.m_ivars.fmt_type(p->m_res_type) << " != " << context.m_ivars.fmt_type(p->m_value_node->m_res_type));
- ASSERT_BUG( p->span(), context.m_ivars.types_equal(p->m_res_type, ty_src),
- "Block and result mismatch - " << context.m_ivars.fmt_type(p->m_res_type) << " != " << context.m_ivars.fmt_type(ty_src)
- );
- p->m_res_type = new_type.clone();
- npp = &p->m_value_node;
- }
- ::HIR::ExprNodeP& node_ptr = *npp;
+ const auto* dep = &dst.m_data.as_Pointer();
+
+ // If using `*mut T` where `*const T` is expected - add cast
+ if( dep->type == ::HIR::BorrowType::Shared && se.type == ::HIR::BorrowType::Unique )
+ {
+ context.equate_types(sp, *dep->inner, *se.inner);
+ if( node_ptr_ptr )
+ {
+ auto& node_ptr = *node_ptr_ptr;
// Add cast down
auto span = node_ptr->span();
- // *<inner>
- DEBUG("- Deref -> " << inner_ty);
- node_ptr = NEWNODE( inner_ty.clone(), span, _Deref, mv$(node_ptr) );
- context.m_ivars.get_type(node_ptr->m_res_type);
- // &*<inner>
- DEBUG("- Borrow -> " << new_type);
- node_ptr = NEWNODE( mv$(new_type) , span, _Borrow, dst_bt, mv$(node_ptr) );
- context.m_ivars.get_type(node_ptr->m_res_type);
-
- context.m_ivars.mark_change();
+ node_ptr->m_res_type = src.clone();
+ node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Cast( mv$(span), mv$(node_ptr), dst.clone() ));
+ node_ptr->m_res_type = dst.clone();
- // Continue on with coercion (now that node_ptr is updated)
+ return CoerceResult::Custom;
}
- else if( l_e.type != r_e.type ) {
- ERROR(sp, E0000, "Type mismatch between " << ty_dst << " and " << ty_src << " - Borrow classes differ");
- }
- // - Check for coercions
- return check_coerce_borrow(context, l_e.type, *l_e.inner, *r_e.inner, node_ptr);
- )
- else TU_IFLET(::HIR::TypeRef::Data, ty_src.m_data, Infer, r_e,
- // Leave for now
- if( r_e.ty_class != ::HIR::InferClass::None ) {
- // ERROR: Must be compatible
- context.equate_types(sp, ty_dst, ty_src);
- BUG(sp, "Type error expected " << ty_dst << " == " << ty_src);
+ else
+ {
+ return CoerceResult::Unsize;
}
+ }
- context.possible_equate_type_coerce_to(r_e.index, ty_dst);
- DEBUG("- Infer, add possibility");
- return false;
- )
- // TODO: If the type is a UfcsKnown but contains ivars (i.e. would be destructured into an associated type rule)
- // don't equate, and instead return false.
- else {
- // Error: Must be compatible, hand over to the equate code.
- // - If this returns early, it's because of a UFCS destructure
- context.equate_types(sp, ty_dst, ty_src);
- //BUG(sp, "Type error expected " << ty << " == " << ty_src);
+ if( dep->type != se.type ) {
+ ERROR(sp, E0000, "Type mismatch between " << dst << " and " << src << " - Pointer mutability differs");
}
+ return CoerceResult::Equality;
),
- (Pointer,
- // Pointers coerce from borrows and similar pointers
- TU_IFLET(::HIR::TypeRef::Data, ty_src.m_data, Borrow, s_e,
+ (Borrow,
+ if( const auto* dep = dst.m_data.opt_Infer() )
+ {
+ context.possible_equate_type_coerce_from(dep->index, src);
+ return CoerceResult::Unknown;
+ }
+ else if( const auto* dep = dst.m_data.opt_Pointer() )
+ {
+ // Add cast to the pointer (if valid strength reduction)
+ // Call unsizing code on casted value
+
// Borrows can coerce to pointers while reducing in strength
// - Shared < Unique. If the destination is not weaker or equal to the source, it's an error
- if( !(l_e.type <= s_e.type) ) {
- ERROR(sp, E0000, "Type mismatch between " << ty_dst << " and " << ty_src << " - Mutability not compatible");
+ if( !(dep->type <= se.type) ) {
+ ERROR(sp, E0000, "Type mismatch between " << dst << " and " << src << " - Mutability not compatible");
}
// 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();
-
- // Add a coerce of src->&dst_inner
- context.equate_types(sp, *l_e.inner, *s_e.inner);
- //context.equate_types_coerce(
- // node_ptr->span(),
- // ::HIR::TypeRef::new_borrow(l_e.type, l_e.inner->clone()),
- // dynamic_cast<::HIR::ExprNode_Cast*>(&*node_ptr)->m_value
- // );
+ if( node_ptr_ptr )
+ {
+ auto& node_ptr = *node_ptr_ptr;
- context.m_ivars.mark_change();
- return true;
- )
- else TU_IFLET(::HIR::TypeRef::Data, ty_src.m_data, Pointer, r_e,
- // If using `*mut T` where `*const T` is expected - add cast
- if( l_e.type == ::HIR::BorrowType::Shared && r_e.type == ::HIR::BorrowType::Unique ) {
- context.equate_types(sp, *l_e.inner, *r_e.inner);
+ switch( check_unsize_tys(context, sp, *dep->inner, *se.inner, node_ptr_ptr) )
+ {
+ case CoerceResult::Unknown:
+ return CoerceResult::Unknown;
+ case CoerceResult::Custom:
+ return CoerceResult::Custom;
+ case CoerceResult::Equality:
+ DEBUG("- NEWNODE _Cast " << &*node_ptr << " -> " << dst);
+ {
+ auto span = node_ptr->span();
+ node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Cast( mv$(span), mv$(node_ptr), dst.clone() ));
+ node_ptr->m_res_type = dst.clone();
+ }
- // Add cast down
- auto span = node_ptr->span();
- node_ptr->m_res_type = ty_src.clone();
- node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Cast( mv$(span), mv$(node_ptr), ty_dst.clone() ));
- node_ptr->m_res_type = ty_dst.clone();
+ context.equate_types(sp, *dep->inner, *se.inner);
+ return CoerceResult::Custom;
+ case CoerceResult::Unsize:
+ auto dst_b = ::HIR::TypeRef::new_borrow(se.type, dep->inner->clone());
+ DEBUG("- NEWNODE _Unsize " << &*node_ptr << " -> " << dst_b);
+ {
+ auto span = node_ptr->span();
+ node_ptr = NEWNODE( dst_b.clone(), span, _Unsize, mv$(node_ptr), dst_b.clone() );
+ }
- context.m_ivars.mark_change();
- return true;
+ DEBUG("- NEWNODE _Cast " << &*node_ptr << " -> " << dst);
+ {
+ auto span = node_ptr->span();
+ node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Cast( mv$(span), mv$(node_ptr), dst.clone() ));
+ node_ptr->m_res_type = dst.clone();
+ }
+ return CoerceResult::Custom;
+ }
+ throw "";
}
-
- if( l_e.type != r_e.type ) {
- ERROR(sp, E0000, "Type mismatch between " << ty_dst << " and " << ty_src << " - Pointer mutability differs");
+ else
+ {
+ TODO(sp, "Inner coercion of borrow to pointer");
+ return CoerceResult::Equality;
}
- context.equate_types(sp, *l_e.inner, *r_e.inner);
- return true;
- )
- else TU_IFLET(::HIR::TypeRef::Data, ty_src.m_data, Infer, r_e,
- if( r_e.ty_class != ::HIR::InferClass::None ) {
- // ERROR: Must be compatible
- context.equate_types(sp, ty_dst, ty_src);
- BUG(sp, "Type error expected " << ty_dst << " == " << ty_src);
- }
- // Can't do much for now
- context.possible_equate_type_coerce_to(r_e.index, ty_dst);
- DEBUG("- Infer, add possibility");
- return false;
- )
- else {
- // Error: Must be compatible, hand over to the equate code.
- // - If this returns early, it's because of a UFCS destructure
- context.equate_types(sp, ty_dst, ty_src);
- //BUG(sp, "Type error expected " << ty << " == " << ty_src);
}
- ),
- (Function,
- TU_IFLET( ::HIR::TypeRef::Data, ty_src.m_data, Function, r_e,
- if(l_e.m_abi != r_e.m_abi && l_e.is_unsafe == true && r_e.is_unsafe == false ) {
- // LHS is unsafe, RHS is not - Insert a cast
+ else if( const auto* dep = dst.m_data.opt_Borrow() )
+ {
+ // Check strength reduction
+ if( dep->type < se.type )
+ {
+ if( node_ptr_ptr )
+ {
+ // > Goes from `src` -> `*src` -> `&`dep->type` `*src`
+ const auto& inner_ty = *se.inner;
+ auto dst_bt = dep->type;
+ auto new_type = ::HIR::TypeRef::new_borrow(dst_bt, inner_ty.clone());
+
+ // If the coercion is of a block, do the reborrow on the last node of the block
+ // - Cleans up the dumped MIR and prevents needing a reborrow elsewhere.
+ ::HIR::ExprNodeP* npp = node_ptr_ptr;
+ while( auto* p = dynamic_cast< ::HIR::ExprNode_Block*>(&**npp) )
+ {
+ DEBUG("- Propagate to the last node of a _Block");
+ ASSERT_BUG( p->span(), context.m_ivars.types_equal(p->m_res_type, p->m_value_node->m_res_type),
+ "Block and result mismatch - " << context.m_ivars.fmt_type(p->m_res_type) << " != " << context.m_ivars.fmt_type(p->m_value_node->m_res_type));
+ ASSERT_BUG( p->span(), context.m_ivars.types_equal(p->m_res_type, src),
+ "Block and result mismatch - " << context.m_ivars.fmt_type(p->m_res_type) << " != " << context.m_ivars.fmt_type(src)
+ );
+ p->m_res_type = new_type.clone();
+ npp = &p->m_value_node;
+ }
+ ::HIR::ExprNodeP& node_ptr = *npp;
- auto ty_dst_new = ty_src.clone();
- ty_dst_new.m_data.as_Function().is_unsafe = true;
- context.equate_types(sp, ty_dst, ty_dst_new);
+ // Add cast down
+ auto span = node_ptr->span();
+ // *<inner>
+ DEBUG("- Deref -> " << inner_ty);
+ node_ptr = NEWNODE( inner_ty.clone(), span, _Deref, mv$(node_ptr) );
+ context.m_ivars.get_type(node_ptr->m_res_type);
+ // &*<inner>
+ DEBUG("- Borrow -> " << new_type);
+ node_ptr = NEWNODE( mv$(new_type) , span, _Borrow, dst_bt, mv$(node_ptr) );
+ context.m_ivars.get_type(node_ptr->m_res_type);
- // Add cast down
- auto span = node_ptr->span();
- node_ptr->m_res_type = ty_src.clone();
- node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Cast( mv$(span), mv$(node_ptr), ty_dst_new.clone() ));
- node_ptr->m_res_type = mv$(ty_dst_new);
+ context.m_ivars.mark_change();
- context.m_ivars.mark_change();
- return true;
+ // Continue on with coercion (now that node_ptr is updated)
+ switch( check_unsize_tys(context, sp, *dep->inner, *se.inner, node_ptr_ptr) )
+ {
+ case CoerceResult::Unknown:
+ return CoerceResult::Unknown;
+ case CoerceResult::Custom:
+ return CoerceResult::Custom;
+ case CoerceResult::Equality:
+ context.equate_types(sp, *dep->inner, *se.inner);
+ return CoerceResult::Custom;
+ case CoerceResult::Unsize:
+ DEBUG("- NEWNODE _Unsize " << &*node_ptr << " -> " << dst);
+ auto span = node_ptr->span();
+ node_ptr = NEWNODE( dst.clone(), span, _Unsize, mv$(node_ptr), dst.clone() );
+ return CoerceResult::Custom;
+ }
+ throw "";
+ }
+ else
+ {
+ TODO(sp, "Borrow strength reduction");
+ }
}
- context.equate_types(sp, ty_dst, ty_src);
- )
- else TU_IFLET( ::HIR::TypeRef::Data, ty_src.m_data, Closure, r_e,
- // TODO: Could capture-less closures coerce to fn() types?
- context.equate_types(sp, ty_dst, ty_src);
- )
- else {
- context.equate_types(sp, ty_dst, ty_src);
+ else if( dep->type == se.type ) {
+ // Valid.
+ }
+ else {
+ ERROR(sp, E0000, "Type mismatch between " << dst << " and " << src << " - Borrow classes differ");
+ }
+ ASSERT_BUG(sp, dep->type == se.type, "Borrow strength mismatch");
+
+ // Call unsizing code
+ return check_unsize_tys(context, sp, *dep->inner, *se.inner, node_ptr_ptr);
+ }
+ else
+ {
+ // TODO: Error here?
+ return CoerceResult::Equality;
}
- return true;
- ),
- (Closure,
- context.equate_types(sp, ty_dst, node_ptr->m_res_type);
- return true;
)
)
+ }
+ bool check_coerce(Context& context, const Context::Coercion& v)
+ {
+ ::HIR::ExprNodeP& node_ptr = *v.right_node_ptr;
+ const auto& sp = node_ptr->span();
+ const auto& ty_dst = context.m_ivars.get_type(v.left_ty);
+ const auto& ty_src = context.m_ivars.get_type(node_ptr->m_res_type);
+ TRACE_FUNCTION_F(v << " - " << context.m_ivars.fmt_type(ty_dst) << " := " << context.m_ivars.fmt_type(ty_src));
- //TODO(sp, "Typecheck_Code_CS - Coercion " << context.m_ivars.fmt_type(ty) << " from " << context.m_ivars.fmt_type(node_ptr->m_res_type));
- DEBUG("TODO - Coercion " << context.m_ivars.fmt_type(ty_dst) << " from " << context.m_ivars.fmt_type(node_ptr->m_res_type));
- return false;
+ switch( check_coerce_tys(context, sp, ty_dst, ty_src, &node_ptr) )
+ {
+ case CoerceResult::Unknown:
+ DEBUG("Unknown - keep");
+ return false;
+ case CoerceResult::Custom:
+ DEBUG("Custom - Completed");
+ return true;
+ case CoerceResult::Equality:
+ DEBUG("Trigger equality - Completed");
+ context.equate_types(sp, ty_dst, ty_src);
+ return true;
+ case CoerceResult::Unsize:
+ DEBUG("Add _Unsize " << &*node_ptr << " -> " << ty_dst);
+ auto span = node_ptr->span();
+ node_ptr = NEWNODE( ty_dst.clone(), span, _Unsize, mv$(node_ptr), ty_dst.clone() );
+ return true;
+ }
+ throw "";
}
bool check_associated(Context& context, const Context::Associated& v)
diff --git a/src/hir_typeck/helpers.cpp b/src/hir_typeck/helpers.cpp
index af72efa7..f56be694 100644
--- a/src/hir_typeck/helpers.cpp
+++ b/src/hir_typeck/helpers.cpp
@@ -1114,6 +1114,11 @@ bool TraitResolution::find_trait_impls(const Span& sp,
::HIR::PathParams real_params { mv$(new_dst) };
rv = callback( ImplRef(type.clone(), mv$(real_params), {}), ::HIR::Compare::Fuzzy );
};
+ //if( dst_ty.m_data.is_Infer() || type.m_data.is_Infer() )
+ //{
+ // rv = callback( ImplRef(type.clone(), params.clone(), {}), ::HIR::Compare::Fuzzy );
+ // return rv;
+ //}
auto cmp = this->can_unsize(sp, dst_ty, type, cb);
if( cmp == ::HIR::Compare::Equal )
{
diff --git a/src/include/tagged_union.hpp b/src/include/tagged_union.hpp
index f30cb7f2..6343c623 100644
--- a/src/include/tagged_union.hpp
+++ b/src/include/tagged_union.hpp
@@ -109,6 +109,10 @@
#define TU_IFLET(CLASS, VAR, TAG, NAME, ...) if(VAR.tag() == CLASS::TAG_##TAG) { auto& NAME = VAR.as_##TAG(); (void)&NAME; __VA_ARGS__ }
+//#define TU_TEST(VAL, ...) (VAL.is_##TAG() && VAL.as_##TAG() TEST)
+#define TU_TEST1(VAL, TAG1, TEST) (VAL.is_##TAG1() && VAL.as_##TAG1() TEST)
+#define TU_TEST2(VAL, TAG1, FLD1,TAG2, TEST) (VAL.is_##TAG1() && VAL.as_##TAG1() FLD1.is_##TAG2() && VAL.as_##TAG1() FLD1.as_##TAG2() TEST)
+
#define TU_DATANAME(name) Data_##name
// Internals of TU_CONS