From f15fcf3c440e3c9c40fa606b0904fb85a8510464 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 4 Jun 2015 14:41:34 +0800 Subject: Rework path handling and resolve to better handle Self --- src/convert/resolve.cpp | 366 ++++++++++++++++++++++++++++-------------------- 1 file changed, 211 insertions(+), 155 deletions(-) (limited to 'src/convert/resolve.cpp') diff --git a/src/convert/resolve.cpp b/src/convert/resolve.cpp index bb6a15e3..54b0ab39 100644 --- a/src/convert/resolve.cpp +++ b/src/convert/resolve.cpp @@ -47,8 +47,14 @@ class CPathResolver: AST::Module* m_module; AST::Path m_module_path; ::std::vector< LocalItem > m_locals; - // TODO: Maintain a stack of variable scopes - ::std::vector< ::std::pair > m_module_stack; + struct Scope { + unsigned int module_idx; + AST::Module *module; // can be NULL + AST::Path module_path; + ::std::vector< ::std::string > locals; + }; + ::std::vector m_scope_stack; + ::std::vector< TypeRef > m_self_type; friend class CResolvePaths_NodeVisitor; @@ -64,6 +70,7 @@ public: virtual void handle_pattern(AST::Pattern& pat, const TypeRef& type_hint) override; virtual void handle_module(AST::Path path, AST::Module& mod) override; + virtual void handle_function(AST::Path path, AST::Function& fcn) override; virtual void start_scope() override; virtual void local_type(::std::string name, TypeRef type) override { @@ -75,12 +82,18 @@ public: m_locals.push_back( LocalItem(LocalItem::TYPE, ::std::move(name), ::std::move(type)) ); } virtual void local_variable(bool _is_mut, ::std::string name, const TypeRef& _type) override { - m_locals.push_back( LocalItem(LocalItem::VAR, ::std::move(name)) ); + assert(m_scope_stack.size() > 0); + m_scope_stack.back().locals.push_back( ::std::move(name) ); } virtual void end_scope() override; ::rust::option lookup_local(LocalItem::Type type, const ::std::string& name) const; + bool find_local_item(AST::Path& path, bool allow_variables); + bool find_mod_item(AST::Path& path); + bool find_super_mod_item(AST::Path& path); + bool find_type_param(const ::std::string& name); + // TODO: Handle a block and obtain the local module (if any) private: void handle_path_int(AST::Path& path, CASTIterator::PathMode mode); @@ -124,23 +137,35 @@ public: { // Add a reference to it to the parent node (add_anon_module will do dedup) - AST::Module& parent_mod = *( (m_res.m_module_stack.size() > 0) ? m_res.m_module_stack.back().second : m_res.m_module ); + AST::Module* parent_mod_p = m_res.m_module; + for(const auto& e : m_res.m_scope_stack) + if(e.module != nullptr) + parent_mod_p = e.module; + AST::Module& parent_mod = *parent_mod_p; auto idx = parent_mod.add_anon_module( node.m_inner_mod.get() ); - // And add to the list of modules to use in lookup - m_res.m_module_stack.push_back( ::std::make_pair(idx, node.m_inner_mod.get()) ); - - // Do use resolution on this module, then do + // Obtain the path AST::Path local_path = m_res.m_module_path; - for(unsigned int i = 0; i < m_res.m_module_stack.size(); i ++) - local_path.nodes().push_back( AST::PathNode( FMT("#" << m_res.m_module_stack[i].first), {} ) ); + for(const auto& e : m_res.m_scope_stack ) { + if( e.module != nullptr ) { + local_path.nodes().push_back( AST::PathNode( FMT("#" << e.module_idx), {} ) ); + } + } + local_path.nodes().push_back( AST::PathNode(FMT("#" << idx), {}) ); + + // And add to the list of modules to use in lookup + m_res.m_scope_stack.push_back( {idx, node.m_inner_mod.get(), local_path, {}} ); - ResolvePaths_HandleModule_Use(m_res.m_crate, local_path, *node.m_inner_mod); + // Do use resolution on this module + // TODO: When is more advanced resolution done? + ResolvePaths_HandleModule_Use(m_res.m_crate, m_res.m_scope_stack.back().module_path, *node.m_inner_mod); + } + else { + m_res.m_scope_stack.push_back( {0, nullptr, AST::Path(), {}} ); } AST::NodeVisitorDef::visit(node); // Once done, pop the module - if( node.m_inner_mod.get() ) - m_res.m_module_stack.pop_back(); + m_res.m_scope_stack.pop_back(); } void visit(AST::ExprNode_IfLet& node) @@ -292,6 +317,7 @@ void CPathResolver::end_scope() // > Search module-level definitions bool lookup_path_in_module(const AST::Crate& crate, const AST::Module& module, const AST::Path& mod_path, AST::Path& path) { + TRACE_FUNCTION_F("mod_path="< 0); - DEBUG("Relative, local"); - - // If there's a single node, and we're in expresion mode, look for a variable - // Otherwise, search for a type - bool is_trivial_path = (path.size() == 1 && path[0].args().size() == 0); + // > UFCS: Expand the types + case AST::Path::UFCS: + handle_path_ufcs(path, mode); + break; + // > Variable: (wait, how is this known already?) + // - 'self' + case AST::Path::VARIABLE: + break; + + case AST::Path::RELATIVE: + // 1. function scopes (variables and local items) + // > Return values: name or path + { + if( this->find_local_item(path, /*allow_variables=*/path.is_trivial()) ) { + break ; + } + else { + // No match, fall through + } + } - LocalItem::Type search_type = (is_trivial_path && mode == MODE_EXPR ? LocalItem::VAR : LocalItem::TYPE); - auto local_o = lookup_local( search_type, path[0].name() ); - if( local_o.is_some() ) + // 2. Type parameters + // - Should probably check if this is expression mode, bare types are invalid there + // NOTES: + // - If the path is bare (i.e. there are no more nodes), then ensure that the mode is TYPE + // - If there are more nodes, replace with a UFCS block { - auto local = local_o.unwrap(); - DEBUG("Local hit: " << path[0].name() << " = " << local); - - switch(mode) + auto tp = this->find_type_param(path[0].name()); + if( tp != false /*nullptr*/ ) { - // Local variable? - // - TODO: What would happen if MODE_EXPR but path isn't a single ident? - case MODE_EXPR: - if( local.type == LocalItem::VAR ) - { - if( !is_trivial_path ) - throw ParseError::Todo("TODO: MODE_EXPR, but not a single identifer, what do?"); - DEBUG("Local variable " << path[0].name()); - path = AST::Path(AST::Path::TagLocal(), path[0].name()); - return ; + if(path.size() > 1) { + // Repalce with UFCS + auto newpath = AST::Path(AST::Path::TagUfcs(), TypeRef(TypeRef::TagArg(), path[0].name()), TypeRef()); + newpath.add_tailing(path); + path = newpath; } - if( is_trivial_path ) - throw ParseError::Generic("Type used in expression context?"); - // Type parameter - case MODE_TYPE: - { - // Convert to a UFCS path - DEBUG("Local type"); - // - "::nodes" - auto np = AST::Path(AST::Path::TagUfcs(), local.tr, TypeRef()); - np.add_tailing(path); - path = ::std::move(np); - DEBUG("path = " << path); - handle_path_ufcs(path, mode); + else { + // Replace with VARIABLE (not strictly speaking true... but close enough) + // - TODO: Not being trivial is an error, not a bug + assert( path.is_trivial() ); + path = AST::Path(AST::Path::TagVariable(), path[0].name()); } - return ; - // Binding is valid - case MODE_BIND: - //break ; - throw ParseError::Todo("TODO: MODE_BIND, but local hit, what do?"); + break; + throw ParseError::Todo("Handle type param in RELATIVE"); } - - // TODO: What about sub-types and methods on type params? - // - Invalid afaik, instead Trait::method() is used } - - if( path.nodes()[0].name() == "super" ) - { - // Unwrap a single named node from the module path, and search that path - // - Requires resolving that path to a module to pass to lookup_path_in_module - AST::Path local_path = m_module_path; - local_path.nodes().pop_back(); - local_path.resolve(m_crate); - DEBUG("'super' path is relative to " << local_path); - path.nodes().erase( path.nodes().begin() ); // delete the 'super' node - const AST::Module& mod = local_path.binding().bound_module(); - if( lookup_path_in_module(m_crate, mod, local_path, path) ) - return ; - // this should always be an error, as 'super' paths are never MaybeBind - } - else + // 3. current module { - // Search backwards up the stack of anon modules - if( m_module_stack.size() ) - { - AST::Path local_path = m_module_path; - for(unsigned int i = 0; i < m_module_stack.size(); i ++) - local_path.nodes().push_back( AST::PathNode( FMT("#" << m_module_stack[i].first), {} ) ); - - for(unsigned int i = m_module_stack.size(); i--; ) - { - if( lookup_path_in_module(m_crate, *m_module_stack[i].second, local_path, path) ) { - // Success! - return ; - } - local_path.nodes().pop_back(); - } + if( this->find_mod_item(path) ) { + break; } - // Search current module, if found return with no error - if( lookup_path_in_module(m_crate, *m_module, m_module_path, path) ) - { - return; + else { } } + // *. No match? I give up + DEBUG("no matches found for path = " << path); + if( mode != MODE_BIND ) + throw ParseError::Generic("CPathResolver::handle_path - Name resolution failed"); + return ; + + // Module relative + case AST::Path::SELF:{ + if( this->find_mod_item(path) ) { + break; + } + else { + } + DEBUG("no matches found for path = " << path); + if( mode != MODE_BIND ) + throw ParseError::Generic("CPathResolver::handle_path - Name resolution failed"); + break; } + // Parent module relative + case AST::Path::SUPER:{ + if( this->find_super_mod_item(path) ) { + break; + } + else { + } DEBUG("no matches found for path = " << path); - assert( path.is_relative() ); if( mode != MODE_BIND ) throw ParseError::Generic("CPathResolver::handle_path - Name resolution failed"); break; } - case AST::Path::LOCAL: - // Don't touch locals, they're already known - break; - case AST::Path::UFCS: - handle_path_ufcs(path, mode); - break; } } void CPathResolver::handle_path_ufcs(AST::Path& path, CASTIterator::PathMode mode) @@ -556,6 +543,58 @@ void CPathResolver::handle_path_ufcs(AST::Path& path, CASTIterator::PathMode mod path.resolve(m_crate); } +bool CPathResolver::find_local_item(AST::Path& path, bool allow_variables) { + TRACE_FUNCTION_F("path="<name() == "" ) + throw ParseError::Todo("Correct handling of 'self' in anon modules"); + + return lookup_path_in_module(m_crate, *m_module, m_module_path, path); +} +bool CPathResolver::find_super_mod_item(AST::Path& path) { + if( m_module->name() == "" ) + throw ParseError::Todo("Correct handling of 'super' in anon modules"); + // 1. + // TODO: lookup_path_in_module + throw ParseError::Todo("Handle 'super'"); +} +bool CPathResolver::find_type_param(const ::std::string& name) { + for( auto it = m_locals.end(); it -- != m_locals.begin(); ) + { + if( it->type == LocalItem::TYPE ) { + if( it->name == name ) { + return true; + } + } + } + return false; +} + void CPathResolver::handle_type(TypeRef& type) { TRACE_FUNCTION_F("type = " << type); @@ -639,8 +678,7 @@ void CPathResolver::handle_pattern(AST::Pattern& pat, const TypeRef& type_hint) ::std::string name = pat.binding(); // Locate a _constant_ within the current namespace which matches this name // - Variables don't count - AST::Path newpath; - newpath.append( AST::PathNode(name, {}) ); + AST::Path newpath = AST::Path(name); handle_path(newpath, CASTIterator::MODE_BIND); if( newpath.is_relative() ) { @@ -668,6 +706,12 @@ void CPathResolver::handle_module(AST::Path path, AST::Module& mod) m_module_path = path; CASTIterator::handle_module(path, mod); } +void CPathResolver::handle_function(AST::Path path, AST::Function& fcn) +{ + m_scope_stack.push_back( {0, nullptr, AST::Path(), {}} ); + CASTIterator::handle_function(::std::move(path), fcn); + m_scope_stack.pop_back(); +} void ResolvePaths_HandleModule_Use(const AST::Crate& crate, const AST::Path& modpath, AST::Module& mod) { @@ -675,23 +719,35 @@ void ResolvePaths_HandleModule_Use(const AST::Crate& crate, const AST::Path& mod ::std::vector new_imports; for( auto& imp : mod.imports() ) { - if( !imp.data.is_absolute() ) + AST::Path& p = imp.data; + DEBUG("p = " << p); + switch( p.type() ) { - if( imp.data[0].name() == "super" ) { - if( modpath.size() < 1 ) - throw ParseError::Generic("Encountered 'super' at crate root"); - auto newpath = modpath; - newpath.nodes().pop_back(); - newpath.add_tailing(imp.data); - DEBUG("Absolutised path " << imp.data << " into " << newpath); - imp.data = ::std::move(newpath); - } - else { - auto newpath = modpath + imp.data; - // TODO: Undo anon modules until item is found - DEBUG("Absolutised path " << imp.data << " into " << newpath); - imp.data = ::std::move(newpath); - } + case AST::Path::ABSOLUTE: + // - No action + break; + // 'super' - Add parent path + // - TODO: Handle nested modules correctly. + case AST::Path::SUPER: { + if( modpath.size() < 1 ) + throw ParseError::Generic("Encountered 'super' at crate root"); + auto newpath = modpath; + newpath.nodes().pop_back(); + newpath += p; + DEBUG("Absolutised path " << p << " into " << newpath); + p = ::std::move(newpath); + break; } + // 'self' - Add parent path + // - TODO: Handle nested modules correctly. + case AST::Path::SELF: { + auto newpath = modpath + p; + // TODO: Undo anon modules until item is found + DEBUG("Absolutised path " << p << " into " << newpath); + p = ::std::move(newpath); + break; } + // Any other class is an error + default: + throw ParseError::Generic( FMT("Invalid path type encounted in 'use' : " << p) ); } // Run resolution on import -- cgit v1.2.3