diff options
Diffstat (limited to 'src/ast/path.cpp')
-rw-r--r-- | src/ast/path.cpp | 370 |
1 files changed, 9 insertions, 361 deletions
diff --git a/src/ast/path.cpp b/src/ast/path.cpp index 8fe42527..8668c386 100644 --- a/src/ast/path.cpp +++ b/src/ast/path.cpp @@ -136,367 +136,7 @@ AST::Path::Path(const Path& x): DEBUG("clone, x = " << x << ", this = " << *this ); } -/// Resolve a path into a canonical form, and bind it to the target value -void Path::resolve(const Crate& root_crate, bool expect_params) -{ - TRACE_FUNCTION_F("*this = "<< *this); - if( m_binding.is_Unbound() ) - { - if( m_class.is_Absolute() ) { - resolve_absolute(root_crate, expect_params); - } - else if(m_class.is_UFCS()) { - resolve_ufcs(root_crate, expect_params); - } - else - throw ParseError::BugCheck("Calling Path::resolve on non-absolute path"); - } -} -void Path::resolve_absolute(const Crate& root_crate, bool expect_params) -{ - auto& nodes = m_class.as_Absolute().nodes; - DEBUG("m_crate = '" << m_crate << "'"); - - unsigned int slice_from = 0; // Used when rewriting the path to be relative to its crate root - - ::std::vector<const Module*> mod_stack; - const Module* mod = &root_crate.get_root_module(m_crate); - for(unsigned int i = 0; i < nodes.size(); i ++ ) - { - mod_stack.push_back(mod); - const bool is_last = (i+1 == nodes.size()); - const bool is_sec_last = (i+2 == nodes.size()); - const PathNode& node = nodes[i]; - DEBUG("[" << i << "/"<<nodes.size()<<"]: " << node); - - if( node.name()[0] == '#' ) - { - // HACK - Compiler-provided functions/types live in the special '#' module - if( node.name() == "#" ) { - if( i != 0 ) - throw ParseError::BugCheck("# module not at path root"); - mod = &g_compiler_module; - continue ; - } - - // Hacky special case - Anon modules are indexed - // - Darn you C++ and no string views - unsigned int index = ::std::strtoul(node.name().c_str()+1, nullptr, 10); // Parse the number at +1 - DEBUG(" index = " << index); - if( index >= mod->anon_mods().size() ) - throw ParseError::Generic("Anon module index out of range"); - mod = mod->anon_mods().at(index); - continue ; - } - - auto item = mod->find_item(node.name(), is_last); // Only allow leaf nodes (functions and statics) if this is the last node - switch( item.type() ) - { - // Not found - case AST::Module::ItemRef::ITEM_none: - // If parent node is anon, backtrack and try again - // TODO: I feel like this shouldn't be done here, instead perform this when absolutising (now that find_item is reusable) - if( i > 0 && nodes[i-1].name()[0] == '#' && nodes[i-1].name().size() > 1 ) - { - i --; - mod_stack.pop_back(); - mod = mod_stack.back(); - mod_stack.pop_back(); - nodes.erase(nodes.begin()+i); - i --; - DEBUG("Failed to locate item in nested, look upwards - " << *this); - - continue ; - } - throw ParseError::Generic("Unable to find component '" + node.name() + "'"); - - // Sub-module - case AST::Module::ItemRef::ITEM_Module: - DEBUG("Sub-module : " << node.name()); - if( node.args().size() ) - throw ParseError::Generic("Generic params applied to module"); - mod = &item.unwrap_Module(); - break; - - // Crate - case AST::Module::ItemRef::ITEM_Crate: { - const ::std::string& crate_name = item.unwrap_Crate(); - DEBUG("Extern crate '" << node.name() << "' = '" << crate_name << "'"); - if( node.args().size() ) - throw ParseError::Generic("Generic params applied to extern crate"); - m_crate = crate_name; - slice_from = i+1; - mod = &root_crate.get_root_module(crate_name); - break; } - - // Type Alias - case AST::Module::ItemRef::ITEM_TypeAlias: { - const auto& ta = item.unwrap_TypeAlias(); - DEBUG("Type alias <"<<ta.params()<<"> " << ta.type()); - //if( node.args().size() != ta.params().size() ) - // throw ParseError::Generic("Param count mismatch when referencing type alias"); - // Make a copy of the path, replace params with it, then replace *this? - // - Maybe leave that up to other code? - if( is_last ) { - check_param_counts(ta.params(), expect_params, nodes[i]); - m_binding = PathBinding::make_TypeAlias( {&ta} ); - goto ret; - } - else { - throw ParseError::Todo("Path::resolve() type method"); - } - break; } - - // Function - case AST::Module::ItemRef::ITEM_Function: { - const auto& fn = item.unwrap_Function(); - DEBUG("Found function"); - if( is_last ) { - check_param_counts(fn.params(), expect_params, nodes[i]); - m_binding = PathBinding::make_Function({&fn}); - goto ret; - } - else { - throw ParseError::Generic("Import of function, too many extra nodes"); - } - break; } - - // Trait - case AST::Module::ItemRef::ITEM_Trait: { - const auto& t = item.unwrap_Trait(); - DEBUG("Found trait"); - if( is_last ) { - check_param_counts(t.params(), expect_params, nodes[i]); - m_binding = PathBinding::make_Trait({&t}); - goto ret; - } - else if( is_sec_last ) { - check_param_counts(t.params(), expect_params, nodes[i]); - // TODO: Also check params on item - m_binding = PathBinding::make_TraitMethod( {&t, nodes[i+1].name()} ); - goto ret; - } - else { - throw ParseError::Generic("Import of trait, too many extra nodes"); - } - break; } - - // Struct - case AST::Module::ItemRef::ITEM_Struct: { - const auto& str = item.unwrap_Struct(); - DEBUG("Found struct"); - if( is_last ) { - check_param_counts(str.params(), expect_params, nodes[i]); - bind_struct(str, node.args()); - goto ret; - } - else if( is_sec_last ) { - check_param_counts(str.params(), expect_params, nodes[i]); - bind_struct_member(str, node.args(), nodes[i+1]); - goto ret; - } - else { - throw ParseError::Generic("Import of struct, too many extra nodes"); - } - break; } - - // Enum / enum variant - case AST::Module::ItemRef::ITEM_Enum: { - const auto& enm = item.unwrap_Enum(); - DEBUG("Found enum"); - if( is_last ) { - check_param_counts(enm.params(), expect_params, nodes[i]); - bind_enum(enm, node.args()); - goto ret; - } - else if( is_sec_last ) { - check_param_counts(enm.params(), expect_params, nodes[i]); - bind_enum_var(enm, nodes[i+1].name(), node.args()); - goto ret; - } - else { - throw ParseError::Generic("Binding path to enum, too many extra nodes"); - } - break; } - - case AST::Module::ItemRef::ITEM_Static: { - const auto& st = item.unwrap_Static(); - DEBUG("Found static/const"); - if( is_last ) { - if( node.args().size() ) - throw ParseError::Generic("Unexpected generic params on static/const"); - bind_static(st); - goto ret; - } - else { - throw ParseError::Generic("Binding path to static, trailing nodes"); - } - break; } - - // Re-export - case AST::Module::ItemRef::ITEM_Use: { - const auto& imp = item.unwrap_Use(); - AST::Path newpath = imp.data; - auto& newnodes = newpath.m_class.as_Absolute().nodes; - DEBUG("Re-exported path " << imp.data); - if( imp.name == "" ) - { - // Replace nodes 0:i-1 with source path, then recurse - for( unsigned int j = i; j < nodes.size(); j ++ ) - { - newnodes.push_back( nodes[j] ); - } - } - else - { - // replace nodes 0:i with the source path - for( unsigned int j = i+1; j < nodes.size(); j ++ ) - { - newnodes.push_back( nodes[j] ); - } - } - - DEBUG("- newpath = " << newpath); - // TODO: This should check for recursion somehow - newpath.resolve(root_crate, expect_params); - - *this = mv$(newpath); - DEBUG("Alias resolved, *this = " << *this); - return; } - } - - } - - // We only reach here if the path points to a module - m_binding = PathBinding::make_Module({mod}); -ret: - if( slice_from > 0 ) - { - DEBUG("Removing " << slice_from << " nodes to rebase path to crate root"); - nodes.erase(nodes.begin(), nodes.begin()+slice_from); - } - return ; -} - -void Path::resolve_ufcs(const Crate& root_crate, bool expect_params) -{ - auto& data = m_class.as_UFCS(); - auto& type = *data.type; - auto& trait = *data.trait; - - // TODO: I can forsee <T>::Assoc::Item desugaring into < <T>::Assoc >::Item, but that will be messy to code - assert(data.nodes.size()); - if(data.nodes.size() != 1) throw ParseError::Todo("Path::resolve_ufcs - Are multi-node UFCS paths valid?"); - auto& node = data.nodes.at(0); - - // If the type is unknown (at this time) - if( type.is_wildcard() || type.is_type_param() ) - { - // - _ as _ = BUG - if( !trait.is_path() ) - { - // Wait, what about <T as _>, is that valid? - throw CompileError::BugCheck( FMT("Path::resolve_ufcs - Path invalid : " << *this) ); - } - // - /*arg*/T as Trait = Type parameter - else if( type.is_type_param() ) - { - // Check that the param is bound on that trait? - //if( !type.type_params_ptr() ) - // throw CompileError::BugCheck( FMT("Path::resolve_ufcs - No bound params on arg") ); - - //const auto& tps = *type.type_params_ptr(); - //for( const auto& bound : tps.bounds() ) - //{ - // // TODO: Check if this type impls the trait - // // - Not needed to do the bind, so ignore for now - //} - - // Search trait for an impl - //throw ParseError::Todo("Path::resolve_ufcs - Arg"); - resolve_ufcs_trait(trait.path(), node); - //throw ParseError::Todo("Path::resolve_ufcs - Arg2"); - } - // - _ as Trait = Inferred type (unknown at the moment) - else - { - throw ParseError::Todo("Path::resolve_ufcs - Handle binding when type is unknown"); - } - } - else - { - // - Type as _ = ? Infer the trait from any matching impls - if( trait.is_wildcard() ) - { - // Search inherent impl first, then (somehow) search in-scope traits - // - TODO: Shouldn't this be the job of CPathResolver? - throw ParseError::Todo("Path::resolve_ufcs - Unknown trait (resolve)"); - } - // - Type as Trait = Obtain from relevant impl - else if( trait.is_path() ) - { - // Locate in the trait, but store Self type somehow? - trait.path().resolve(root_crate, true); - resolve_ufcs_trait(trait.path(), node); - } - // - Type as ! = Item from the inherent impl (similar to above) - else if( trait == TypeRef(TypeRef::TagInvalid()) ) - { - // TODO: Handle case where 'type' is a trait object - // 1. Obtain the impl - AST::Impl* impl_ptr; - if( ! root_crate.find_impl(AST::Path(), type, &impl_ptr) ) - throw ParseError::Generic("Path::resolve_ufcs - No impl block for type"); - assert( impl_ptr ); - - for( const auto& it : impl_ptr->functions() ) - { - if( it.name == node.name() ) { - check_param_counts(it.data.params(), expect_params, node); - m_binding = PathBinding::make_Function( {&it.data} ); - goto _impl_item_bound; - } - } - throw ParseError::Generic( FMT("Path::resolve_ufcs - No item named '"<<node.name()<<"' in inherent")); - _impl_item_bound: - DEBUG("UFCS inherent bound to " << m_binding); - } - // - Type as * = Bug - else - { - throw CompileError::BugCheck( FMT("Path::resolve_ufcs - Path invalid : " << *this) ); - } - } -} - -void Path::resolve_ufcs_trait(const AST::Path& trait_path, AST::PathNode& node) -{ - if( !trait_path.m_binding.is_Trait() ) - ERROR(trait_path.span(), E0000, "Trait in UFCS path is not a trait"); - const auto& trait_def = *trait_path.m_binding.as_Trait().trait_; - - // Check that the requested item exists within the trait, and bind to that item - for( const auto& fn : trait_def.functions() ) - { - if( fn.name == node.name() ) { - check_param_counts(fn.data.params(), true, node); - m_binding = PathBinding::make_Function( {&fn.data} ); - goto _trait_item_bound; - } - } - for( const auto& it : trait_def.types() ) - { - if( it.name == node.name() ) { - check_param_counts(it.data.params(), true, node); - m_binding = PathBinding::make_TypeAlias( {&it.data} ); - goto _trait_item_bound; - } - } - throw ParseError::Todo("Path::resolve_ufcs - Fully known"); -_trait_item_bound: - DEBUG("UFCS trait bound to " << m_binding); -} - +/* void Path::check_param_counts(const TypeParams& params, bool expect_params, PathNode& node) { if( !expect_params ) @@ -530,11 +170,15 @@ void Path::check_param_counts(const TypeParams& params, bool expect_params, Path } } } +*/ void Path::bind_variable(unsigned int slot) { m_binding = PathBinding::make_Variable({slot}); } +void Path::bind_module(const Module& mod) { + m_binding = PathBinding::make_Module({&mod}); +} void Path::bind_enum(const Enum& ent, const ::std::vector<TypeRef>& args) { DEBUG("Bound to enum"); @@ -585,6 +229,10 @@ void Path::bind_static(const Static& ent) { m_binding = PathBinding::make_Static({&ent}); } +void Path::bind_trait(const Trait& ent, const ::std::vector<TypeRef>& args) +{ + m_binding = PathBinding::make_Trait({&ent}); +} void Path::resolve_args(::std::function<TypeRef(const char*)> fcn) { |