diff options
-rw-r--r-- | src/ast/ast.cpp | 23 | ||||
-rw-r--r-- | src/ast/ast.hpp | 3 | ||||
-rw-r--r-- | src/convert/typecheck_expr.cpp | 149 | ||||
-rw-r--r-- | src/types.cpp | 33 | ||||
-rw-r--r-- | src/types.hpp | 2 |
5 files changed, 157 insertions, 53 deletions
diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index b8de6d4c..80bc3c57 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -105,10 +105,27 @@ const Module& Crate::get_root_module(const ::std::string& name) const { throw ParseError::Generic("crate name unknown");
}
-Impl& Crate::find_impl(const TypeRef& trait, const TypeRef& type)
+::rust::option<Impl&> Crate::find_impl(const TypeRef& trait, const TypeRef& type)
{
- // TODO: Support autoderef here?
- throw ParseError::Generic( FMT("TODO: Lookup impl of " << trait << " for type " << type));
+ // TODO: Support autoderef here? NO
+ if( trait.is_wildcard() && !type.is_path() )
+ {
+ // You can only have 'impl <type> { }' for user-defined types (i.e. paths)
+ // - Return failure
+ return ::rust::option<Impl&>();
+ }
+
+ for( auto implptr : m_impl_index )
+ {
+ Impl& impl = *implptr;
+ // TODO: Pass to impl for comparison (handles type params)
+ if( impl.trait() == trait && impl.type() == type )
+ {
+ return ::rust::option<Impl&>(impl);
+ }
+ }
+ DEBUG("No impl of " << trait << " for " << type);
+ return ::rust::option<Impl&>();
}
Function& Crate::lookup_method(const TypeRef& type, const char *name)
diff --git a/src/ast/ast.hpp b/src/ast/ast.hpp index 94ffbb79..40a44b78 100644 --- a/src/ast/ast.hpp +++ b/src/ast/ast.hpp @@ -483,6 +483,7 @@ public: class Crate:
public Serialisable
{
+ ::std::vector<Impl*> m_impl_index;
public:
Module m_root_module;
::std::map< ::std::string, ExternCrate> m_extern_crates;
@@ -499,7 +500,7 @@ public: ::std::map< ::std::string, ExternCrate>& extern_crates() { return m_extern_crates; }
const ::std::map< ::std::string, ExternCrate>& extern_crates() const { return m_extern_crates; }
- Impl& find_impl(const TypeRef& trait, const TypeRef& type);
+ ::rust::option<Impl&> find_impl(const TypeRef& trait, const TypeRef& type);
Function& lookup_method(const TypeRef& type, const char *name);
void load_extern_crate(::std::string name);
diff --git a/src/convert/typecheck_expr.cpp b/src/convert/typecheck_expr.cpp index 4fa62fe2..c647ed54 100644 --- a/src/convert/typecheck_expr.cpp +++ b/src/convert/typecheck_expr.cpp @@ -1,4 +1,9 @@ /* + * MRustC - Mutabah's Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * convert/typecheck_expr.cpp + * - Handles type checking, expansion, and method resolution for expressions (function bodies) */ #include <main_bindings.hpp> #include "ast_iterate.hpp" @@ -15,6 +20,7 @@ class CTypeChecker: ::std::vector< ::std::tuple<bool, ::std::string, TypeRef> > vars; ::std::vector< ::std::tuple< ::std::string, TypeRef> > types; ::std::map< ::std::string, TypeRef > params; + ::std::vector< AST::Path > traits; }; AST::Crate& m_crate; @@ -42,7 +48,8 @@ private: TypeRef& get_local_var(const char* name); const TypeRef& get_local_type(const char* name); const TypeRef& get_type_param(const char* name); - void lookup_method(const TypeRef& type, const char* name); + + void iterate_traits(::std::function<bool(const TypeRef& trait)> fcn); }; class CTC_NodeVisitor: public AST::NodeVisitor @@ -191,19 +198,18 @@ void CTypeChecker::handle_function(AST::Path path, AST::Function& fcn) end_scope(); } -void CTypeChecker::lookup_method(const TypeRef& type, const char* name) +void CTypeChecker::iterate_traits(::std::function<bool(const TypeRef& trait)> fcn) { - DEBUG("(type = " << type << ", name = " << name << ")"); - // 1. Look for inherent methods on the type - // 2. Iterate all in-scope traits, and locate an impl of that trait for this type - //for(auto traitptr : m_scope_traits ) - //{ - // if( !traitptr ) continue; - // const auto& trait = *traitptr; - // if( trait.has_method(name) && trait.find_impl(type) ) - // { - // } - //} + for( auto scopei = m_scopes.end(); scopei-- != m_scopes.begin(); ) + { + for( auto& trait : scopei->traits ) + { + if( !fcn(trait) ) + { + return; + } + } + } } /// Named value - leaf @@ -372,54 +378,99 @@ void CTC_NodeVisitor::visit(AST::ExprNode_CallMethod& node) // Locate method const TypeRef& type = node.m_val->get_res_type(); DEBUG("CallMethod - type = " << type); - if( type.is_wildcard() ) - { - // No idea (yet) - // - TODO: Support case where a trait is known - throw ::std::runtime_error("Unknown type in CallMethod"); - } - else if( type.is_type_param() ) + + // Replace generic references in 'type' (copying the type) with + // '_: Bounds' (allowing method lookup to succeed) + TypeRef ltype = type; + unsigned int deref_count = 0; + ltype.resolve_args( [&](const char* name) { + return m_tc.get_type_param(name); + } ); + + // Begin trying options (attempting an autoderef each time) + const char * const name = node.m_method.name().c_str(); + AST::Function* fcnp = nullptr; + do { - const char *name = type.type_param().c_str(); - // Find this name in the current set of type params - const TypeRef& p_type = m_tc.get_type_param(name); - // Iterate bounds on type param - TypeRef ret_type; - for( const auto& t : p_type.traits() ) + // 1. Handle bounded wildcard types + if( ltype.is_wildcard() ) { - DEBUG("- Trait " << t.path()); - const AST::Trait& trait = t.path().bound_trait(); - // - Find method on one of them - for( const auto& m : trait.functions() ) + if( ltype.traits().size() == 0 ) { + DEBUG("- Unconstrained wildcard, returning"); + return ; + } + + for( const auto& t : ltype.traits() ) { - DEBUG(" > method: " << m.name << " search: " << node.m_method.name()); - if( m.name == node.m_method.name() ) + DEBUG("- Trait " << t.path()); + AST::Trait& trait = const_cast<AST::Trait&>( t.path().bound_trait() ); + // - Find method on one of them + for( auto& m : trait.functions() ) { - DEBUG(" > Found method"); - if( m.data.params().n_params() ) + DEBUG(" > method: " << m.name << " search: " << node.m_method.name()); + if( m.name == node.m_method.name() ) { - throw ::std::runtime_error("TODO: Call method with params"); + DEBUG(" > Found method"); + fcnp = &m.data; + break; } - ret_type = m.data.rettype(); } + if(fcnp) break; } + if(fcnp) break; } - if( ret_type.is_wildcard() ) + else { - throw ::std::runtime_error("Couldn't find method"); + // 2. Find inherent impl + auto oimpl = m_tc.m_crate.find_impl(TypeRef(), ltype); + if( oimpl.is_some() ) + { + AST::Impl& impl = oimpl.unwrap(); + // 1.1. Search impl for this method + for(auto& fcn : impl.functions()) + { + if( fcn.name == name ) + { + fcnp = &fcn.data; + break; + } + } + if(fcnp) break; + } + + // 2. Iterate in-scope traits + m_tc.iterate_traits( [&](const TypeRef& trait) { + // TODO: Check trait first, then find an impl + auto oimpl = m_tc.m_crate.find_impl(trait, ltype); + if( oimpl.is_some() ) + { + AST::Impl& impl = oimpl.unwrap(); + for(auto& fcn : impl.functions()) + { + if( fcn.name == name ) + { + fcnp = &fcn.data; + break; + } + } + } + return fcnp == nullptr; + }); } - } - else + if( fcnp ) + break; + deref_count ++; + } while( ltype.deref(true) ); + + if( fcnp ) { - // Replace generic references in 'type' (copying the type) with - // '_: Bounds' (allowing method lookup to succeed) - TypeRef ltype = type; - ltype.resolve_args( [&](const char* name) { - return m_tc.get_type_param(name); - } ); - // - Search for a method on this type - // TODO: Requires passing knowledge of in-scope traits (or trying traits) - AST::Function& fcn = m_tc.m_crate.lookup_method(ltype, node.m_method.name().c_str()); + DEBUG("deref_count = " << deref_count); + for( unsigned i = 0; i < deref_count; i ++ ) + { + node.m_val = ::std::unique_ptr<AST::ExprNode>(new AST::ExprNode_Deref( ::std::move(node.m_val) )); + } + + AST::Function& fcn = *fcnp; if( fcn.params().n_params() != node.m_method.args().size() ) { throw ::std::runtime_error("CallMethod with param count mismatch"); diff --git a/src/types.cpp b/src/types.cpp index 58d820f5..33b564dd 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -33,6 +33,39 @@ const char* coretype_name(const eCoreType ct ) { return "NFI"; } +/// Replace this type reference with a dereferenced version +bool TypeRef::deref(bool is_implicit) +{ + switch(m_class) + { + case TypeRef::ANY: + // TODO: Check if the _ is bounded by Deref<Output=?>, if so use that + throw ::std::runtime_error("Dereferencing _"); + break; + case TypeRef::UNIT: + throw ::std::runtime_error("Dereferencing ()"); + case TypeRef::PRIMITIVE: + throw ::std::runtime_error("Dereferencing a primtive type"); + case TypeRef::GENERIC: + throw ::std::runtime_error("Dereferencing a generic"); + case TypeRef::REFERENCE: + *this = m_inner_types[0]; + return true; + case TypeRef::POINTER: + // raw pointers can't be implicitly dereferenced + if( is_implicit ) + return false; + *this = m_inner_types[0]; + return true; + case TypeRef::TUPLE: + case TypeRef::ARRAY: + case TypeRef::PATH: + throw ::std::runtime_error("TODO: Search for an impl of Deref"); + case TypeRef::ASSOCIATED: + throw ::std::runtime_error("TODO: TypeRef::deref on ASSOCIATED"); + } +} + /// Merge the contents of the passed type with this type /// /// \note Both types must be of the same form (i.e. both be tuples) diff --git a/src/types.hpp b/src/types.hpp index b40c9424..2cbbd0bb 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -126,6 +126,8 @@ public: m_inner_types( {::std::move(base), ::std::move(trait)} )
{}
+ /// Dereference the type (return the result of *type_instance)
+ bool deref(bool is_implicit);
/// Merge with another type (combines known aspects, conflitcs cause an exception)
void merge_with(const TypeRef& other);
/// Replace 'GENERIC' entries with the return value of the closure
|