summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Hodge <tpg@ucc.asn.au>2019-02-24 16:12:56 +0800
committerJohn Hodge <tpg@ucc.asn.au>2019-02-24 16:12:56 +0800
commitebe84a819577aaf0d06171a33e343e04b1d4717e (patch)
treeeefc9b949aad7c471a489d71e258ce820b580464 /src
parent054faa303107d5940463465d729cc0d1e2ec8473 (diff)
downloadmrust-ebe84a819577aaf0d06171a33e343e04b1d4717e.tar.gz
Typecheck Expr - Refactor of ivar possibilities to simplify logic
Diffstat (limited to 'src')
-rw-r--r--src/common.hpp5
-rw-r--r--src/hir_typeck/expr_cs.cpp1085
-rw-r--r--src/hir_typeck/helpers.cpp10
3 files changed, 444 insertions, 656 deletions
diff --git a/src/common.hpp b/src/common.hpp
index f46f93fd..7521b2de 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -244,6 +244,11 @@ inline Join<T> join(const char *sep, const ::std::vector<T> v) {
namespace std {
template <typename T>
+inline auto operator<<(::std::ostream& os, const T& v) -> decltype(v.fmt(os)) {
+ return v.fmt(os);
+}
+
+template <typename T>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::vector<T*>& v) {
if( v.size() > 0 )
{
diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp
index 70b9c26c..90146635 100644
--- a/src/hir_typeck/expr_cs.cpp
+++ b/src/hir_typeck/expr_cs.cpp
@@ -27,6 +27,15 @@ namespace {
rv.m_path.m_components.pop_back();
return rv;
}
+
+ bool type_contains_impl_placeholder(const ::HIR::TypeRef& t) {
+ return visit_ty_with(t, [&](const auto& ty)->bool {
+ if( ty.m_data.is_Generic() && ty.m_data.as_Generic().binding >> 8 == 2 ) {
+ return true;
+ }
+ return false;
+ });
+ }
}
#define NEWNODE(TY, SP, CLASS, ...) mk_exprnodep(new HIR::ExprNode##CLASS(SP ,## __VA_ARGS__), TY)
@@ -221,7 +230,7 @@ struct Context
/// Default type
//void possible_equate_type_def(unsigned int ivar_index, const ::HIR::TypeRef& t);
/// Add a possible type for an ivar (which is used if only one possibility meets available bounds)
- void possible_equate_type_bound(unsigned int ivar_index, const ::HIR::TypeRef& t);
+ void possible_equate_type_bound(const Span& sp, unsigned int ivar_index, const ::HIR::TypeRef& t);
void possible_equate_type(unsigned int ivar_index, const ::HIR::TypeRef& t, bool is_to, bool is_borrow);
void possible_equate_type_disable(unsigned int ivar_index, bool is_to);
@@ -2726,7 +2735,9 @@ namespace {
const auto& sp = node.span();
const auto& ty = this->context.get_type(node.m_value->m_res_type);
- TRACE_FUNCTION_F("(CallMethod) {" << this->context.m_ivars.fmt_type(ty) << "}." << node.m_method << node.m_params);
+ TRACE_FUNCTION_F("(CallMethod) {" << this->context.m_ivars.fmt_type(ty) << "}." << node.m_method << node.m_params
+ << "(" << FMT_CB(os, for( const auto& arg_node : node.m_args ) os << this->context.m_ivars.fmt_type(arg_node->m_res_type) << ", ";) << ")"
+ );
// Make sure that no mentioned types are inferred until this method is known
this->context.equate_types_from_shadow(node.span(), node.m_res_type);
@@ -2747,7 +2758,7 @@ namespace {
if( possible_methods.empty() )
{
//ERROR(sp, E0000, "Could not find method `" << method_name << "` on type `" << top_ty << "`");
- ERROR(sp, E0000, "No applicable methods for {" << ty << "}." << node.m_method);
+ ERROR(sp, E0000, "No applicable methods for {" << this->context.m_ivars.fmt_type(ty) << "}." << node.m_method);
}
if( possible_methods.size() > 1 )
{
@@ -3399,18 +3410,8 @@ void Context::equate_types(const Span& sp, const ::HIR::TypeRef& li, const ::HIR
// Instantly apply equality
TRACE_FUNCTION_F(li << " == " << ri);
- visit_ty_with(ri, [&](const auto& ty)->bool {
- if( ty.m_data.is_Generic() && ty.m_data.as_Generic().binding >> 8 == 2 ) {
- BUG(sp, "Type contained an impl placeholder parameter - " << ri);
- }
- return false;
- });
- visit_ty_with(li, [&](const auto& ty)->bool {
- if( ty.m_data.is_Generic() && ty.m_data.as_Generic().binding >> 8 == 2 ) {
- BUG(sp, "Type contained an impl placeholder parameter - " << li);
- }
- return false;
- });
+ ASSERT_BUG(sp, !type_contains_impl_placeholder(ri), "Type contained an impl placeholder parameter - " << ri);
+ ASSERT_BUG(sp, !type_contains_impl_placeholder(li), "Type contained an impl placeholder parameter - " << li);
::HIR::TypeRef l_tmp;
::HIR::TypeRef r_tmp;
@@ -3956,6 +3957,11 @@ void Context::handle_pattern(const Span& sp, ::HIR::Pattern& pat, const ::HIR::T
MatchErgonomicsRevisit::disable_possibilities_on_bindings(sp, context, pattern);
return false;
}
+ if( TU_TEST1(ty_p->m_data, Path, .binding.is_Unbound()) ) {
+ // TODO: Visit all inner bindings and disable coercion fallbacks on them.
+ MatchErgonomicsRevisit::disable_possibilities_on_bindings(sp, context, pattern);
+ return false;
+ }
const auto& ty = *ty_p;
// Here we have a known type and binding mode for this pattern
@@ -5005,7 +5011,7 @@ void Context::possible_equate_type(unsigned int ivar_index, const ::HIR::TypeRef
);
list.push_back( t.clone() );
}
-void Context::possible_equate_type_bound(unsigned int ivar_index, const ::HIR::TypeRef& t) {
+void Context::possible_equate_type_bound(const Span& sp, unsigned int ivar_index, const ::HIR::TypeRef& t) {
{
::HIR::TypeRef ty_l;
ty_l.m_data.as_Infer().index = ivar_index;
@@ -5015,6 +5021,8 @@ void Context::possible_equate_type_bound(unsigned int ivar_index, const ::HIR::T
DEBUG("IVar " << ivar_index << " is actually " << real_ty);
return ;
}
+
+ ASSERT_BUG(sp, !type_contains_impl_placeholder(t), "Type contained an impl placeholder parameter - " << t);
}
if( ivar_index >= possible_ivar_vals.size() ) {
@@ -5219,8 +5227,11 @@ namespace {
};
// 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)
+ CoerceResult check_unsize_tys(Context& context_mut_r, const Span& sp, const ::HIR::TypeRef& dst_raw, const ::HIR::TypeRef& src_raw, ::HIR::ExprNodeP* node_ptr_ptr=nullptr, bool allow_mutate=true)
{
+ Context* context_mut = (allow_mutate ? &context_mut_r : nullptr);
+ const Context& context = context_mut_r;
+
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);
@@ -5258,8 +5269,11 @@ namespace {
DEBUG("Literal ivars");
return CoerceResult::Equality;
}
- context.possible_equate_type_unsize_to(src.m_data.as_Infer().index, dst);
- context.possible_equate_type_unsize_from(dst.m_data.as_Infer().index, src);
+ if( context_mut )
+ {
+ context_mut->possible_equate_type_unsize_to(src.m_data.as_Infer().index, dst);
+ context_mut->possible_equate_type_unsize_from(dst.m_data.as_Infer().index, src);
+ }
DEBUG("Both ivars");
return CoerceResult::Unknown;
}
@@ -5271,7 +5285,10 @@ namespace {
DEBUG("Literal with primitive");
return CoerceResult::Equality;
}
- context.possible_equate_type_unsize_from(dep->index, src);
+ if( context_mut )
+ {
+ context_mut->possible_equate_type_unsize_from(dep->index, src);
+ }
DEBUG("Dst ivar");
return CoerceResult::Unknown;
}
@@ -5283,7 +5300,10 @@ namespace {
DEBUG("Literal with primitive");
return CoerceResult::Equality;
}
- context.possible_equate_type_unsize_to(sep->index, dst);
+ if( context_mut )
+ {
+ context_mut->possible_equate_type_unsize_to(sep->index, dst);
+ }
DEBUG("Src is ivar (" << src << "), return Unknown");
return CoerceResult::Unknown;
}
@@ -5295,7 +5315,10 @@ namespace {
// 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( context_mut )
+ {
+ context_mut->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)
@@ -5329,10 +5352,9 @@ namespace {
// Deref coercions
// - If right can be dereferenced to left
- if(node_ptr_ptr)
+ if(node_ptr_ptr || !allow_mutate)
{
DEBUG("-- Deref coercions");
- auto& node_ptr = *node_ptr_ptr;
::HIR::TypeRef tmp_ty;
const ::HIR::TypeRef* out_ty_p = &src;
unsigned int count = 0;
@@ -5342,9 +5364,26 @@ namespace {
const auto& out_ty = context.m_ivars.get_type(*out_ty_p);
count += 1;
- if( out_ty.m_data.is_Infer() && !out_ty.m_data.as_Infer().is_lit() ) {
- // Hit a _, so can't keep going
- break;
+ if( const auto* sep = out_ty.m_data.opt_Infer() )
+ {
+ if( !sep->is_lit() )
+ {
+ // Hit a _, so can't keep going
+ if( context_mut )
+ {
+ // Could also be any deref chain of the destination type
+ ::HIR::TypeRef tmp_ty2;
+ const ::HIR::TypeRef* d_ty_p = &dst;
+ context_mut->possible_equate_type_unsize_to(sep->index, dst);
+ for(unsigned int i = 0; i < count && (d_ty_p = context.m_resolve.autoderef(sp, *d_ty_p, tmp_ty2)); i ++)
+ {
+ context_mut->possible_equate_type_unsize_to(sep->index, *d_ty_p);
+ }
+ }
+ DEBUG("Src derefs to ivar (" << src << "), return Unknown");
+ return CoerceResult::Unknown;
+ }
+ // Literal infer, keep going (but remember how many times we dereferenced?)
}
types.push_back( out_ty.clone() );
@@ -5363,11 +5402,17 @@ namespace {
continue ;
}
DEBUG("Same tag and fuzzy match - assuming " << dst << " == " << out_ty);
- context.equate_types(sp, dst, out_ty);
+ if( context_mut )
+ {
+ context_mut->equate_types(sp, dst, out_ty);
+ }
),
(Slice,
// Equate!
- context.equate_types(sp, dst, out_ty);
+ if(context_mut)
+ {
+ context_mut->equate_types(sp, dst, out_ty);
+ }
// - Fall through
)
)
@@ -5377,21 +5422,25 @@ namespace {
}
}
- add_coerce_borrow(context, node_ptr, types.back(), [&](auto& node_ptr)->void {
- // node_ptr = node that yeilds ty_src
- assert( count == types.size() );
- for(unsigned int i = 0; i < types.size(); i ++ )
- {
- auto span = node_ptr->span();
- // TODO: Replace with a call to context.create_autoderef to handle cases where the below assertion would fire.
- ASSERT_BUG(span, !node_ptr->m_res_type.m_data.is_Array(), "Array->Slice shouldn't be in deref coercions");
- auto ty = mv$(types[i]);
- node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Deref( mv$(span), mv$(node_ptr) ));
- DEBUG("- Deref " << &*node_ptr << " -> " << ty);
- node_ptr->m_res_type = mv$(ty);
- context.m_ivars.get_type(node_ptr->m_res_type);
- }
- });
+ if( context_mut && node_ptr_ptr )
+ {
+ auto& node_ptr = *node_ptr_ptr;
+ add_coerce_borrow(*context_mut, node_ptr, types.back(), [&](auto& node_ptr)->void {
+ // node_ptr = node that yeilds ty_src
+ assert( count == types.size() );
+ for(unsigned int i = 0; i < types.size(); i ++ )
+ {
+ auto span = node_ptr->span();
+ // TODO: Replace with a call to context.create_autoderef to handle cases where the below assertion would fire.
+ ASSERT_BUG(span, !node_ptr->m_res_type.m_data.is_Array(), "Array->Slice shouldn't be in deref coercions");
+ auto ty = mv$(types[i]);
+ node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Deref( mv$(span), mv$(node_ptr) ));
+ DEBUG("- Deref " << &*node_ptr << " -> " << ty);
+ node_ptr->m_res_type = mv$(ty);
+ context.m_ivars.get_type(node_ptr->m_res_type);
+ }
+ });
+ }
return CoerceResult::Custom;
}
@@ -5414,9 +5463,12 @@ namespace {
}
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 ++)
+ if( context_mut )
{
- context.equate_types(sp, tys_d[i], tys_s.at(i));
+ for(size_t i = 0; i < tys_d.size(); i ++)
+ {
+ context_mut->equate_types(sp, tys_d[i], tys_s.at(i));
+ }
}
// 2. Destination markers must be a strict subset
@@ -5476,24 +5528,27 @@ namespace {
}
// 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]);
- }
- for(const auto& tyb : dep->m_trait.m_type_bounds)
+ if( context_mut )
{
- auto ty = best_impl.get_type(tyb.first.c_str());
- if( ty != ::HIR::TypeRef() )
+ context_mut->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, tyb.second, ty);
+ context_mut->equate_types(sp, trait.m_params.m_types[i], args.m_types[i]);
}
- else
+ for(const auto& tyb : dep->m_trait.m_type_bounds)
{
- // Error? Log? ...
- DEBUG("Associated type " << tyb.first << " not present in impl, can't equate");
+ auto ty = best_impl.get_type(tyb.first.c_str());
+ if( ty != ::HIR::TypeRef() )
+ {
+ context_mut->equate_types(sp, tyb.second, ty);
+ }
+ else
+ {
+ // Error? Log? ...
+ DEBUG("Associated type " << tyb.first << " not present in impl, can't equate");
+ }
}
}
}
@@ -5563,7 +5618,10 @@ namespace {
{
auto pp = best_impl.get_trait_params();
DEBUG("Fuzzy, best was Unsize" << pp);
- context.equate_types(sp, dst, pp.m_types.at(0));
+ if( context_mut )
+ {
+ context_mut->equate_types(sp, dst, pp.m_types.at(0));
+ }
return CoerceResult::Unsize;
}
else
@@ -5619,7 +5677,7 @@ namespace {
{
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, idst, isrc, nullptr);
+ return check_unsize_tys(context_mut_r, sp, idst, isrc, nullptr, allow_mutate);
}
else
{
@@ -5634,9 +5692,15 @@ namespace {
)
}
- DEBUG("Reached end of check_unsize_tys, return Unknown");
+ // If the destination is an Unbound path, return Unknown
+ if( TU_TEST1(dst.m_data, Path, .binding.is_Unbound()) )
+ {
+ return CoerceResult::Unknown;
+ }
+
+ DEBUG("Reached end of check_unsize_tys, return Equality");
// TODO: Determine if this unsizing could ever happen.
- return CoerceResult::Unknown;
+ return CoerceResult::Equality;
}
/// Checks if two types can be a valid coercion
@@ -6326,7 +6390,7 @@ namespace {
if( TU_TEST1(impl_ty.m_data, Infer, .is_lit() == false) )
{
DEBUG("Unbounded ivar, waiting - TODO: Add possibility " << impl_ty << " == " << possible_impl_ty);
- //context.possible_equate_type_bound(impl_ty.m_data.as_Infer().index, possible_impl_ty);
+ //context.possible_equate_type_bound(sp, impl_ty.m_data.as_Infer().index, possible_impl_ty);
return false;
}
// Only one possible impl
@@ -6389,7 +6453,15 @@ namespace {
{
const auto& t = context.get_type(v.params.m_types[i]);
if( const auto* e = t.m_data.opt_Infer() ) {
- context.possible_equate_type_bound(e->index, pi.params.m_types[i]);
+ const auto& pi_t = pi.params.m_types[i];
+ if( !type_contains_impl_placeholder(pi_t) )
+ {
+ context.possible_equate_type_bound(sp, e->index, pi_t);
+ }
+ else
+ {
+ DEBUG("Not adding placeholder-containing type as a bound - " << pi_t);
+ }
}
}
}
@@ -6437,7 +6509,7 @@ namespace {
bound_failed = false;
return true;
});
- if( bound_failed ) {
+ if( bound_failed && ! t.m_data.is_Infer() ) {
// If none was found, remove from the possibility list
DEBUG("Remove possibility " << new_ty << " because it failed a bound");
return true;
@@ -6463,7 +6535,7 @@ namespace {
::std::vector<::std::pair<TraitResolution::AutoderefBorrow, ::HIR::Path>> possible_methods;
unsigned int deref_count = context.m_resolve.autoderef_find_method(node.span(), node.m_traits, node.m_trait_param_ivars, t, node.m_method, possible_methods);
DEBUG("> deref_count = " << deref_count << ", " << possible_methods);
- if( possible_methods.empty() )
+ if( !t.m_data.is_Infer() && possible_methods.empty() )
{
// No method found, which would be an error
return true;
@@ -6477,661 +6549,358 @@ namespace {
return false;
}
- bool check_ivar_poss__coercions(Context& context, unsigned int i, Context::IVarPossible& ivar_ent, const ::HIR::TypeRef& ty_l, bool honour_disable=true)
+ /// Check IVar possibilities, from both coercion/unsizing (which have well-encoded rules) and from trait impls
+ bool check_ivar_poss(Context& context, unsigned int i, Context::IVarPossible& ivar_ent, bool honour_disable=true)
{
static Span _span;
const auto& sp = _span;
- bool allow_unsized = !(i < context.m_ivars_sized.size() ? context.m_ivars_sized.at(i) : false);
+ if( honour_disable && (ivar_ent.force_no_to || ivar_ent.force_no_from) )
+ {
+ DEBUG(i << ": forced unknown");
+ return false;
+ }
- enum class DedupKeep {
- Both,
- Left,
- Right,
- };
- struct H {
- static void dedup_type_list_with(::std::vector< ::HIR::TypeRef>& list, ::std::function<DedupKeep(const ::HIR::TypeRef& l, const ::HIR::TypeRef& r)> cmp) {
- if( list.size() <= 1 )
- return ;
+ ::HIR::TypeRef ty_l_ivar;
+ ty_l_ivar.m_data.as_Infer().index = i;
+ const auto& ty_l = context.m_ivars.get_type(ty_l_ivar);
- for( auto it = list.begin(); it != list.end(); )
- {
- bool found = false;
- for( auto it2 = list.begin(); it2 != it; ++ it2 ) {
- auto action = cmp(*it, *it2);
- if( action != DedupKeep::Both )
- {
- if( action == DedupKeep::Right ) {
- //DEBUG("Keep " << *it << ", toss " << *it2);
- }
- else {
- ::std::swap(*it2, *it);
- //DEBUG("Keep " << *it << ", toss " << *it2 << " (swapped)");
- }
- found = true;
- break;
- }
- }
- if( found ) {
- it = list.erase(it);
- }
- else {
- ++ it;
- }
- }
- }
- // De-duplicate list (taking into account other ivars)
- // - TODO: Use the direction and do a fuzzy equality based on coercion possibility
- static void dedup_type_list(const Context& context, ::std::vector< ::HIR::TypeRef>& list) {
- dedup_type_list_with(list, [&context](const auto& l, const auto& r){ return H::equal_to(context, l, r) ? DedupKeep::Left : DedupKeep::Both; });
+ if( ty_l != ty_l_ivar ) {
+ if( ivar_ent.has_rules() )
+ {
+ DEBUG("- IVar " << i << " had possibilities, but was known to be " << ty_l);
+ // Completely clear by reinitialising
+ ivar_ent = Context::IVarPossible();
}
+ return false;
+ }
- // Types are equal from the view of being coercion targets
- // - Inequality here means that the targets could coexist in the list (e.g. &[u8; N] and &[u8])
- // - Equality means that they HAVE to be equal (even if they're not currently due to ivars)
- // - E.g. &mut HashMap<_1> and &mut HashMap<T> would unify
- static bool equal_to(const Context& context, const ::HIR::TypeRef& ia, const ::HIR::TypeRef& ib) {
- const auto& a = context.m_ivars.get_type(ia);
- const auto& b = context.m_ivars.get_type(ib);
- if( a.m_data.tag() != b.m_data.tag() )
- return false;
- TU_MATCH_DEF(::HIR::TypeRef::Data, (a.m_data, b.m_data), (e_a, e_b),
- (
- return context.m_ivars.types_equal(a, b);
- ),
- (Borrow,
- if( e_a.type != e_b.type )
- return false;
- const auto& ia = context.m_ivars.get_type(*e_a.inner);
- const auto& ib = context.m_ivars.get_type(*e_b.inner);
- if( ia.m_data.tag() != ib.m_data.tag() )
- return false;
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ia.m_data, ib.m_data), (e_ia, e_ib),
- (
- return context.m_ivars.types_equal(ia, ib);
- ),
- (Infer,
- return false;
- ),
- (Path,
- if( e_ia.binding.tag() != e_ib.binding.tag() )
- return false;
- TU_MATCHA( (e_ia.binding, e_ib.binding), (pbe_a, pbe_b),
- (Unbound, return false; ),
- (Opaque,
- // TODO: Check bounds?
- return false;
- ),
- (ExternType,
- return false;
- ),
- (Struct,
- if(pbe_a != pbe_b) return false;
- if( !pbe_a->m_struct_markings.can_unsize )
- return true;
- // It _could_ unsize, so let it coexist
- return false;
- ),
- (Union,
- return false;
- ),
- (Enum,
- return false;
- )
- )
- ),
- (Slice,
- const auto& ia2 = context.m_ivars.get_type(*e_ia.inner);
- const auto& ib2 = context.m_ivars.get_type(*e_ib.inner);
- if(ia2.m_data.is_Infer() || ib2.m_data.is_Infer())
- return true;
- return context.m_ivars.types_equal(ia2, ib2);
- )
- )
- ),
- (Pointer,
- if( e_a.type != e_b.type )
- return false;
- // TODO: Rules are subtly different when coercing from a pointer?
- const auto& ia2 = context.m_ivars.get_type(*e_a.inner);
- const auto& ib2 = context.m_ivars.get_type(*e_b.inner);
- if(ia2.m_data.is_Infer() || ib2.m_data.is_Infer())
- return true;
- return context.m_ivars.types_equal(ia2, ib2);
- )
- )
- //
- return context.m_ivars.types_equal(a, b);
- }
- // Types are equal from the view of being coercion sources
- static bool equal_from(const Context& context, const ::HIR::TypeRef& a, const ::HIR::TypeRef& b) {
- return context.m_ivars.types_equal(a, b);
- }
+ if( ! ivar_ent.has_rules() ) {
+ // No rules, don't do anything (and don't print)
+ DEBUG(i << ": No rules");
+ return false;
+ }
- // TODO: `can_unsize_to`
- static bool can_coerce_to(const Context& context, const ::HIR::TypeRef& dst, const ::HIR::TypeRef& src) {
- if( dst.m_data.is_Infer() )
- return false;
- if( src.m_data.is_Infer() )
- return false;
+ TRACE_FUNCTION_F(i << (honour_disable ? "" : " fallback"));
- if( dst.m_data.is_Borrow() && src.m_data.is_Borrow() ) {
- const auto& d_e = dst.m_data.as_Borrow();
- const auto& s_e = src.m_data.as_Borrow();
- // Higher = more specific (e.g. Unique > Shared)
- if( s_e.type < d_e.type ) {
- return false;
- }
- else if( s_e.type == d_e.type ) {
- // Check relationship
- // - 1. Deref chain.
- // - 2. Trait object?
- }
- else {
- return context.m_ivars.types_equal(*s_e.inner, *d_e.inner);
- }
- }
+ bool has_no_coerce_posiblities;
- if( dst.m_data.is_Pointer() && src.m_data.is_Pointer() ) {
- const auto& d_e = dst.m_data.as_Pointer();
- const auto& s_e = src.m_data.as_Pointer();
- // Higher = more specific (e.g. Unique > Shared)
- if( s_e.type < d_e.type ) {
- return false;
- }
- else if( s_e.type == d_e.type ) {
- // Check relationship
- // - 1. Deref chain.
- // - 2. Trait object?
- }
- else {
- return context.m_ivars.types_equal(*s_e.inner, *d_e.inner);
- }
+ // Fill a single list with all possibilities, and pick the most suitable type.
+ // - This list needs to include flags to say if the type can be dereferenced.
+ {
+ struct PossibleType {
+ bool is_pointer; // I.e. it's from a coerce
+ bool can_deref; // I.e. from an unsize or coerce, AND it's a "from"
+ const ::HIR::TypeRef* ty;
+
+ bool operator<(const PossibleType& o) const {
+ if( *ty != *o.ty )
+ return *ty < *o.ty;
+ if( is_pointer != o.is_pointer )
+ return is_pointer < o.is_pointer;
+ if( can_deref < o.can_deref )
+ return can_deref < o.can_deref;
+ return false;
}
-
- if( dst.m_data.is_Pointer() && src.m_data.is_Borrow() ) {
- const auto& d_e = dst.m_data.as_Pointer();
- const auto& s_e = src.m_data.as_Borrow();
- if( s_e.type == d_e.type ) {
- return context.m_ivars.types_equal(*s_e.inner, *d_e.inner);
- }
+ ::std::ostream& fmt(::std::ostream& os) const {
+ return os << (is_pointer ? "C" : "-") << (can_deref ? "D" : "-") << " " << *ty;
}
- return false;
- }
+ };
- // Returns true if the `src` concretely cannot coerce to `dst`
- static bool cannot_coerce_to(const Context& context, const ::HIR::TypeRef& dst, const ::HIR::TypeRef& src) {
- TU_IFLET( ::HIR::TypeRef::Data, src.m_data, Borrow, se,
- TU_IFLET( ::HIR::TypeRef::Data, dst.m_data, Borrow, de,
- )
- else TU_IFLET( ::HIR::TypeRef::Data, dst.m_data, Pointer, de,
- )
- else {
- return true;
- }
- )
- return false;
- }
+ bool allow_unsized = !(i < context.m_ivars_sized.size() ? context.m_ivars_sized.at(i) : false);
- static const ::HIR::TypeRef* find_lowest_type(const Context& context, const ::std::vector< ::HIR::TypeRef>& list)
+ ::std::vector<PossibleType> possible_tys;
+ static ::HIR::TypeRef null_placeholder;
+ if( honour_disable && ivar_ent.force_no_from ) // TODO: This can't happen, there's an early return above.
{
- // 1. Locate types that cannot coerce to anything
- // - &TraitObject and &[T] are the main pair
- for(const auto& ty : list) {
- TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Borrow, e,
- TU_MATCH_DEF(::HIR::TypeRef::Data, (e.inner->m_data), (e2),
- (
- ),
- (Slice,
- return &ty;
- ),
- (TraitObject,
- return &ty;
- )
- )
- )
- }
-
- // 2. Search the list for a type that is a valid coercion target for all other types in the list
- // - NOTE: Ivars return `false` nomatter what order
- const auto* cur_type = &list[0];
- for(const auto& ty : list) {
- // If ty can be coerced to the current type
- if( H::can_coerce_to(context, *cur_type, ty) ) {
- // - Keep current type
- }
- else if( H::can_coerce_to(context, ty, *cur_type) ) {
- cur_type = &ty;
- }
- else {
- // Error? Give up.
- cur_type = nullptr;
- break;
- }
- }
- if( cur_type ) {
- // TODO: Replace
- //return cur_type;
- }
-
- return nullptr;
+ possible_tys.push_back(PossibleType { false, true, &null_placeholder });
}
-
- /// Returns true if `dst` is found when dereferencing `src`
- static bool type_derefs_from(const Span& sp, const Context& context, const ::HIR::TypeRef& dst, const ::HIR::TypeRef& src)
+ for(const auto& new_ty : ivar_ent.types_coerce_from )
{
- ::HIR::TypeRef tmp;
- const ::HIR::TypeRef* ty = &src;
- do
- {
- if( context.m_ivars.types_equal(*ty, dst) )
- return true;
- } while( (ty = context.m_resolve.autoderef(sp, *ty, tmp)) );
- return false;
+ possible_tys.push_back(PossibleType { true , true, &new_ty });
}
-
- static ::std::vector<::HIR::TypeRef>& merge_lists(const Context& context, ::std::vector<::HIR::TypeRef>& list_a, ::std::vector<::HIR::TypeRef>& list_b, ::std::vector<::HIR::TypeRef>& out)
+ for(const auto& new_ty : ivar_ent.types_unsize_from )
{
- if( list_a.size() == 0 )
- return list_b;
- else if( list_b.size() == 0 )
- return list_a;
- else {
- for(const auto& t : list_a) {
- out.push_back( t.clone() );
- }
- for(const auto& t : list_b ) {
- out.push_back( t.clone() );
- }
- H::dedup_type_list(context, out);
- return out;
- }
+ possible_tys.push_back(PossibleType { false, true, &new_ty });
}
- };
-
- // If this type has an infer class active, don't allw a non-primitive to coerce over it.
- if( ty_l.m_data.as_Infer().is_lit() )
- {
- DEBUG("Literal checks");
- // TODO: Actively search possibility list for the real type.
- for(const auto& ty : ivar_ent.types_coerce_to)
- if( ty.m_data.is_Primitive() ) {
- context.equate_types(sp, ty_l, ty);
- return true;
- }
- for(const auto& ty : ivar_ent.types_unsize_to)
- if( ty.m_data.is_Primitive() ) {
- context.equate_types(sp, ty_l, ty);
- return true;
- }
- for(const auto& ty : ivar_ent.types_coerce_from)
- if( ty.m_data.is_Primitive() ) {
- context.equate_types(sp, ty_l, ty);
- return true;
- }
- for(const auto& ty : ivar_ent.types_unsize_from)
- if( ty.m_data.is_Primitive() ) {
- context.equate_types(sp, ty_l, ty);
- return true;
- }
- return false;
- }
-
- if( honour_disable && (ivar_ent.force_no_to || ivar_ent.force_no_from) )
- {
- DEBUG("- IVar " << ty_l << " is forced unknown");
- return false;
- }
- else
- {
- // TODO: Dedup based on context?
- // - The dedup should probably be aware of the way the types are used (for coercions).
- H::dedup_type_list(context, ivar_ent.types_coerce_to);
- H::dedup_type_list(context, ivar_ent.types_unsize_to);
- H::dedup_type_list(context, ivar_ent.types_coerce_from);
- H::dedup_type_list(context, ivar_ent.types_unsize_from);
-
- #if 0
- // If there is a default type compatible with all possibilities, use that.
- if( ivar_ent.types_default.size() > 0 ) {
- // TODO: Should multiple options be valid?
- ASSERT_BUG(Span(), ivar_ent.types_def.size() == 1, "TODO: Multiple default types for an ivar - " << ivar_ent.types_def);
+ if( honour_disable && ivar_ent.force_no_to ) // TODO: This can't happen, there's an early return above.
+ {
+ possible_tys.push_back(PossibleType { false, false, &null_placeholder });
}
- #endif
-
- if( ivar_ent.types_coerce_from.size() == 0 && ivar_ent.types_coerce_to.size() == 0
- && ivar_ent.types_unsize_from.size() == 0 && ivar_ent.types_unsize_to.size() == 0
- )
+ for(const auto& new_ty : ivar_ent.types_coerce_to )
{
- DEBUG("-- No known options for " << ty_l);
- return false;
+ possible_tys.push_back(PossibleType { true , false, &new_ty });
}
- DEBUG("-- " << ty_l << " FROM=Coerce:{" << ivar_ent.types_coerce_from << "} / Unsize:{" << ivar_ent.types_unsize_from << "},"
- << " TO=Coerce:{" << ivar_ent.types_coerce_to << "} / Unsize:{" << ivar_ent.types_unsize_to << "}");
-
- // Find an entry in the `types_unsize_from` list that all other entries can unsize to
- H::dedup_type_list_with(ivar_ent.types_unsize_from, [&](const auto& l, const auto& r) {
- if( l.m_data.is_Infer() || r.m_data.is_Infer() )
- return DedupKeep::Both;
-
- // Check for fuzzy equality of types, and keep only one
- // TODO: Ensure that whatever ivar differs can't be different (i.e. it wouldn't change the unsize/coerce)
- // TODO: Use `check_unsize_tys` instead
- if( l.compare_with_placeholders(sp, r, context.m_ivars.callback_resolve_infer()) != ::HIR::Compare::Unequal )
- {
- DEBUG("Possible match, keep left");
- return DedupKeep::Left;
- }
-
- // &T and T
- TU_IFLET( ::HIR::TypeRef::Data, l.m_data, Borrow, le,
- TU_IFLET( ::HIR::TypeRef::Data, r.m_data, Borrow, re,
- )
- else {
- // if *le.inner == r, return DedupKeep::Right
- if( context.m_ivars.types_equal(*le.inner, r) )
- return DedupKeep::Right;
- }
- )
- else {
- TU_IFLET( ::HIR::TypeRef::Data, r.m_data, Borrow, re,
- // if *re.inner == l, return DedupKeep::Left
- if( context.m_ivars.types_equal(*re.inner, l) )
- return DedupKeep::Left;
- )
- }
- return DedupKeep::Both;
- });
- // Find an entry in the `types_coerce_from` list that all other entries can coerce to
- H::dedup_type_list_with(ivar_ent.types_coerce_from, [&](const auto& l, const auto& r) {
- if( l.m_data.is_Infer() || r.m_data.is_Infer() )
- return DedupKeep::Both;
-
- // Check for fuzzy equality of types, and keep only one
- // TODO: Ensure that whatever ivar differs can't be different (i.e. it wouldn't change the unsize/coerce)
- // TODO: Use `check_coerce_tys` instead
- if( l.compare_with_placeholders(sp, r, context.m_ivars.callback_resolve_infer()) != ::HIR::Compare::Unequal )
- {
- DEBUG("Possible match, keep left");
- return DedupKeep::Left;
- }
-
- if( l.m_data.is_Borrow() )
+ for(const auto& new_ty : ivar_ent.types_unsize_to )
+ {
+ possible_tys.push_back(PossibleType { false, false, &new_ty });
+ }
+#if 1
+ DEBUG("possible_tys = " << possible_tys);
+ DEBUG("Adding bounded [" << ivar_ent.bounded << "]");
+ if( !possible_tys.empty() )
+ {
+ for(const auto& new_ty : ivar_ent.bounded)
{
- const auto& le = l.m_data.as_Borrow();
- ASSERT_BUG(sp, r.m_data.is_Borrow() || r.m_data.is_Pointer(), "Coerce source for borrow isn't a borrow/pointert - " << r);
- const auto re_borrow_type = r.m_data.is_Borrow() ? r.m_data.as_Borrow().type : r.m_data.as_Pointer().type;
- const auto& re_inner = r.m_data.is_Borrow() ? r.m_data.as_Borrow().inner : r.m_data.as_Pointer().inner;
-
- if( le.type < re_borrow_type ) {
- if( !context.m_ivars.types_equal(*le.inner, *re_inner) ) {
- return DedupKeep::Both;
+ bool failed_a_bound = false;
+ // Check if this bounded type _cannot_ work with any of the existing bounds
+ // - Don't add to the possiblity list if so
+ for(const auto& opt : possible_tys)
+ {
+ CoerceResult res;
+ if( opt.can_deref ) {
+ DEBUG(" > " << new_ty << " =? " << *opt.ty);
+ res = check_unsize_tys(context, sp, new_ty, *opt.ty, nullptr, false);
}
- DEBUG("- Remove " << r);
- return DedupKeep::Left;
- }
- else if( le.type > re_borrow_type ) {
- if( !context.m_ivars.types_equal(*le.inner, *re_inner) ) {
- return DedupKeep::Both;
+ else {
+ // Destination type, this option must deref to it
+ DEBUG(" > " << *opt.ty << " =? " << new_ty);
+ res = check_unsize_tys(context, sp, *opt.ty, new_ty, nullptr, false);
+ }
+ DEBUG(" = " << res);
+ if( res == CoerceResult::Equality ) {
+ failed_a_bound = true;
+ break;
+ }
+ else if( res == CoerceResult::Unknown ) {
+ // Should this also be treated as a fail?
}
- DEBUG("- Remove " << l);
- return DedupKeep::Right;
}
- else {
+ if( !failed_a_bound )
+ {
+ possible_tys.push_back(PossibleType { false, false, &new_ty });
}
-
- // Dereference `*re.inner` until it isn't possible or it equals `*le.inner`
- // - Repeat going the other direction.
- if( H::type_derefs_from(sp, context, *le.inner, *re_inner) )
- return DedupKeep::Left;
- if( H::type_derefs_from(sp, context, *re_inner, *le.inner) )
- return DedupKeep::Right;
}
- return DedupKeep::Both;
- });
-
-
- // If there's one option for both desination types, and nothing for the source ...
- if( ivar_ent.types_coerce_to.size() == 1 && ivar_ent.types_unsize_to.size() == 1 && ivar_ent.types_coerce_from.empty() && ivar_ent.types_unsize_from.empty() )
+ }
+#endif
+ DEBUG("possible_tys = " << possible_tys);
+ // Filter out useless options and impossiblities
+ for(auto it = possible_tys.begin(); it != possible_tys.end(); )
{
- // And the coercion can unsize to the unsize, pick the coercion (it's valid, the other way around isn't)
- // TODO: Use a `can_unsize_to` functon instead (that handles Unsize as well as Deref)
- if( H::type_derefs_from(sp, context, ivar_ent.types_unsize_to[0], ivar_ent.types_coerce_to[0]) )
+ bool remove_option = false;
+ if( *it->ty == ty_l )
{
- const auto& new_ty = ivar_ent.types_coerce_to[0];
- DEBUG("- IVar " << ty_l << " = " << new_ty << " (unsize to)");
- context.equate_types(sp, ty_l, new_ty);
- return true;
+ remove_option = true;
}
- }
- #if 0
- // If there's only one coerce and unsize from, ...
- if( ivar_ent.types_coerce_from.size() == 1 && ivar_ent.types_unsize_from.size() == 1 && ivar_ent.types_coerce_to.empty() && ivar_ent.types_unsize_to.empty() )
- {
- // and if the coerce can unsize to the unsize target.
- if( H::can_coerce_to(context, ivar_ent.types_unsize_from[0], ivar_ent.types_coerce_from[0]) )
+ else if( !allow_unsized && context.m_resolve.type_is_sized(sp, *it->ty) == ::HIR::Compare::Unequal )
{
- const auto& new_ty = ivar_ent.types_coerce_from[0];
- DEBUG("- IVar " << ty_l << " = " << new_ty << " (coerce from)");
- context.equate_types(sp, ty_l, new_ty);
- return true;
+ remove_option = true;
}
- }
- #endif
-
- //
-
- // HACK: Merge into a single lists
- ::std::vector< ::HIR::TypeRef> types_from_o;
- auto& types_from = H::merge_lists(context, ivar_ent.types_coerce_from, ivar_ent.types_unsize_from, types_from_o);
- ::std::vector< ::HIR::TypeRef> types_to_o;
- auto& types_to = H::merge_lists(context, ivar_ent.types_coerce_to , ivar_ent.types_unsize_to , types_to_o );
- DEBUG(" " << ty_l << " FROM={" << types_from << "}, TO={" << types_to << "}");
-
- // TODO: If there is only a single option and it's from an Unsize, is it valid?
-
- // TODO: Loop both lists and if there's T<_{int}> and T<uN/iN> unify those.
-
- // Same type on both sides, pick it.
- if( types_from == types_to && types_from.size() == 1 ) {
- const auto& new_ty = types_from[0];
- DEBUG("- IVar " << ty_l << " = " << new_ty << " (only)");
- context.equate_types(sp, ty_l, new_ty);
- return true;
- }
-
- // Eliminate possibilities that don't fit known constraints
- if( types_to.size() > 0 && types_from.size() > 0 )
- {
- // TODO: Search `types_to` too
- for(auto it = types_from.begin(); it != types_from.end(); )
+ else
{
- bool remove = false;
- const auto& new_ty = context.get_type(*it);
- if( !new_ty.m_data.is_Infer() )
- {
- remove = check_ivar_poss__fails_bounds(sp, context, ty_l, new_ty);
-
- if( !remove && !allow_unsized )
- {
- if( context.m_resolve.type_is_sized(sp, new_ty) == ::HIR::Compare::Unequal )
- {
- remove = true;
- DEBUG("Remove possibility " << new_ty << " because it isn't Sized");
- }
- }
- }
-
- if( remove ) {
- it = types_from.erase(it);
- }
- else {
- ++it;
- }
+ // Keep
}
- // Eliminate `to` types that can't be coerced from `from` types
- if(types_from.size() > 0)
- for(auto it = types_to.begin(); it != types_to.end(); )
+ it = (remove_option ? possible_tys.erase(it) : it + 1);
+ }
+ DEBUG("possible_tys = " << possible_tys);
+ for(auto it = possible_tys.begin(); it != possible_tys.end(); )
+ {
+ bool remove_option = false;
+ for(const auto& other_opt : possible_tys)
{
- if( it->m_data.is_Infer() ) {
- ++ it;
- continue;
- }
- bool remove = false;
-
- for(const auto& src : types_from)
+ if( &other_opt == &*it )
+ continue ;
+ if( *other_opt.ty == *it->ty )
{
- if( H::cannot_coerce_to(context, *it, src) ) {
- remove = true;
- DEBUG("Remove target type " << *it << " because it cannot be created from a source type");
+ // Potential duplicate
+ // - If the flag set is the same, then it is a duplicate
+ if( other_opt.can_deref == it->can_deref && other_opt.is_pointer == it->is_pointer ) {
+ remove_option = true;
break;
}
- }
-
- if( !remove && !allow_unsized )
- {
- if( context.m_resolve.type_is_sized(sp, *it) == ::HIR::Compare::Unequal )
+ // If not an ivar, AND both are either unsize/pointer AND the deref flags are different
+ if( !it->ty->m_data.is_Infer() && other_opt.is_pointer == it->is_pointer && other_opt.can_deref != it->can_deref )
{
- remove = true;
- DEBUG("Remove possibility " << *it << " because it isn't Sized");
+ DEBUG("Source and destination possibility, picking " << *it->ty);
+ context.equate_types(sp, ty_l, *it->ty);
+ return true;
+ }
+ // - Otherwise, we want to keep the option which doesn't allow dereferencing (remove current
+ // if it's the deref option)
+ if( it->can_deref && other_opt.is_pointer == it->is_pointer ) {
+ remove_option = true;
+ break;
}
- }
-
- if( remove ) {
- it = types_to.erase(it);
- }
- else {
- ++it;
}
}
+ it = (remove_option ? possible_tys.erase(it) : it + 1);
}
-
- // De-duplicate lists (after unification) using strict equality
- H::dedup_type_list_with(types_from, [&](const auto& l, const auto& r) {
- return context.m_ivars.types_equal(l, r) ? DedupKeep::Left : DedupKeep::Both;
- });
- H::dedup_type_list_with(types_to, [&](const auto& l, const auto& r) {
- return context.m_ivars.types_equal(l, r) ? DedupKeep::Left : DedupKeep::Both;
- });
-
- // If there is a common type in both lists, use it
- for(const auto& t1 : types_from)
+ DEBUG("possible_tys = " << possible_tys);
+ // Remove any options that are filled by other options (e.g. `str` and a derferencable String)
+ for(auto it = possible_tys.begin(); it != possible_tys.end(); )
{
- for(const auto& t2 : types_to)
+ bool remove_option = false;
+ if( it->can_deref && !it->ty->m_data.is_Infer() )
{
- if(t1 == t2) {
- DEBUG("- IVar " << ty_l << " = " << t1 << " (present in both lists)");
- context.equate_types(sp, ty_l, t1);
- return true;
- }
- }
- }
-
- // Prefer cases where this type is being created from a known type
- if( types_from.size() == 1 || types_to.size() == 1 ) {
- const char* list_name = (types_from.size() ? "from" : "to");
- const ::HIR::TypeRef& ty_r = (types_from.size() == 1 ? types_from[0] : types_to[0]);
- // Only one possibility
- // - If it would ivar unify, don't if the target has guessing disabled
- if( const auto* e = ty_r.m_data.opt_Infer() )
- {
- if( e->index < context.possible_ivar_vals.size() )
+ DEBUG("> " << *it);
+ // Dereference once before starting the search
+ ::HIR::TypeRef tmp, tmp2;
+ const auto* dty = it->ty;
+ auto src_bty = ::HIR::BorrowType::Shared;
+ if(it->is_pointer)
+ {
+ if( dty->m_data.is_Borrow() )
+ src_bty = dty->m_data.as_Borrow().type;
+ dty = context.m_resolve.autoderef(sp, *dty, tmp);
+ // NOTE: Coercions can also do closure->fn, so deref isn't always possible
+ //ASSERT_BUG(sp, dty, "Pointer (coercion source) that can't dereference - " << *it->ty);
+ }
+ //while( dty && !remove_option && (dty = context.m_resolve.autoderef(sp, *dty, tmp)) )
+ if( dty )
{
- const auto& p = context.possible_ivar_vals[e->index];
- if( honour_disable && (p.force_no_to || p.force_no_from) )
+ for(const auto& other_opt : possible_tys)
{
- DEBUG("- IVar " << ty_l << " ?= " << ty_r << " (single " << list_name << ", rhs disabled)");
- return false;
+ if( &other_opt == &*it )
+ continue ;
+ if( other_opt.ty->m_data.is_Infer() )
+ continue ;
+ DEBUG(" > " << other_opt);
+
+ const auto* oty = other_opt.ty;
+ auto o_bty = ::HIR::BorrowType::Owned;
+ if(other_opt.is_pointer)
+ {
+ if( oty->m_data.is_Borrow() )
+ o_bty = oty->m_data.as_Borrow().type;
+ oty = context.m_resolve.autoderef(sp, *oty, tmp2);
+ //ASSERT_BUG(sp, oty, "Pointer (coercion src/dst) that can't dereference - " << *other_opt.ty);
+ }
+ if( o_bty > src_bty ) // Smaller means less powerful. Converting & -> &mut is invalid
+ {
+ // Borrow types aren't compatible
+ DEBUG("BT " << o_bty << " > " << src_bty);
+ break;
+ }
+ // TODO: Check if unsize is possible from `dty` to `oty`
+ if( oty )
+ {
+ DEBUG(" > " << *dty << " =? " << *oty);
+ auto cmp = check_unsize_tys(context, sp, *oty, *dty, nullptr, false);
+ DEBUG(" = " << cmp);
+ if( cmp == CoerceResult::Equality )
+ {
+ //TODO(sp, "Impossibility for " << *oty << " := " << *dty);
+ }
+ else if( cmp == CoerceResult::Unknown )
+ {
+ }
+ else
+ {
+ remove_option = true;
+ DEBUG("- Remove " << *it << ", can deref to " << other_opt);
+ break;
+ }
+ }
}
}
}
- // If there are from options, AND the to option is an Unsize
- if( types_from.size() > 1 && ivar_ent.types_unsize_to.size() == 1 && ivar_ent.types_coerce_to.size() == 0
- && ::std::any_of(types_from.begin(), types_from.end(), [](const auto&x){ return x.m_data.is_Infer(); }) ) {
- DEBUG("- IVar " << ty_l << " != " << ty_r << " (single " << list_name << ", but was unsize and from has ivars)");
- return false;
+ if( !remove_option && !it->ty->m_data.is_Infer() && check_ivar_poss__fails_bounds(sp, context, ty_l, *it->ty) )
+ {
+ remove_option = true;
+ DEBUG("- Remove " << *it << " due to bounds");
}
+ it = (remove_option ? possible_tys.erase(it) : it + 1);
+ }
+ DEBUG("possible_tys = " << possible_tys);
- DEBUG("- IVar " << ty_l << " = " << ty_r << " (single " << list_name << ")");
- context.equate_types(sp, ty_l, ty_r);
+ if( possible_tys.size() == 1 )
+ {
+ const auto& new_ty = *possible_tys[0].ty;
+ DEBUG("Only " << new_ty << " is an option");
+ context.equate_types(sp, ty_l, new_ty);
+ return true;
+ }
+ // If there's only one non-deref in the list OR there's only one deref in the list
+ if( !honour_disable && ::std::count_if(possible_tys.begin(), possible_tys.end(), [](const PossibleType& pt){ return pt.can_deref; }) == 1 )
+ {
+ auto it = ::std::find_if(possible_tys.begin(), possible_tys.end(), [](const PossibleType& pt){ return pt.can_deref; });
+ const auto& new_ty = *it->ty;
+ DEBUG("Picking " << new_ty << " as the only source [" << possible_tys << "]");
+ context.equate_types(sp, ty_l, new_ty);
+ return true;
+ }
+ if( !honour_disable && ::std::count_if(possible_tys.begin(), possible_tys.end(), [](const PossibleType& pt){ return !pt.can_deref; }) == 1 )
+ {
+ auto it = ::std::find_if(possible_tys.begin(), possible_tys.end(), [](const PossibleType& pt){ return !pt.can_deref; });
+ const auto& new_ty = *it->ty;
+ DEBUG("Picking " << new_ty << " as the only target [" << possible_tys << "]");
+ context.equate_types(sp, ty_l, new_ty);
+ return true;
+ }
+ // If there's multiple possiblilties, we're in fallback mode, AND there's no ivars in the list
+ if( possible_tys.size() > 0 && !honour_disable && !::std::any_of(possible_tys.begin(), possible_tys.end(), [](const PossibleType& pt){ return pt.ty->m_data.is_Infer(); }) )
+ {
+ //::std::sort(possible_tys.begin(), possible_tys.end()); // Sorts ivars to the front
+ const auto& new_ty = *possible_tys.back().ty;
+ DEBUG("Picking " << new_ty << " as an arbitary an option from [" << possible_tys << "]");
+ context.equate_types(sp, ty_l, new_ty);
return true;
}
- else {
- DEBUG("- IVar " << ty_l << " not concretely known {" << types_from << "} and {" << types_to << "}" );
- // If one side is completely unknown, pick the most liberal of the other side
- if( types_to.size() == 0 && types_from.size() > 0 )
+ // If only one bound meets the possible set, use it
+ if( ! possible_tys.empty() )
+ {
+ DEBUG("Checking bounded [" << ivar_ent.bounded << "]");
+ ::std::vector<const ::HIR::TypeRef*> feasable_bounds;
+ for(const auto& new_ty : ivar_ent.bounded)
{
- // Search for the lowest-level source type (e.g. &[T])
- const auto* lowest_type = H::find_lowest_type(context, types_from);
- if( lowest_type )
+ bool failed_a_bound = false;
+ // TODO: Check if this bounded type _cannot_ work with any of the existing bounds
+ // - Don't add to the possiblity list if so
+ for(const auto& opt : possible_tys)
{
- const ::HIR::TypeRef& ty_r = *lowest_type;
- DEBUG("- IVar " << ty_l << " = " << ty_r << " (from, lowest)");
- context.equate_types(sp, ty_l, ty_r);
- return true;
+ if( opt.can_deref ) {
+ const auto* dty = opt.ty;
+
+ DEBUG(" > " << new_ty << " =? " << *dty);
+ auto cmp = check_unsize_tys(context, sp, new_ty, *dty, nullptr, false);
+ DEBUG(" = " << cmp);
+ if( cmp == CoerceResult::Equality ) {
+ failed_a_bound = true;
+ break;
+ }
+ }
+ else {
+ // Destination type, this option must deref to it
+ DEBUG(" > " << *opt.ty << " =? " << new_ty);
+ auto cmp = check_unsize_tys(context, sp, *opt.ty, new_ty, nullptr, false);
+ DEBUG(" = " << cmp);
+ if( cmp == CoerceResult::Equality ) {
+ failed_a_bound = true;
+ break;
+ }
+ }
+ }
+ // TODO: Should this also check check_ivar_poss__fails_bounds
+ if( !failed_a_bound )
+ {
+ feasable_bounds.push_back(&new_ty);
}
}
- else if( types_to.size() > 0 && types_from.size() == 0 )
- {
- // TODO: Get highest-level target type
- }
- else
+ if( feasable_bounds.size() == 1 )
{
+ const auto& new_ty = *feasable_bounds.front();
+ DEBUG("Picking " << new_ty << " as it's the only bound that fits coercions");
+ context.equate_types(sp, ty_l, new_ty);
+ return true;
}
}
- }
-
- return false;
- }
-
- /// Check IVar possibilities, from both coercion/unsizing (which have well-encoded rules) and from trait impls
- bool check_ivar_poss(Context& context, unsigned int i, Context::IVarPossible& ivar_ent, bool honour_disable=true)
- {
- static Span _span;
- const auto& sp = _span;
-
- if( honour_disable && (ivar_ent.force_no_to || ivar_ent.force_no_from) )
- {
- DEBUG(i << ": forced unknown");
- return false;
- }
-
- ::HIR::TypeRef ty_l_ivar;
- ty_l_ivar.m_data.as_Infer().index = i;
- const auto& ty_l = context.m_ivars.get_type(ty_l_ivar);
-
- if( ty_l != ty_l_ivar ) {
- if( ivar_ent.has_rules() )
+ else
{
- DEBUG("- IVar " << i << " had possibilities, but was known to be " << ty_l);
- // Completely clear by reinitialising
- ivar_ent = Context::IVarPossible();
+ // Not checking bounded list, because there's nothing to check
}
- return false;
- }
- if( ! ivar_ent.has_rules() ) {
- // No rules, don't do anything (and don't print)
- DEBUG(i << ": No rules");
- return false;
- }
-
- TRACE_FUNCTION_F(i);
-
-
- if( check_ivar_poss__coercions(context, i, ivar_ent, ty_l, honour_disable) )
- {
- return true;
+ has_no_coerce_posiblities = possible_tys.empty();
}
- if( !ivar_ent.bounded.empty() )
+ if( has_no_coerce_posiblities && !ivar_ent.bounded.empty() )
{
// TODO: Search know possibilties and check if they satisfy the bounds for this ivar
DEBUG("Options: " << ivar_ent.bounded);
unsigned int n_good = 0;
+ unsigned int n_good_ints = 0;
const ::HIR::TypeRef* only_good = nullptr;
for(const auto& new_ty : ivar_ent.bounded)
{
@@ -7144,22 +6913,28 @@ namespace {
n_good ++;
if( !only_good )
only_good = &new_ty;
+
+ if( new_ty.m_data.is_Primitive() )
+ n_good_ints ++;
+
DEBUG("> " << new_ty << " feasible");
}
}
// Picks the first if in fallback mode (which is signalled by `honour_disable` being false)
// - This handles the case where there's multiple valid options (needed for libcompiler_builtins)
- if( (honour_disable ? n_good == 1 : n_good > 0)
- && ivar_ent.types_coerce_from.size() == 0 && ivar_ent.types_coerce_to.size() == 0
- && ivar_ent.types_unsize_from.size() == 0 && ivar_ent.types_unsize_to.size() == 0
- )
+ // TODO: Only pick any if all options are the same class (or just all are integers)
+ if( (honour_disable ? n_good == 1 : n_good > 0) )
+ //if( n_good == 1 || (honour_disable ? false : n_good == n_good_ints) )
{
- DEBUG("Only " << *only_good << " fits current bound sets");
+ if( honour_disable )
+ DEBUG("Only " << *only_good << " fits current bound sets");
+ else
+ DEBUG("Picking " << *only_good << " as first of " << n_good << " options");
// Since it's the only possibility, choose it?
context.equate_types(sp, ty_l, *only_good);
return true;
}
- DEBUG(n_good << " valid options");
+ DEBUG(n_good << " valid options (" << n_good_ints << " primitives)");
}
return false;
@@ -7411,6 +7186,9 @@ void Typecheck_Code_CS(const typeck::ModuleState& ms, t_args& args, const ::HIR:
for(unsigned int i = context.possible_ivar_vals.size(); i --; )
{
if( check_ivar_poss(context, i, context.possible_ivar_vals[i], /*honour_disable=*/false) ) {
+#if 1
+ break;
+#else
static Span sp;
assert( context.possible_ivar_vals[i].has_rules() );
// Disable all metioned ivars in the possibilities
@@ -7442,6 +7220,7 @@ void Typecheck_Code_CS(const typeck::ModuleState& ms, t_args& args, const ::HIR:
context.equate_types_shadow(sp, t, false);
}
}
+#endif
}
else {
//assert( !context.m_ivars.peek_changed() );
diff --git a/src/hir_typeck/helpers.cpp b/src/hir_typeck/helpers.cpp
index 15c61bc7..7e282cb0 100644
--- a/src/hir_typeck/helpers.cpp
+++ b/src/hir_typeck/helpers.cpp
@@ -3651,12 +3651,16 @@ const ::HIR::TypeRef* TraitResolution::autoderef(const Span& sp, const ::HIR::Ty
DEBUG("Deref " << ty << " into " << *e.inner);
return &this->m_ivars.get_type(*e.inner);
)
- // TODO: Just doing `*[1,2,3]` doesn't work, but this is needed to allow `[1,2,3].iter()` to work
+ // HACK?: Just doing `*[1,2,3]` doesn't work, but this is needed to allow `[1,2,3].iter()` to work
else TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Array, e,
DEBUG("Deref " << ty << " into [" << *e.inner << "]");
tmp_type = ::HIR::TypeRef::new_slice( e.inner->clone() );
return &tmp_type;
)
+ // Shortcut, don't look up a Deref impl for primitives or slices
+ else if( ty.m_data.is_Slice() || ty.m_data.is_Primitive() ) {
+ return nullptr;
+ }
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");
@@ -3812,7 +3816,7 @@ const ::HIR::TypeRef* TraitResolution::check_method_receiver(const Span& sp, con
case ::HIR::Function::Receiver::Value:
if( access >= TraitResolution::MethodAccess::Move )
{
- return &ty;
+ return &this->m_ivars.get_type(ty);
}
break;
case ::HIR::Function::Receiver::BorrowOwned:
@@ -3865,7 +3869,7 @@ const ::HIR::TypeRef* TraitResolution::check_method_receiver(const Span& sp, con
};
if( fcn.m_args.front().second .match_test_generics(sp, ty, this->m_ivars.callback_resolve_infer(), cb_getself) ) {
assert(detected_self_ty);
- return detected_self_ty;
+ return &this->m_ivars.get_type(*detected_self_ty);
}
}
return nullptr;