diff options
| author | John Hodge <tpg@mutabah.net> | 2016-07-31 15:40:13 +0800 | 
|---|---|---|
| committer | John Hodge <tpg@mutabah.net> | 2016-07-31 15:40:13 +0800 | 
| commit | d37c27292945148d53108fc876cff3371c53404c (patch) | |
| tree | 29c75f7d3dd9ffc6db9fc4b69a23b6bda51f5941 | |
| parent | 416c4aa0ae52104f8b97ae4262452f07225329db (diff) | |
| download | mrust-d37c27292945148d53108fc876cff3371c53404c.tar.gz | |
HIR Typecheck - Fixed associated type lookup
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | src/hir_typeck/helpers.cpp | 406 | ||||
| -rw-r--r-- | src/hir_typeck/helpers.hpp | 1 | ||||
| -rw-r--r-- | src/hir_typeck/impl_ref.hpp | 7 | ||||
| -rw-r--r-- | src/hir_typeck/static.cpp | 11 | 
5 files changed, 223 insertions, 204 deletions
| @@ -17,7 +17,7 @@ SHELL = bash  ifeq ($(DBGTPL),)  else ifeq ($(DBGTPL),gdb) -  DBG := echo -e "r\nbt 10\nq" | gdb --args +  DBG := echo -e "r\nbt 12\nq" | gdb --args  else ifeq ($(DBGTPL),valgrind)    DBG := valgrind --leak-check=full --num-callers=35  else diff --git a/src/hir_typeck/helpers.cpp b/src/hir_typeck/helpers.cpp index ca30e583..d919bae1 100644 --- a/src/hir_typeck/helpers.cpp +++ b/src/hir_typeck/helpers.cpp @@ -1332,202 +1332,8 @@ bool TraitResolution::has_associated_type(const ::HIR::TypeRef& input) const              // - Only try resolving if the binding isn't known              if( !e.binding.is_Unbound() )                  return input; -            // TODO: If opaque, still search a list of known equalities -             -            DEBUG("Locating associated type for " << e.path); -             -            *pe.type = expand_associated_types(sp, mv$(*pe.type)); -             -             -            // - If it's a closure, then the only trait impls are those generated by typeck -            TU_IFLET(::HIR::TypeRef::Data, pe.type->m_data, Closure, te, -                const auto trait_fn = this->m_crate.get_lang_item_path(sp, "fn"); -                const auto trait_fn_mut = this->m_crate.get_lang_item_path(sp, "fn_mut"); -                const auto trait_fn_once = this->m_crate.get_lang_item_path(sp, "fn_once"); -                if( pe.trait.m_path == trait_fn || pe.trait.m_path == trait_fn_mut || pe.trait.m_path == trait_fn_once  ) { -                    if( pe.item == "Output" ) { -                        return te.m_rettype->clone(); -                    } -                    else { -                        ERROR(sp, E0000, "No associated type " << pe.item << " for trait " << pe.trait); -                    } -                } -                else { -                    ERROR(sp, E0000, "No implementation of " << pe.trait << " for " << *pe.type); -                } -            ) -             -            //this->find_impl() -             -            // 1. Bounds -            bool rv; -            bool assume_opaque = true; -            rv = this->iterate_bounds([&](const auto& b) { -                TU_MATCH_DEF(::HIR::GenericBound, (b), (be), -                ( -                    ), -                (TraitBound, -                    DEBUG("Trait bound - " << be.type << " : " << be.trait); -                    // 1. Check if the type matches -                    //  - TODO: This should be a fuzzier match? -                    if( be.type != *pe.type ) -                        return false; -                    // 2. Check if the trait (or any supertrait) includes pe.trait -                    if( be.trait.m_path == pe.trait ) { -                        auto it = be.trait.m_type_bounds.find(pe.item); -                        // 1. Check if the bounds include the desired item -                        if( it == be.trait.m_type_bounds.end() ) { -                            // If not, assume it's opaque and return as such -                            // TODO: What happens if there's two bounds that overlap? 'F: FnMut<()>, F: FnOnce<(), Output=Bar>' -                            DEBUG("Found impl for " << input << " but no bound on item, assuming opaque"); -                        } -                        else { -                            assume_opaque = false; -                            input = it->second.clone(); -                        } -                        return true; -                    } -                     -                    bool found_supertrait = this->find_named_trait_in_trait(sp, -                        pe.trait.m_path, pe.trait.m_params, -                        *be.trait.m_trait_ptr, be.trait.m_path.m_path, be.trait.m_path.m_params, *pe.type, -                        [&pe,&input,&assume_opaque](const auto&, const auto& x, const auto& assoc){ -                            auto it = assoc.find(pe.item); -                            if( it != assoc.end() ) { -                                assume_opaque = false; -                                DEBUG("Found associated type " << input << " = " << it->second); -                                input = it->second.clone(); -                            } -                            return true; -                        } -                        ); -                    if( found_supertrait ) { -                        auto it = be.trait.m_type_bounds.find(pe.item); -                        // 1. Check if the bounds include the desired item -                        if( it == be.trait.m_type_bounds.end() ) { -                            // If not, assume it's opaque and return as such -                            // TODO: What happens if there's two bounds that overlap? 'F: FnMut<()>, F: FnOnce<(), Output=Bar>' -                            if( assume_opaque ) -                                DEBUG("Found impl for " << input << " but no bound on item, assuming opaque"); -                        } -                        else { -                            assume_opaque = false; -                            input = it->second.clone(); -                        } -                        return true; -                    } -                     -                    // - Didn't match -                    ), -                (TypeEquality, -                    DEBUG("Equality - " << be.type << " = " << be.other_type); -                    if( input == be.type ) { -                        assume_opaque = false; -                        input = be.other_type.clone(); -                        return true; -                    } -                    ) -                ) -                return false; -                }); -            if( rv ) { -                if( assume_opaque ) { -                    DEBUG("Assuming that " << input << " is an opaque name"); -                    input.m_data.as_Path().binding = ::HIR::TypeRef::TypePathBinding::make_Opaque({}); -                     -                    DEBUG("- " << m_type_equalities.size() << " replacements"); -                    for( const auto& v : m_type_equalities ) -                        DEBUG(" > " << v.first << " = " << v.second); -                     -                    auto a = m_type_equalities.find(input); -                    if( a != m_type_equalities.end() ) { -                        input = a->second.clone(); -                    } -                } -                input = this->expand_associated_types(sp, mv$(input)); -                return input; -            } - -            // If the type of this UfcsKnown is ALSO a UfcsKnown - Check if it's bounded by this trait with equality -            // Use bounds on other associated types too (if `pe.type` was resolved to a fixed associated type) -            TU_IFLET(::HIR::TypeRef::Data, pe.type->m_data, Path, te_inner, -                TU_IFLET(::HIR::Path::Data, te_inner.path.m_data, UfcsKnown, pe_inner, -                    // TODO: Search for equality bounds on this associated type (pe_inner) that match the entire type (pe) -                    // - Does simplification of complex associated types -                    const auto& trait_ptr = this->m_crate.get_trait_by_path(sp, pe_inner.trait.m_path); -                    const auto& assoc_ty = trait_ptr.m_types.at(pe_inner.item); -                    DEBUG("TODO: Search bounds on associated type - " << assoc_ty.m_trait_bounds); -                     -                    // Resolve where Self=pe_inner.type (i.e. for the trait this inner UFCS is on) -                    auto cb_placeholders_trait = [&](const auto& ty)->const auto&{ -                        TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e, -                            if( e.binding == 0xFFFF ) -                                return *pe_inner.type; -                            else { -                                // TODO: Look in pe_inner.trait.m_params -                                TODO(sp, "Handle type params when expanding associated bound (#" << e.binding << " " << e.name); -                            } -                        ) -                        else { -                            return ty; -                        } -                        }; -                    for(const auto& bound : assoc_ty.m_trait_bounds) -                    { -                        // If the bound is for Self and the outer trait -                        // - TODO: Parameters? -                        if( bound.m_path == pe.trait ) { -                            auto it = bound.m_type_bounds.find( pe.item ); -                            if( it != bound.m_type_bounds.end() ) { -                                if( monomorphise_type_needed(it->second) ) { -                                    input = monomorphise_type_with(sp, it->second, cb_placeholders_trait); -                                } -                                else { -                                    input = it->second.clone(); -                                } -                                input = this->expand_associated_types(sp, mv$(input)); -                                return input; -                            } -                        } -                    } -                    DEBUG("pe = " << *pe.type << ", input = " << input); -                ) -            ) - -            // 2. Crate-level impls -            // TODO: Search for the actual trait containing this associated type -            ::HIR::GenericPath  trait_path; -            if( !this->trait_contains_type(sp, pe.trait, this->m_crate.get_trait_by_path(sp, pe.trait.m_path), pe.item, trait_path) ) -                BUG(sp, "Cannot find associated type " << pe.item << " anywhere in trait " << pe.trait); -            //pe.trait = mv$(trait_path); -             -            DEBUG("Searching for impl"); -            rv = this->find_trait_impls_crate(sp, trait_path.m_path, trait_path.m_params, *pe.type, [&](auto impl, auto qual) { -                DEBUG("Found " << impl); -                auto ty = impl.get_type( pe.item.c_str() ); -                if( ty == ::HIR::TypeRef() ) -                    ERROR(sp, E0000, "Couldn't find assocated type " << pe.item << " in " << pe.trait); -                 -                DEBUG("Converted UfcsKnown - " << e.path << " = " << ty); -                input = mv$(ty); -                return true; -                }); -            if( rv ) { -                input = this->expand_associated_types(sp, mv$(input)); -                return input; -            } -             -            // If there are no ivars in this path, set its binding to Opaque -            if( !this->m_ivars.type_contains_ivars(input) ) { -                // TODO: If the type is a generic or an opaque associated, we can't know. -                // - If the trait contains any of the above, it's unknowable -                // - Otherwise, it's an error -                e.binding = ::HIR::TypeRef::TypePathBinding::make_Opaque({}); -                DEBUG("Couldn't resolve associated type for " << input << " (and won't ever be able to)"); -            } -            else { -                DEBUG("Couldn't resolve associated type for " << input << " (will try again later)"); -            } +            this->expand_associated_types__UfcsKnown(sp, input); +            return input;              ),          (UfcsUnknown,              BUG(sp, "Encountered UfcsUnknown"); @@ -1567,6 +1373,210 @@ bool TraitResolution::has_associated_type(const ::HIR::TypeRef& input) const  } +void TraitResolution::expand_associated_types__UfcsKnown(const Span& sp, ::HIR::TypeRef& input) const +{ +    auto& e = input.m_data.as_Path(); +    auto& pe = e.path.m_data.as_UfcsKnown(); +    // TODO: If opaque, still search a list of known equalities +     +    DEBUG("Locating associated type for " << e.path); +     +    *pe.type = expand_associated_types(sp, mv$(*pe.type)); +     +     +    // - If it's a closure, then the only trait impls are those generated by typeck +    TU_IFLET(::HIR::TypeRef::Data, pe.type->m_data, Closure, te, +        const auto trait_fn = this->m_crate.get_lang_item_path(sp, "fn"); +        const auto trait_fn_mut = this->m_crate.get_lang_item_path(sp, "fn_mut"); +        const auto trait_fn_once = this->m_crate.get_lang_item_path(sp, "fn_once"); +        if( pe.trait.m_path == trait_fn || pe.trait.m_path == trait_fn_mut || pe.trait.m_path == trait_fn_once  ) { +            if( pe.item == "Output" ) { +                input = te.m_rettype->clone(); +                return ; +            } +            else { +                ERROR(sp, E0000, "No associated type " << pe.item << " for trait " << pe.trait); +            } +        } +        else { +            ERROR(sp, E0000, "No implementation of " << pe.trait << " for " << *pe.type); +        } +    ) +     +    //this->find_impl() +     +    // 1. Bounds +    bool rv; +    bool assume_opaque = true; +    rv = this->iterate_bounds([&](const auto& b) { +        TU_MATCH_DEF(::HIR::GenericBound, (b), (be), +        ( +            ), +        (TraitBound, +            DEBUG("Trait bound - " << be.type << " : " << be.trait); +            // 1. Check if the type matches +            //  - TODO: This should be a fuzzier match? +            if( be.type != *pe.type ) +                return false; +            // 2. Check if the trait (or any supertrait) includes pe.trait +            if( be.trait.m_path == pe.trait ) { +                auto it = be.trait.m_type_bounds.find(pe.item); +                // 1. Check if the bounds include the desired item +                if( it == be.trait.m_type_bounds.end() ) { +                    // If not, assume it's opaque and return as such +                    // TODO: What happens if there's two bounds that overlap? 'F: FnMut<()>, F: FnOnce<(), Output=Bar>' +                    DEBUG("Found impl for " << input << " but no bound on item, assuming opaque"); +                } +                else { +                    assume_opaque = false; +                    input = it->second.clone(); +                } +                return true; +            } +             +            bool found_supertrait = this->find_named_trait_in_trait(sp, +                pe.trait.m_path, pe.trait.m_params, +                *be.trait.m_trait_ptr, be.trait.m_path.m_path, be.trait.m_path.m_params, *pe.type, +                [&pe,&input,&assume_opaque](const auto&, const auto& x, const auto& assoc){ +                    auto it = assoc.find(pe.item); +                    if( it != assoc.end() ) { +                        assume_opaque = false; +                        DEBUG("Found associated type " << input << " = " << it->second); +                        input = it->second.clone(); +                    } +                    return true; +                } +                ); +            if( found_supertrait ) { +                auto it = be.trait.m_type_bounds.find(pe.item); +                // 1. Check if the bounds include the desired item +                if( it == be.trait.m_type_bounds.end() ) { +                    // If not, assume it's opaque and return as such +                    // TODO: What happens if there's two bounds that overlap? 'F: FnMut<()>, F: FnOnce<(), Output=Bar>' +                    if( assume_opaque ) +                        DEBUG("Found impl for " << input << " but no bound on item, assuming opaque"); +                } +                else { +                    assume_opaque = false; +                    input = it->second.clone(); +                } +                return true; +            } +             +            // - Didn't match +            ), +        (TypeEquality, +            DEBUG("Equality - " << be.type << " = " << be.other_type); +            if( input == be.type ) { +                assume_opaque = false; +                input = be.other_type.clone(); +                return true; +            } +            ) +        ) +        return false; +        }); +    if( rv ) { +        if( assume_opaque ) { +            DEBUG("Assuming that " << input << " is an opaque name"); +            input.m_data.as_Path().binding = ::HIR::TypeRef::TypePathBinding::make_Opaque({}); +             +            DEBUG("- " << m_type_equalities.size() << " replacements"); +            for( const auto& v : m_type_equalities ) +                DEBUG(" > " << v.first << " = " << v.second); +             +            auto a = m_type_equalities.find(input); +            if( a != m_type_equalities.end() ) { +                input = a->second.clone(); +            } +        } +        input = this->expand_associated_types(sp, mv$(input)); +        return ; +    } + +    // If the type of this UfcsKnown is ALSO a UfcsKnown - Check if it's bounded by this trait with equality +    // Use bounds on other associated types too (if `pe.type` was resolved to a fixed associated type) +    TU_IFLET(::HIR::TypeRef::Data, pe.type->m_data, Path, te_inner, +        TU_IFLET(::HIR::Path::Data, te_inner.path.m_data, UfcsKnown, pe_inner, +            // TODO: Search for equality bounds on this associated type (pe_inner) that match the entire type (pe) +            // - Does simplification of complex associated types +            const auto& trait_ptr = this->m_crate.get_trait_by_path(sp, pe_inner.trait.m_path); +            const auto& assoc_ty = trait_ptr.m_types.at(pe_inner.item); +            DEBUG("TODO: Search bounds on associated type - " << assoc_ty.m_trait_bounds); +             +            // Resolve where Self=pe_inner.type (i.e. for the trait this inner UFCS is on) +            auto cb_placeholders_trait = [&](const auto& ty)->const auto&{ +                TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e, +                    if( e.binding == 0xFFFF ) +                        return *pe_inner.type; +                    else { +                        // TODO: Look in pe_inner.trait.m_params +                        TODO(sp, "Handle type params when expanding associated bound (#" << e.binding << " " << e.name); +                    } +                ) +                else { +                    return ty; +                } +                }; +            for(const auto& bound : assoc_ty.m_trait_bounds) +            { +                // If the bound is for Self and the outer trait +                // - TODO: Parameters? +                if( bound.m_path == pe.trait ) { +                    auto it = bound.m_type_bounds.find( pe.item ); +                    if( it != bound.m_type_bounds.end() ) { +                        if( monomorphise_type_needed(it->second) ) { +                            input = monomorphise_type_with(sp, it->second, cb_placeholders_trait); +                        } +                        else { +                            input = it->second.clone(); +                        } +                        input = this->expand_associated_types(sp, mv$(input)); +                        return ; +                    } +                } +            } +            DEBUG("pe = " << *pe.type << ", input = " << input); +        ) +    ) + +    // 2. Crate-level impls +    // TODO: Search for the actual trait containing this associated type +    ::HIR::GenericPath  trait_path; +    if( !this->trait_contains_type(sp, pe.trait, this->m_crate.get_trait_by_path(sp, pe.trait.m_path), pe.item, trait_path) ) +        BUG(sp, "Cannot find associated type " << pe.item << " anywhere in trait " << pe.trait); +    //pe.trait = mv$(trait_path); +     +    DEBUG("Searching for impl"); +    rv = this->find_trait_impls_crate(sp, trait_path.m_path, trait_path.m_params, *pe.type, [&](auto impl, auto qual) { +        DEBUG("Found " << impl); +        auto ty = impl.get_type( pe.item.c_str() ); +        if( ty == ::HIR::TypeRef() ) +            ERROR(sp, E0000, "Couldn't find assocated type " << pe.item << " in " << pe.trait); +         +        DEBUG("Converted UfcsKnown - " << e.path << " = " << ty); +        input = mv$(ty); +        return true; +        }); +    if( rv ) { +        input = this->expand_associated_types(sp, mv$(input)); +        return ; +    } +     +    // If there are no ivars in this path, set its binding to Opaque +    if( !this->m_ivars.type_contains_ivars(input) ) { +        // TODO: If the type is a generic or an opaque associated, we can't know. +        // - If the trait contains any of the above, it's unknowable +        // - Otherwise, it's an error +        e.binding = ::HIR::TypeRef::TypePathBinding::make_Opaque({}); +        DEBUG("Couldn't resolve associated type for " << input << " (and won't ever be able to)"); +    } +    else { +        DEBUG("Couldn't resolve associated type for " << input << " (will try again later)"); +    } +} + +  // -------------------------------------------------------------------------------------------------------------------  //  // ------------------------------------------------------------------------------------------------------------------- @@ -1846,7 +1856,7 @@ bool TraitResolution::find_trait_impls_crate(const Span& sp,                          ab.second = this->expand_associated_types(sp, mv$(ab.second));                      }                      const auto& real_trait_path = real_trait.m_path; -                    DEBUG("- " << real_type << " : " << real_trait_path); +                    DEBUG("- " << real_type << " : " << real_trait);                      auto rv = this->find_trait_impls(sp, real_trait_path.m_path, real_trait_path.m_params, real_type, [&](auto impl, auto impl_cmp) {                          for(const auto& assoc_bound : real_trait.m_type_bounds) {                              ::HIR::TypeRef  tmp; @@ -1904,7 +1914,7 @@ bool TraitResolution::find_trait_impls_crate(const Span& sp,              //if( match == ::HIR::Compare::Fuzzy ) {              //    TODO(sp, "- Pass on fuzzy match status");              //} -            return callback(ImplRef(mv$(impl_params), impl), match); +            return callback(ImplRef(mv$(impl_params), impl, mv$(placeholders)), match);              //return callback(ty_mono, args_mono, types/*, (match == ::HIR::Compare::Fuzzy)*/);          }          ); diff --git a/src/hir_typeck/helpers.hpp b/src/hir_typeck/helpers.hpp index d6b4e149..d5f7e94f 100644 --- a/src/hir_typeck/helpers.hpp +++ b/src/hir_typeck/helpers.hpp @@ -167,6 +167,7 @@ public:      bool has_associated_type(const ::HIR::TypeRef& ty) const;      /// Expand any located associated types in the input, operating in-place and returning the result      ::HIR::TypeRef expand_associated_types(const Span& sp, ::HIR::TypeRef input) const; +    void expand_associated_types__UfcsKnown(const Span& sp, ::HIR::TypeRef& input) const;      const ::HIR::TypeRef& expand_associated_types(const Span& sp, const ::HIR::TypeRef& input, ::HIR::TypeRef& tmp) const {          if( this->has_associated_type(input) ) {              return (tmp = this->expand_associated_types(sp, input.clone())); diff --git a/src/hir_typeck/impl_ref.hpp b/src/hir_typeck/impl_ref.hpp index e2ec171e..e98ec988 100644 --- a/src/hir_typeck/impl_ref.hpp +++ b/src/hir_typeck/impl_ref.hpp @@ -9,6 +9,7 @@ struct ImplRef      TAGGED_UNION(Data, TraitImpl,      (TraitImpl, struct {          ::std::vector<const ::HIR::TypeRef*>   params; +        ::std::vector<::HIR::TypeRef>   params_ph;          const ::HIR::TraitImpl* impl;          }),      (BoundedPtr, struct { @@ -26,10 +27,10 @@ struct ImplRef      Data    m_data;      ImplRef(): -        m_data(Data::make_TraitImpl({ {}, nullptr })) +        m_data(Data::make_TraitImpl({ {}, {}, nullptr }))      {} -    ImplRef(::std::vector<const ::HIR::TypeRef*> params, const ::HIR::TraitImpl& impl): -        m_data(Data::make_TraitImpl({ mv$(params), &impl })) +    ImplRef(::std::vector<const ::HIR::TypeRef*> params, const ::HIR::TraitImpl& impl, ::std::vector< ::HIR::TypeRef> params_ph={}): +        m_data(Data::make_TraitImpl({ mv$(params), mv$(params_ph), &impl }))      {}      ImplRef(const ::HIR::TypeRef* type, const ::HIR::PathParams* args, const ::std::map< ::std::string, ::HIR::TypeRef>* assoc): diff --git a/src/hir_typeck/static.cpp b/src/hir_typeck/static.cpp index d9972116..7526f76e 100644 --- a/src/hir_typeck/static.cpp +++ b/src/hir_typeck/static.cpp @@ -674,8 +674,15 @@ bool ImplRef::type_is_specializable(const char* name) const                  const auto& ge = gt.m_data.as_Generic();                  assert(ge.binding < 256);                  assert(ge.binding < e.params.size()); -                assert(e.params[ge.binding]); -                return *e.params[ge.binding]; +                if( e.params[ge.binding] ) { +                    return *e.params[ge.binding]; +                } +                else if( e.params_ph.size() && e.params_ph[ge.binding] != ::HIR::TypeRef() ) { +                    return e.params_ph[ge.binding]; +                } +                else { +                    BUG(Span(), "Param #" << ge.binding << " " << ge.name << " isn't constrained for " << *this); +                }                  };              return monomorphise_type_with(sp, it->second, cb_monomorph);          } | 
