summaryrefslogtreecommitdiff
path: root/src/hir_typeck
diff options
context:
space:
mode:
authorJohn Hodge <tpg@ucc.asn.au>2017-07-16 19:28:32 +0800
committerJohn Hodge <tpg@ucc.asn.au>2017-07-16 19:28:32 +0800
commit20074c81d0385d71c9b91a5f62c4b6be9b444875 (patch)
treecf84c7fb0e3b25b0f6b1b2a3d2abfb709318855a /src/hir_typeck
parentc0688f03d081882dde1e5c8d2be6460cb3df4619 (diff)
downloadmrust-20074c81d0385d71c9b91a5f62c4b6be9b444875.tar.gz
HIR Typecheck - Refactor method name resolution (slower, but more correct)
Diffstat (limited to 'src/hir_typeck')
-rw-r--r--src/hir_typeck/expr_cs.cpp47
-rw-r--r--src/hir_typeck/helpers.cpp574
-rw-r--r--src/hir_typeck/helpers.hpp13
3 files changed, 329 insertions, 305 deletions
diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp
index 66c07e6d..7bd7d2be 100644
--- a/src/hir_typeck/expr_cs.cpp
+++ b/src/hir_typeck/expr_cs.cpp
@@ -2630,53 +2630,6 @@ namespace {
}
else
{
- auto receiver_class = node.m_cache.m_fcn->m_receiver;
- ::HIR::BorrowType bt;
-
- auto& node_ptr = node.m_value;
- auto span = node_ptr->span();
- switch(receiver_class)
- {
- case ::HIR::Function::Receiver::Free:
- BUG(sp, "Method call resolved to a free function - " << node.m_method_path);
- case ::HIR::Function::Receiver::Value:
- // by value - nothing needs to be added
- break;
- case ::HIR::Function::Receiver::BorrowShared: bt = ::HIR::BorrowType::Shared; if(0)
- case ::HIR::Function::Receiver::BorrowUnique: bt = ::HIR::BorrowType::Unique; if(0)
- case ::HIR::Function::Receiver::BorrowOwned: bt = ::HIR::BorrowType::Owned; {
- // - Add correct borrow operation
- auto ty = ::HIR::TypeRef::new_borrow(bt, node_ptr->m_res_type.clone());
- DEBUG("- Ref " << &*node_ptr << " -> " << ty);
- node_ptr = NEWNODE(mv$(ty), span, _Borrow, bt, mv$(node_ptr) );
- } break;
- case ::HIR::Function::Receiver::Box: {
- // - Undo a deref (there must have been one?) and ensure that it leads to a Box<Self>
- // NOTE: Doesn't check deref_count, because this could have been calld as `(*somebox).method()`
- auto* deref_ptr = dynamic_cast< ::HIR::ExprNode_Deref*>(&*node_ptr);
- ASSERT_BUG(sp, deref_ptr != nullptr, "Calling Box receiver method but no deref happened");
- node_ptr = mv$(deref_ptr->m_value);
- DEBUG("- Undo deref " << deref_ptr << " -> " << node_ptr->m_res_type);
-
- // Triple-check that the input to the above Deref was a Box (lang="owned_box")
- const auto& box_ty = this->context.get_type(node_ptr->m_res_type);
- TU_IFLET(::HIR::TypeRef::Data, box_ty.m_data, Path, e,
- TU_IFLET(::HIR::Path::Data, e.path.m_data, Generic, pe,
- if( pe.m_path == context.m_lang_Box ) {
- }
- else {
- ERROR(sp, E0000, "Calling Box receiver method on non-box - " << box_ty);
- }
- )
- else {
- ERROR(sp, E0000, "Calling Box receiver method on non-box - " << box_ty);
- }
- )
- else {
- ERROR(sp, E0000, "Calling Box receiver method on non-box - " << box_ty);
- }
- } break;
- }
}
// Equate the type for `self` (to ensure that Self's type params infer correctly)
diff --git a/src/hir_typeck/helpers.cpp b/src/hir_typeck/helpers.cpp
index 504ff454..8791be72 100644
--- a/src/hir_typeck/helpers.cpp
+++ b/src/hir_typeck/helpers.cpp
@@ -2843,38 +2843,26 @@ bool TraitResolution::find_trait_impls_crate(const Span& sp,
}
namespace {
- bool trait_contains_method_(const ::HIR::Trait& trait_ptr, const ::std::string& name, TraitResolution::AllowedReceivers ar)
+ bool trait_contains_method_inner(const ::HIR::Trait& trait_ptr, const ::std::string& name, ::HIR::Function::Receiver& receiver)
{
auto it = trait_ptr.m_values.find(name);
if( it != trait_ptr.m_values.end() )
{
if( it->second.is_Function() ) {
const auto& v = it->second.as_Function();
- switch(v.m_receiver)
- {
- case ::HIR::Function::Receiver::Free:
- break;
- case ::HIR::Function::Receiver::Value:
- if( ar != TraitResolution::AllowedReceivers::All && ar != TraitResolution::AllowedReceivers::Value )
- break;
- if(0)
- case ::HIR::Function::Receiver::Box:
- if( ar != TraitResolution::AllowedReceivers::Box )
- break;
- default:
- return true;
- }
+ receiver = v.m_receiver;
+ return true;
}
}
return false;
}
}
-bool TraitResolution::trait_contains_method(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::HIR::TypeRef& self, const ::std::string& name, AllowedReceivers ar, ::HIR::GenericPath& out_path) const
+bool TraitResolution::trait_contains_method(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::HIR::TypeRef& self, const ::std::string& name, ::HIR::Function::Receiver& out_receiver, ::HIR::GenericPath& out_path) const
{
- TRACE_FUNCTION_FR("trait_path=" << trait_path << ",name=" << name << ",ar=" << ar, out_path);
+ TRACE_FUNCTION_FR("trait_path=" << trait_path << ",name=" << name, out_path);
- if( trait_contains_method_(trait_ptr, name, ar) )
+ if( trait_contains_method_inner(trait_ptr, name, out_receiver) )
{
out_path = trait_path.clone();
return true;
@@ -2883,7 +2871,7 @@ bool TraitResolution::trait_contains_method(const Span& sp, const ::HIR::Generic
auto monomorph_cb = monomorphise_type_get_cb(sp, &self, &trait_path.m_params, nullptr);
for(const auto& st : trait_ptr.m_all_parent_traits)
{
- if( trait_contains_method_(*st.m_trait_ptr, name, ar) )
+ if( trait_contains_method_inner(*st.m_trait_ptr, name, out_receiver) )
{
out_path.m_path = st.m_path.m_path;
out_path.m_params = monomorphise_path_params_with(sp, st.m_path.m_params, monomorph_cb, false);
@@ -3387,143 +3375,86 @@ const ::HIR::TypeRef* TraitResolution::autoderef(const Span& sp, const ::HIR::Ty
}
unsigned int TraitResolution::autoderef_find_method(const Span& sp, const HIR::t_trait_list& traits, const ::std::vector<unsigned>& ivars, const ::HIR::TypeRef& top_ty, const ::std::string& method_name, /* Out -> */::HIR::Path& fcn_path, AutoderefBorrow& borrow) const
{
+ TRACE_FUNCTION_F("{" << top_ty << "}." << method_name);
unsigned int deref_count = 0;
::HIR::TypeRef tmp_type; // Temporary type used for handling Deref
const auto& top_ty_r = this->m_ivars.get_type(top_ty);
const auto* current_ty = &top_ty_r;
- bool unconditional_allow_move = true;
-
- // If the top is a borrow, search dereferenced first.
- TU_IFLET(::HIR::TypeRef::Data, top_ty_r.m_data, Borrow, e,
- if( e.type == ::HIR::BorrowType::Owned ) {
- // Can move, because we have &move
- }
- // TODO: What if this returns Fuzzy?
- else if( this->type_is_copy(sp, *e.inner) == ::HIR::Compare::Equal ) {
- // Can move, because it's Copy
- }
- else {
- unconditional_allow_move = false;
- }
- current_ty = &*e.inner;
- deref_count += 1;
- )
- // TODO: This appears to dereference a &mut to call a `self: Self` method, where it should use the trait impl on &mut Self.
- // - Shouldn't deref to get a by-value receiver.// unless it's via a &move.
- bool can_move = unconditional_allow_move;
- do {
- // TODO: Update `unconditional_allow_move` based on the current type.
+ // Correct algorithm:
+ // - Find any available method with a receiver type of `T`
+ // - If no, try &T
+ // - If no, try &mut T
+ // - If no, try &move T
+ // - If no, dereference T and try again
+ auto cur_access = MethodAccess::Move; // Assume that the input value is movable
+ do
+ {
const auto& ty = this->m_ivars.get_type(*current_ty);
- if( type_is_unbounded_infer(ty) ) {
- DEBUG("- Ivar, pausing");
+ auto should_pause = [](const auto& ty)->bool {
+ if( type_is_unbounded_infer(ty) ) {
+ DEBUG("- Ivar" << ty << ", pausing");
+ return true;
+ }
+ if(ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Unbound()) {
+ DEBUG("- Unbound type path " << ty << ", pausing");
+ return true;
+ }
+ return false;
+ };
+ if( should_pause(ty) ) {
return ~0u;
}
- if(ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Unbound()) {
- DEBUG("- Unbound type path " << ty << ", pausing");
+ if( ty.m_data.is_Borrow() && should_pause( this->m_ivars.get_type(*ty.m_data.as_Borrow().inner) ) ) {
return ~0u;
}
- DEBUG("ty = " << ty);
+ // TODO: Pause on Box<_>
+ DEBUG(deref_count << ": " << ty);
- auto allowed_receivers = (can_move ? AllowedReceivers::All : AllowedReceivers::AnyBorrow);
- if( this->find_method(sp, traits, ivars, ty, method_name, allowed_receivers, fcn_path) ) {
- DEBUG("FOUND " << deref_count << ", fcn_path = " << fcn_path);
+ // Non-referenced
+ if( this->find_method(sp, traits, ivars, ty, method_name, cur_access, fcn_path) )
+ {
borrow = AutoderefBorrow::None;
return deref_count;
}
- // Handle `self: Box<Self>` methods by detecting m_lang_Box and searchig for box receiver methods
- // - Only possible if we can move the receiver
- if( allowed_receivers == AllowedReceivers::All )
- {
- if( const auto* typ = this->type_is_owned_box(sp, ty) )
- {
- if( ! type_is_unbounded_infer(*typ) )
- {
- // Search for methods on the inner type with Receiver::Box
- if( this->find_method(sp, traits, ivars, *typ, method_name, AllowedReceivers::Box, fcn_path) ) {
- DEBUG("FOUND Box, fcn_path = " << fcn_path);
- borrow = AutoderefBorrow::None;
- return deref_count+1;
- }
- }
- }
- }
-
-
+ // Auto-ref
auto borrow_ty = ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, ty.clone());
- if( find_method(sp, traits, ivars, borrow_ty, method_name, AllowedReceivers::Value, fcn_path) ) {
- DEBUG("FOUND &*" << deref_count << ", fcn_path = " << fcn_path);
+ if( this->find_method(sp, traits, ivars, borrow_ty, method_name, MethodAccess::Move, fcn_path) )
+ {
+ DEBUG("FOUND & *{" << deref_count << "}, fcn_path = " << fcn_path);
borrow = AutoderefBorrow::Shared;
return deref_count;
}
borrow_ty.m_data.as_Borrow().type = ::HIR::BorrowType::Unique;
- if( find_method(sp, traits, ivars, borrow_ty, method_name, AllowedReceivers::Value, fcn_path) ) {
- DEBUG("FOUND &mut*" << deref_count << ", fcn_path = " << fcn_path);
+ if( cur_access >= MethodAccess::Unique && this->find_method(sp, traits, ivars, borrow_ty, method_name, MethodAccess::Move, fcn_path) )
+ {
+ DEBUG("FOUND &mut *{" << deref_count << "}, fcn_path = " << fcn_path);
borrow = AutoderefBorrow::Unique;
return deref_count;
}
borrow_ty.m_data.as_Borrow().type = ::HIR::BorrowType::Owned;
- if( find_method(sp, traits, ivars, borrow_ty, method_name, AllowedReceivers::Value, fcn_path) ) {
- DEBUG("FOUND &mut*" << deref_count << ", fcn_path = " << fcn_path);
+ if( cur_access >= MethodAccess::Move && this->find_method(sp, traits, ivars, borrow_ty, method_name, MethodAccess::Move, fcn_path) )
+ {
+ DEBUG("FOUND &move *{" << deref_count << "}, fcn_path = " << fcn_path);
borrow = AutoderefBorrow::Owned;
return deref_count;
}
- // 3. Dereference and try again
+ // Auto-dereference
deref_count += 1;
if( const auto* typ = this->type_is_owned_box(sp, ty) )
{
+ // `cur_access` can stay as-is (Box can be moved out of)
current_ty = typ;
}
else
{
+ // TODO: Update `cur_access` based on the avaliable Deref impls
current_ty = this->autoderef(sp, ty, tmp_type);
- if( current_ty )
- can_move = this->type_is_copy(sp, *current_ty) == ::HIR::Compare::Equal;
- }
- } while( current_ty );
-
- // If the top is a borrow, search for methods on &/&mut
- TU_IFLET(::HIR::TypeRef::Data, top_ty_r.m_data, Borrow, e,
- const auto& ty = top_ty_r;
-
- if( find_method(sp, traits, ivars, ty, method_name, AllowedReceivers::All, fcn_path) ) {
- DEBUG("FOUND " << 0 << ", fcn_path = " << fcn_path);
- borrow = AutoderefBorrow::None;
- return 0;
}
- )
-
- // If there are ivars within the type, don't error (yet)
- if( this->m_ivars.type_contains_ivars(top_ty) )
- {
- DEBUG("- Contains ivars, pausing");
- return ~0u;
- }
-
- // Insert a single reference and try again (only allowing by-value methods), returning a magic value (e.g. ~1u)
- // - Required for calling `(self[..]: str).into_searcher(haystack)` - Which invokes `<&str as Pattern>::into_searcher(&self[..], haystack)`
- // - Have to do several tries, each with different borrow classes.
- auto borrow_ty = ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, top_ty.clone());
- if( find_method(sp, traits, ivars, borrow_ty, method_name, AllowedReceivers::Value, fcn_path) ) {
- DEBUG("FOUND &, fcn_path = " << fcn_path);
- borrow = AutoderefBorrow::Shared;
- return 0;
- }
- borrow_ty.m_data.as_Borrow().type = ::HIR::BorrowType::Unique;
- if( find_method(sp, traits, ivars, borrow_ty, method_name, AllowedReceivers::Value, fcn_path) ) {
- DEBUG("FOUND &mut, fcn_path = " << fcn_path);
- borrow = AutoderefBorrow::Unique;
- return 0;
- }
- borrow_ty.m_data.as_Borrow().type = ::HIR::BorrowType::Owned;
- if( find_method(sp, traits, ivars, borrow_ty, method_name, AllowedReceivers::Value, fcn_path) ) {
- DEBUG("FOUND &move, fcn_path = " << fcn_path);
- borrow = AutoderefBorrow::Owned;
- return 0;
- }
+ } while(current_ty);
// Dereference failed! This is a hard error (hitting _ is checked above and returns ~0)
//this->m_ivars.dump();
@@ -3536,20 +3467,101 @@ unsigned int TraitResolution::autoderef_find_method(const Span& sp, const HIR::t
{
case TraitResolution::AllowedReceivers::All: os << "All"; break;
case TraitResolution::AllowedReceivers::AnyBorrow: os << "AnyBorrow"; break;
+ case TraitResolution::AllowedReceivers::SharedBorrow:os << "SharedBorrow"; break;
case TraitResolution::AllowedReceivers::Value: os << "Value"; break;
case TraitResolution::AllowedReceivers::Box: os << "Box"; break;
}
return os;
}
+::std::ostream& operator<<(::std::ostream& os, const TraitResolution::MethodAccess& x)
+{
+ switch(x)
+ {
+ case TraitResolution::MethodAccess::Shared: os << "Shared"; break;
+ case TraitResolution::MethodAccess::Unique: os << "Unique"; break;
+ case TraitResolution::MethodAccess::Move: os << "Move"; break;
+ }
+ return os;
+}
+
+// Checks that a given real receiver type matches a desired receiver type (with the correct access)
+// Returns the pointer to the `Self` type, or nullptr if there's a mismatch
+const ::HIR::TypeRef* TraitResolution::check_method_receiver(const Span& sp, ::HIR::Function::Receiver receiver, const ::HIR::TypeRef& ty, TraitResolution::MethodAccess access) const
+{
+ switch(receiver)
+ {
+ case ::HIR::Function::Receiver::Free:
+ // Free functions are never usable
+ return nullptr;
+ case ::HIR::Function::Receiver::Value:
+ if( access >= TraitResolution::MethodAccess::Move )
+ {
+ return &ty;
+ }
+ break;
+ case ::HIR::Function::Receiver::BorrowOwned:
+ if( !ty.m_data.is_Borrow() )
+ ;
+ else if( ty.m_data.as_Borrow().type != ::HIR::BorrowType::Owned )
+ ;
+ else if( access < TraitResolution::MethodAccess::Move )
+ ;
+ else
+ {
+ return &this->m_ivars.get_type(*ty.m_data.as_Borrow().inner);
+ }
+ break;
+ case ::HIR::Function::Receiver::BorrowUnique:
+ if( !ty.m_data.is_Borrow() )
+ ;
+ else if( ty.m_data.as_Borrow().type != ::HIR::BorrowType::Unique )
+ ;
+ else if( access < TraitResolution::MethodAccess::Unique )
+ ;
+ else
+ {
+ return &this->m_ivars.get_type(*ty.m_data.as_Borrow().inner);
+ }
+ break;
+ case ::HIR::Function::Receiver::BorrowShared:
+ if( !ty.m_data.is_Borrow() )
+ ;
+ else if( ty.m_data.as_Borrow().type != ::HIR::BorrowType::Shared )
+ ;
+ else if( access < TraitResolution::MethodAccess::Shared )
+ ;
+ else
+ {
+ return &this->m_ivars.get_type(*ty.m_data.as_Borrow().inner);
+ }
+ break;
+ case ::HIR::Function::Receiver::Box:
+ if(const auto* ity = this->type_is_owned_box(sp, ty))
+ {
+ if( access < TraitResolution::MethodAccess::Move )
+ {
+ }
+ else
+ {
+ return &this->m_ivars.get_type(*ity);
+ }
+ }
+ break;
+ }
+ return nullptr;
+}
bool TraitResolution::find_method(
const Span& sp,
const HIR::t_trait_list& traits, const ::std::vector<unsigned>& ivars,
- const ::HIR::TypeRef& ty, const ::std::string& method_name, AllowedReceivers ar,
+ const ::HIR::TypeRef& ty, const ::std::string& method_name, MethodAccess access,
/* Out -> */::HIR::Path& fcn_path) const
{
- TRACE_FUNCTION_F("ty=" << ty << ", name=" << method_name << ", ar=" << ar);
+ TRACE_FUNCTION_F("ty=" << ty << ", name=" << method_name << ", access=" << access);
+
// 1. Search generic bounds for a match
+ // - If there is a bound on the receiver, then that bound is usable no-matter what
+ DEBUG("> Bounds");
const ::HIR::GenericParams* v[2] = { m_item_params, m_impl_params };
for(auto p : v)
{
@@ -3557,85 +3569,135 @@ bool TraitResolution::find_method(
for(const auto& b : p->m_bounds)
{
TU_IFLET(::HIR::GenericBound, b, TraitBound, e,
- // TODO: Do a fuzzy match here?
- if( e.type != ty )
- continue ;
- // - Bound's type matches, check if the bounded trait has the method we're searching for
- DEBUG("Bound `" << e.type << " : " << e.trait.m_path << "` - Type match " << ty);
- ::HIR::GenericPath final_trait_path;
assert(e.trait.m_trait_ptr);
- if( !this->trait_contains_method(sp, e.trait.m_path, *e.trait.m_trait_ptr, ty, method_name, ar, final_trait_path) ) {
+ // 1. Find the named method in the trait.
+ ::HIR::GenericPath final_trait_path;
+ ::HIR::Function::Receiver receiver;
+ if( !this->trait_contains_method(sp, e.trait.m_path, *e.trait.m_trait_ptr, e.type, method_name, receiver, final_trait_path) ) {
DEBUG("- Method '" << method_name << "' missing");
continue ;
}
DEBUG("- Found trait " << final_trait_path);
- // TODO: Re-monomorphise final trait using `ty`?
- // - Could collide with legitimate uses of `Self`
- // Found the method, return the UFCS path for it
- fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
- box$( ty.clone() ),
- mv$(final_trait_path),
- method_name,
- {}
- }) );
- return true;
+ // 2. Compare the receiver of the above to this type and the bound.
+ if(const auto* self_ty = check_method_receiver(sp, receiver, ty, access))
+ {
+ // TODO: Do a fuzzy match here?
+ if( *self_ty == e.type )
+ {
+ // TODO: Re-monomorphise final trait using `ty`?
+ // - Could collide with legitimate uses of `Self`
+
+ // Found the method, return the UFCS path for it
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
+ box$( self_ty->clone() ),
+ mv$(final_trait_path),
+ method_name,
+ {}
+ }) );
+ return true;
+ }
+ else
+ {
+ DEBUG("> Type mismatch - " << *self_ty << " != " << e.type);
+ }
+ }
+ else
+ {
+ DEBUG("> Receiver mismatch");
+ }
)
}
}
- TU_IFLET(::HIR::TypeRef::Data, ty.m_data, TraitObject, e,
- // TODO: This _Should_ be set, but almost needs a pass?
- //assert( e.m_trait.m_trait_ptr );
- //const auto& trait = *e.m_trait.m_trait_ptr;
- const auto& trait = this->m_crate.get_trait_by_path(sp, e.m_trait.m_path.m_path);
+ auto get_inner_type = [this,sp](const ::HIR::TypeRef& ty, ::std::function<bool(const ::HIR::TypeRef&)> cb)->const ::HIR::TypeRef* {
+ if( cb(ty) ) {
+ return &ty;
+ }
+ else if( ty.m_data.is_Borrow() ) {
+ const auto& ity = this->m_ivars.get_type(*ty.m_data.as_Borrow().inner);
+ if( cb(ity) ) {
+ return &ity;
+ }
+ else {
+ return nullptr;
+ }
+ }
+ else {
+ auto tp = this->type_is_owned_box(sp, ty);
+ if( tp && cb(*tp) ) {
+ return tp;
+ }
+ else {
+ return nullptr;
+ }
+ }
+ };
+ DEBUG("> Special cases");
+ // 2. If the type is a trait object, search for methods on that trait object
+ // - NOTE: This isnt mutually exclusive with the below set (an inherent impl of `(Trait)` is valid)
+ if( const auto* ityp = get_inner_type(ty, [](const auto& t){ return t.m_data.is_TraitObject(); }) )
+ {
+ const auto& e = ityp->m_data.as_TraitObject();
+ const auto& trait = this->m_crate.get_trait_by_path(sp, e.m_trait.m_path.m_path);
::HIR::GenericPath final_trait_path;
- if( this->trait_contains_method(sp, e.m_trait.m_path, trait, ::HIR::TypeRef("Self", 0xFFFF), method_name, ar, final_trait_path) )
+ ::HIR::Function::Receiver receiver;
+ if( this->trait_contains_method(sp, e.m_trait.m_path, trait, ::HIR::TypeRef("Self", 0xFFFF), method_name, receiver, final_trait_path) )
{
DEBUG("- Found trait " << final_trait_path);
-
- fcn_path = ::HIR::Path( ::HIR::Path::Data::Data_UfcsKnown({
- box$( ty.clone() ),
- mv$(final_trait_path),
- method_name,
- {}
- }) );
- return true;
+ // - If the receiver is valid, then it's correct (no need to check the type again)
+ if(const auto* self_ty_p = check_method_receiver(sp, receiver, ty, access))
+ {
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::Data_UfcsKnown({
+ box$( self_ty_p->clone() ),
+ mv$(final_trait_path),
+ method_name,
+ {}
+ }) );
+ return true;
+ }
}
- )
+ }
- // Erased type - `impl Trait`
- TU_IFLET(::HIR::TypeRef::Data, ty.m_data, ErasedType, e,
+ // 3. Mutually exclusive searches
+ // - Erased type - `impl Trait`
+ if( const auto* ityp = get_inner_type(ty, [](const auto& t){ return t.m_data.is_ErasedType(); }) )
+ {
+ const auto& e = ityp->m_data.as_ErasedType();
for(const auto& trait_path : e.m_traits)
{
const auto& trait = this->m_crate.get_trait_by_path(sp, trait_path.m_path.m_path);
::HIR::GenericPath final_trait_path;
- if( this->trait_contains_method(sp, trait_path.m_path, trait, ::HIR::TypeRef("Self", 0xFFFF), method_name, ar, final_trait_path) )
+ ::HIR::Function::Receiver receiver;
+ if( this->trait_contains_method(sp, trait_path.m_path, trait, ::HIR::TypeRef("Self", 0xFFFF), method_name, receiver, final_trait_path) )
{
DEBUG("- Found trait " << final_trait_path);
- fcn_path = ::HIR::Path( ::HIR::Path::Data::Data_UfcsKnown({
- box$( ty.clone() ),
- mv$(final_trait_path),
- method_name,
- {}
- }) );
- return true;
+ if(const auto* self_ty_p = check_method_receiver(sp, receiver, ty, access))
+ {
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::Data_UfcsKnown({
+ box$( self_ty_p->clone() ),
+ mv$(final_trait_path),
+ method_name,
+ {}
+ }) );
+ return true;
+ }
}
}
- )
-
- // Trait object - `(Trait)`
- TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e,
- // No match, keep trying.
- )
- else if( ty.m_data.is_Path() && ty.m_data.as_Path().path.m_data.is_UfcsKnown() )
+ }
+ // Generics: Nothing except the bounds (Which have already been checked)
+ else if( get_inner_type(ty, [](const auto& t){ return t.m_data.is_Generic(); }) )
{
- const auto& e = ty.m_data.as_Path().path.m_data.as_UfcsKnown();
+ }
+ // UfcsKnown paths: Can have trait bounds added by the definer
+ else if( const auto* ityp = get_inner_type(ty, [](const auto& t){ return t.m_data.is_Path() && t.m_data.as_Path().path.m_data.is_UfcsKnown(); }) )
+ {
+ const auto& e = ityp->m_data.as_Path().path.m_data.as_UfcsKnown();
DEBUG("UfcsKnown - Search associated type bounds in trait - " << e.trait);
// UFCS known - Assuming that it's reached the maximum resolvable level (i.e. a type within is generic), search for trait bounds on the type
@@ -3667,23 +3729,27 @@ bool TraitResolution::find_method(
{
ASSERT_BUG(sp, bound.m_trait_ptr, "Pointer to trait " << bound.m_path << " not set in " << e.trait.m_path);
::HIR::GenericPath final_trait_path;
- if( !this->trait_contains_method(sp, bound.m_path, *bound.m_trait_ptr, ::HIR::TypeRef("Self", 0xFFFF), method_name, ar, final_trait_path) )
+ ::HIR::Function::Receiver receiver;
+ if( !this->trait_contains_method(sp, bound.m_path, *bound.m_trait_ptr, ::HIR::TypeRef("Self", 0xFFFF), method_name, receiver, final_trait_path) )
continue ;
DEBUG("- Found trait " << final_trait_path);
- if( monomorphise_pathparams_needed(final_trait_path.m_params) ) {
- final_trait_path.m_params = monomorphise_path_params_with(sp, final_trait_path.m_params, monomorph_cb, false);
- DEBUG("- Monomorph to " << final_trait_path);
- }
+ if(const auto* self_ty_p = check_method_receiver(sp, receiver, ty, access))
+ {
+ if( monomorphise_pathparams_needed(final_trait_path.m_params) ) {
+ final_trait_path.m_params = monomorphise_path_params_with(sp, final_trait_path.m_params, monomorph_cb, false);
+ DEBUG("- Monomorph to " << final_trait_path);
+ }
- // Found the method, return the UFCS path for it
- fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
- box$( ty.clone() ),
- mv$(final_trait_path),
- method_name,
- {}
- }) );
- return true;
+ // Found the method, return the UFCS path for it
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
+ box$( self_ty_p->clone() ),
+ mv$(final_trait_path),
+ method_name,
+ {}
+ }) );
+ return true;
+ }
}
// Search `<Self as Trait>::Name` bounds on the trait itself
@@ -3703,101 +3769,96 @@ bool TraitResolution::find_method(
if( be_type_pe.item != e.item )
continue ;
+ // Found such a bound, now to test if it is useful
+
::HIR::GenericPath final_trait_path;
- if( !this->trait_contains_method(sp, be.trait.m_path, *be.trait.m_trait_ptr, ::HIR::TypeRef("Self", 0xFFFF), method_name, ar, final_trait_path) )
+ ::HIR::Function::Receiver receiver;
+ if( !this->trait_contains_method(sp, be.trait.m_path, *be.trait.m_trait_ptr, ::HIR::TypeRef("Self", 0xFFFF), method_name, receiver, final_trait_path) )
continue ;
DEBUG("- Found trait " << final_trait_path);
- if( monomorphise_pathparams_needed(final_trait_path.m_params) ) {
- final_trait_path.m_params = monomorphise_path_params_with(sp, final_trait_path.m_params, monomorph_cb, false);
- DEBUG("- Monomorph to " << final_trait_path);
- }
+ if(const auto* self_ty_p = check_method_receiver(sp, receiver, ty, access))
+ {
+ if( monomorphise_pathparams_needed(final_trait_path.m_params) ) {
+ final_trait_path.m_params = monomorphise_path_params_with(sp, final_trait_path.m_params, monomorph_cb, false);
+ DEBUG("- Monomorph to " << final_trait_path);
+ }
- // Found the method, return the UFCS path for it
- fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
- box$( ty.clone() ),
- mv$(final_trait_path),
- method_name,
- {}
- }) );
- return true;
+ // Found the method, return the UFCS path for it
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
+ box$( self_ty_p->clone() ),
+ mv$(final_trait_path),
+ method_name,
+ {}
+ }) );
+ return true;
+ }
}
}
- else {
- // 2. Search for inherent methods
- bool rv = m_crate.find_type_impls(ty, m_ivars.callback_resolve_infer(), [&](const auto& impl) {
+ else
+ {
+ }
+
+ // 4. Search for inherent methods
+ DEBUG("> Inherent methods");
+ {
+ const ::HIR::TypeRef* cur_check_ty = &ty;
+ auto find_type_impls_cb = [&](const auto& impl) {
// TODO: Should this take into account the actual suitability of this method? Or just that the name exists?
// - If this impl matches fuzzily, it may not actually match
auto it = impl.m_methods.find( method_name );
if( it == impl.m_methods.end() )
return false ;
const ::HIR::Function& fcn = it->second.data;
- switch(fcn.m_receiver)
+ if( const auto* self_ty_p = this->check_method_receiver(sp, fcn.m_receiver, ty, access) )
{
- case ::HIR::Function::Receiver::Free:
- break;
- case ::HIR::Function::Receiver::Value:
- if( ar == AllowedReceivers::AnyBorrow )
- break;
- if( 0 )
- case ::HIR::Function::Receiver::Box:
- if( ar != AllowedReceivers::Box )
- break;
- if(0)
- default:
- if( ar == AllowedReceivers::Value || ar == AllowedReceivers::Box )
- break;
- DEBUG("Matching `impl" << impl.m_params.fmt_args() << " " << impl.m_type << "`"/* << " - " << top_ty*/);
- fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsInherent({
- box$(ty.clone()),
- method_name,
- {}
- }) );
- return true;
+ DEBUG("Found `impl" << impl.m_params.fmt_args() << " " << impl.m_type << "` fn " << method_name/* << " - " << top_ty*/);
+ if( *self_ty_p == *cur_check_ty )
+ {
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsInherent({
+ box$(self_ty_p->clone()),
+ method_name,
+ {}
+ }) );
+ return true;
+ }
}
+ DEBUG("[find_method] Method was present in `impl" << impl.m_params.fmt_args() << " " << impl.m_type << "` but receiver mismatched");
return false;
- });
- if( rv ) {
+ };
+ if( m_crate.find_type_impls(ty, m_ivars.callback_resolve_infer(), find_type_impls_cb) )
+ {
+ return true;
+ }
+ cur_check_ty = (ty.m_data.is_Borrow() ? &*ty.m_data.as_Borrow().inner : nullptr);
+ if( cur_check_ty && m_crate.find_type_impls(*cur_check_ty, m_ivars.callback_resolve_infer(), find_type_impls_cb) )
+ {
+ return true;
+ }
+ cur_check_ty = this->type_is_owned_box(sp, ty);
+ if( cur_check_ty && m_crate.find_type_impls(*cur_check_ty, m_ivars.callback_resolve_infer(), find_type_impls_cb) )
+ {
return true;
}
}
- // 3. Search for trait methods (using currently in-scope traits)
+ // 5. Search for trait methods (using currently in-scope traits)
+ DEBUG("> Trait methods");
for(const auto& trait_ref : ::reverse(traits))
{
if( trait_ref.first == nullptr )
break;
- DEBUG("Search " << *trait_ref.first);
-
- //::HIR::GenericPath final_trait_path;
- //if( !this->trait_contains_method(sp, *trait_ref.first, *trait_ref.second, method_name, final_trait_path) )
- // continue ;
- //DEBUG("- Found trait " << final_trait_path);
- // TODO: Shouldn't this use trait_contains_method?
- // TODO: Search supertraits too
- auto it = trait_ref.second->m_values.find(method_name);
- if( it == trait_ref.second->m_values.end() )
- continue ;
- if( !it->second.is_Function() )
+ ::HIR::GenericPath final_trait_path;
+ ::HIR::Function::Receiver receiver;
+ if( !this->trait_contains_method(sp, *trait_ref.first, *trait_ref.second, ::HIR::TypeRef("Self", 0xFFFF), method_name, receiver, final_trait_path) )
continue ;
- const auto& v = it->second.as_Function();
- switch(v.m_receiver)
+ DEBUG("- Found trait " << final_trait_path);
+
+ if( const auto* self_ty_p = check_method_receiver(sp, receiver, ty, access) )
{
- case ::HIR::Function::Receiver::Free:
- break;
- case ::HIR::Function::Receiver::Value:
- if( ar == AllowedReceivers::AnyBorrow )
- break;
- if(0)
- case ::HIR::Function::Receiver::Box:
- if( ar != AllowedReceivers::Box )
- break;
- if(0)
- default:
- if( ar == AllowedReceivers::Value || ar == AllowedReceivers::Box || (v.m_receiver == ::HIR::Function::Receiver::Box && ar != AllowedReceivers::AnyBorrow) )
- break;
- DEBUG("Search for impl of " << *trait_ref.first);
+ const auto& self_ty = *self_ty_p;
+ DEBUG("Search for impl of " << *trait_ref.first << " for " << self_ty);
// Use the set of ivars we were given to populate the trait parameters
unsigned int n_params = trait_ref.second->m_params.m_types.size();
@@ -3809,18 +3870,19 @@ bool TraitResolution::find_method(
ASSERT_BUG(sp, m_ivars.get_type( trait_params.m_types.back() ).m_data.as_Infer().index == ivars[i], "A method selection ivar was bound");
}
- //if( find_trait_impls(sp, *trait_ref.first, trait_params, ty, [](auto , auto ) { return true; }) ) {
- if( find_trait_impls_crate(sp, *trait_ref.first, &trait_params, ty, [](auto , auto ) { return true; }) ) {
- DEBUG("Found trait impl " << *trait_ref.first << trait_params << " for " << ty << " ("<<m_ivars.fmt_type(ty)<<")");
+ // TODO: Re-monomorphise the trait path!
+
+ //if( find_trait_impls(sp, *trait_ref.first, trait_params, self_ty, [](auto , auto ) { return true; }) ) {
+ if( find_trait_impls_crate(sp, *trait_ref.first, &trait_params, self_ty, [](auto , auto ) { return true; }) ) {
+ DEBUG("Found trait impl " << *trait_ref.first << trait_params << " for " << self_ty << " ("<<m_ivars.fmt_type(self_ty)<<")");
fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
- box$( ty.clone() ),
+ box$( self_ty.clone() ),
::HIR::GenericPath( *trait_ref.first, mv$(trait_params) ),
method_name,
{}
}) );
return true;
}
- break;
}
}
diff --git a/src/hir_typeck/helpers.hpp b/src/hir_typeck/helpers.hpp
index d715905c..ab0ea93d 100644
--- a/src/hir_typeck/helpers.hpp
+++ b/src/hir_typeck/helpers.hpp
@@ -252,17 +252,26 @@ public:
bool find_field(const Span& sp, const ::HIR::TypeRef& ty, const ::std::string& name, /* Out -> */::HIR::TypeRef& field_type) const;
+ enum class MethodAccess {
+ Shared,
+ Unique,
+ Move,
+ };
+private:
+ const ::HIR::TypeRef* check_method_receiver(const Span& sp, ::HIR::Function::Receiver receiver, const ::HIR::TypeRef& ty, TraitResolution::MethodAccess access) const;
+public:
enum class AllowedReceivers {
All,
AnyBorrow,
+ SharedBorrow,
Value,
Box,
};
friend ::std::ostream& operator<<(::std::ostream& os, const AllowedReceivers& x);
- bool find_method(const Span& sp, const HIR::t_trait_list& traits, const ::std::vector<unsigned>& ivars, const ::HIR::TypeRef& ty, const ::std::string& method_name, AllowedReceivers ar, /* Out -> */::HIR::Path& fcn_path) const;
+ bool find_method(const Span& sp, const HIR::t_trait_list& traits, const ::std::vector<unsigned>& ivars, const ::HIR::TypeRef& ty, const ::std::string& method_name, MethodAccess access, /* Out -> */::HIR::Path& fcn_path) const;
/// Locates a named method in a trait, and returns the path of the trait that contains it (with fixed parameters)
- bool trait_contains_method(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::HIR::TypeRef& self, const ::std::string& name, AllowedReceivers ar, ::HIR::GenericPath& out_path) const;
+ bool trait_contains_method(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::HIR::TypeRef& self, const ::std::string& name, ::HIR::Function::Receiver& out_receiver, ::HIR::GenericPath& out_path) const;
bool trait_contains_type(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::std::string& name, ::HIR::GenericPath& out_path) const;
::HIR::Compare type_is_sized(const Span& sp, const ::HIR::TypeRef& ty) const;