├── .gitignore ├── Makefile ├── Cargo.toml ├── src ├── parser.lalrpop ├── ast.rs ├── lib.rs └── resolve.rs ├── Cargo.lock └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | target 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | rustc --test src/resolve.rs 3 | RUST_LOG=resolve::nameresolution,resolve::test ./resolve 4 | 5 | TAGS: 6 | etags src/*.rs 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "name-resolution-algorithm" 3 | version = "0.1.0" 4 | authors = ["Niko Matsakis "] 5 | build = "build.rs" # LALRPOP preprocessing 6 | 7 | # Add a dependency on the LALRPOP runtime library: 8 | [dependencies.lalrpop-util] 9 | version = "0.8.0" 10 | 11 | [build-dependencies.lalrpop] 12 | version = "0.8.0" 13 | 14 | [dependencies] 15 | lalrpop-intern = "0.8.0" -------------------------------------------------------------------------------- /src/parser.lalrpop: -------------------------------------------------------------------------------- 1 | use ast::*; 2 | use intern::{intern, InternedString}; 3 | 4 | grammar(krate: &mut Krate); 5 | 6 | pub Krate: () = { 7 | => krate.modules[ROOT_ID.0].items = items 8 | }; 9 | 10 | Item: ItemId = { 11 | Module => ItemId::Module(<>), 12 | Structure => ItemId::Structure(<>), 13 | Import => ItemId::Import(<>), 14 | Glob => ItemId::Glob(<>), 15 | MacroDef => ItemId::MacroDef(<>), 16 | MacroRef => ItemId::MacroRef(<>), 17 | Code => ItemId::Code(<>), 18 | }; 19 | 20 | Module: ModuleId = { 21 | "mod" "{" "}" => 22 | krate.add_module(Module { privacy: privacy, name: name, items: items }), 23 | }; 24 | 25 | Structure: StructureId = { 26 | "struct" "{" "}" => 27 | krate.add_structure(Structure { privacy: privacy, name: name }), 28 | }; 29 | 30 | Import: ImportId = { 31 | "use" )?> ";" => 32 | krate.add_import(Import { privacy: privacy, path: path, alt_name: a }), 33 | }; 34 | 35 | Glob: GlobId = { 36 | "use" "::" "*" ";" => 37 | krate.add_glob(Glob { privacy: privacy, path: path, }), 38 | }; 39 | 40 | MacroDef: MacroDefId = { 41 | "macro_rules" "!" "{" "}" => 42 | krate.add_macro_def(MacroDef { privacy: privacy, name: name, items: items }), 43 | }; 44 | 45 | MacroRef: MacroRefId = { 46 | "!" ";" => 47 | krate.add_macro_ref(MacroRef { path: path }), 48 | }; 49 | 50 | Code: CodeId = { 51 | "{" ";")*> "}" => 52 | krate.add_code(Code { paths: paths }), 53 | }; 54 | 55 | Path: PathId = { 56 | "self" "::" => krate.add_path(Path::Cons(THIS_PATH, <>)), 57 | "::" => krate.add_path(Path::Cons(ROOT_PATH, <>)), 58 | Id => krate.add_path(Path::Cons(ROOT_PATH, <>)), 59 | "::" => krate.add_path(Path::Cons(<>)), 60 | }; 61 | 62 | Privacy: Privacy = { 63 | "pub" => Privacy::Pub, 64 | () => Privacy::Priv, 65 | }; 66 | 67 | Id: InternedString = { 68 | r"[a-zA-Z_][a-zA-Z0-9_]*" => intern(<>), 69 | }; 70 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "name-resolution-algorithm" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "lalrpop 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "lalrpop-intern 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "lalrpop-util 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 8 | ] 9 | 10 | [[package]] 11 | name = "advapi32-sys" 12 | version = "0.1.2" 13 | source = "registry+https://github.com/rust-lang/crates.io-index" 14 | dependencies = [ 15 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "aho-corasick" 21 | version = "0.3.4" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | dependencies = [ 24 | "memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 25 | ] 26 | 27 | [[package]] 28 | name = "diff" 29 | version = "0.1.7" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | 32 | [[package]] 33 | name = "fixedbitset" 34 | version = "0.0.5" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | 37 | [[package]] 38 | name = "itertools" 39 | version = "0.3.25" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | 42 | [[package]] 43 | name = "kernel32-sys" 44 | version = "0.2.1" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | dependencies = [ 47 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 48 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 49 | ] 50 | 51 | [[package]] 52 | name = "lalrpop" 53 | version = "0.8.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | dependencies = [ 56 | "diff 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 57 | "itertools 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "lalrpop-intern 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 59 | "lalrpop-snap 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 60 | "lalrpop-util 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 61 | "petgraph 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 62 | "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 63 | "term 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "unicode-xid 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 65 | ] 66 | 67 | [[package]] 68 | name = "lalrpop-intern" 69 | version = "0.8.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | 72 | [[package]] 73 | name = "lalrpop-snap" 74 | version = "0.8.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | dependencies = [ 77 | "diff 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "itertools 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", 79 | "lalrpop-intern 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "lalrpop-util 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "rusty-peg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "term 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 85 | "unicode-xid 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 86 | ] 87 | 88 | [[package]] 89 | name = "lalrpop-util" 90 | version = "0.8.0" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | 93 | [[package]] 94 | name = "libc" 95 | version = "0.2.1" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | 98 | [[package]] 99 | name = "memchr" 100 | version = "0.1.7" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | dependencies = [ 103 | "libc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 104 | ] 105 | 106 | [[package]] 107 | name = "petgraph" 108 | version = "0.1.12" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | dependencies = [ 111 | "fixedbitset 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 112 | ] 113 | 114 | [[package]] 115 | name = "rand" 116 | version = "0.3.12" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | dependencies = [ 119 | "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 120 | "libc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 121 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 122 | ] 123 | 124 | [[package]] 125 | name = "regex" 126 | version = "0.1.41" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | dependencies = [ 129 | "aho-corasick 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 130 | "memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 131 | "regex-syntax 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 132 | ] 133 | 134 | [[package]] 135 | name = "regex-syntax" 136 | version = "0.2.2" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | 139 | [[package]] 140 | name = "rusty-peg" 141 | version = "0.3.0" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | dependencies = [ 144 | "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 145 | ] 146 | 147 | [[package]] 148 | name = "term" 149 | version = "0.2.13" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | dependencies = [ 152 | "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 153 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 154 | ] 155 | 156 | [[package]] 157 | name = "unicode-xid" 158 | version = "0.0.2" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | 161 | [[package]] 162 | name = "winapi" 163 | version = "0.2.5" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | 166 | [[package]] 167 | name = "winapi-build" 168 | version = "0.1.1" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | 171 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use intern::{intern, InternedString}; 4 | 5 | pub struct Krate { 6 | pub modules: Vec, 7 | pub structures: Vec, 8 | pub imports: Vec, 9 | pub globs: Vec, 10 | pub macro_defs: Vec, 11 | pub macro_refs: Vec, 12 | pub macro_husks: Vec, 13 | pub paths: Vec, 14 | pub codes: Vec, 15 | } 16 | 17 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 18 | pub enum ItemId { 19 | Module(ModuleId), 20 | Structure(StructureId), 21 | Import(ImportId), 22 | Glob(GlobId), 23 | MacroDef(MacroDefId), 24 | MacroRef(MacroRefId), 25 | MacroHusk(MacroHuskId), 26 | Code(CodeId), 27 | } 28 | 29 | pub const ROOT_ID: ModuleId = ModuleId(0); 30 | pub const ROOT_PATH: PathId = PathId(0); 31 | pub const THIS_PATH: PathId = PathId(1); 32 | 33 | impl Krate { 34 | pub fn new() -> Krate { 35 | Krate { 36 | modules: vec![Module { privacy: Privacy::Pub, name: intern(""), items: vec![] }], 37 | structures: vec![], 38 | imports: vec![], 39 | globs: vec![], 40 | macro_defs: vec![], 41 | macro_refs: vec![], 42 | macro_husks: vec![], 43 | paths: vec![Path::Root, Path::This], 44 | codes: vec![], 45 | } 46 | } 47 | 48 | pub fn module_ids(&self) -> Vec { 49 | (0..self.modules.len()).map(|i| ModuleId(i)).collect() 50 | } 51 | 52 | pub fn add_module(&mut self, module: Module) -> ModuleId { 53 | self.modules.push(module); 54 | ModuleId(self.modules.len() - 1) 55 | } 56 | 57 | pub fn add_structure(&mut self, structure: Structure) -> StructureId { 58 | self.structures.push(structure); 59 | StructureId(self.structures.len() - 1) 60 | } 61 | 62 | pub fn add_import(&mut self, import: Import) -> ImportId { 63 | self.imports.push(import); 64 | ImportId(self.imports.len() - 1) 65 | } 66 | 67 | pub fn add_glob(&mut self, glob: Glob) -> GlobId { 68 | self.globs.push(glob); 69 | GlobId(self.globs.len() - 1) 70 | } 71 | 72 | pub fn add_macro_def(&mut self, macro_def: MacroDef) -> MacroDefId { 73 | self.macro_defs.push(macro_def); 74 | MacroDefId(self.macro_defs.len() - 1) 75 | } 76 | 77 | pub fn add_macro_ref(&mut self, macro_ref: MacroRef) -> MacroRefId { 78 | self.macro_refs.push(macro_ref); 79 | MacroRefId(self.macro_refs.len() - 1) 80 | } 81 | 82 | pub fn add_macro_husk(&mut self, macro_husk: MacroHusk) -> MacroHuskId { 83 | self.macro_husks.push(macro_husk); 84 | MacroHuskId(self.macro_husks.len() - 1) 85 | } 86 | 87 | pub fn add_code(&mut self, code: Code) -> CodeId { 88 | self.codes.push(code); 89 | CodeId(self.codes.len() - 1) 90 | } 91 | 92 | pub fn add_path(&mut self, path: Path) -> PathId { 93 | self.paths.push(path); 94 | PathId(self.paths.len() - 1) 95 | } 96 | 97 | pub fn import_name(&self, import_id: ImportId) -> InternedString { 98 | let import = &self.imports[import_id.0]; 99 | import.alt_name.unwrap_or_else(|| { 100 | match self.paths[import.path.0] { 101 | Path::Root | Path::This => unreachable!(), // grammar doesn't allow this 102 | Path::Cons(_, s) => s, 103 | } 104 | }) 105 | } 106 | } 107 | 108 | /////////////////////////////////////////////////////////////////////////// 109 | 110 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 111 | pub enum Privacy { 112 | Priv, 113 | Pub, 114 | } 115 | 116 | /////////////////////////////////////////////////////////////////////////// 117 | 118 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 119 | pub struct ModuleId(pub usize); 120 | 121 | #[derive(Debug)] 122 | pub struct Module { 123 | pub privacy: Privacy, 124 | pub name: InternedString, 125 | pub items: Vec, 126 | } 127 | 128 | /////////////////////////////////////////////////////////////////////////// 129 | 130 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 131 | pub struct StructureId(pub usize); 132 | 133 | #[derive(Debug)] 134 | pub struct Structure { 135 | pub privacy: Privacy, 136 | pub name: InternedString, 137 | } 138 | 139 | /////////////////////////////////////////////////////////////////////////// 140 | 141 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 142 | pub struct ImportId(pub usize); 143 | 144 | pub struct Import { 145 | pub privacy: Privacy, 146 | pub path: PathId, 147 | pub alt_name: Option, 148 | } 149 | 150 | /////////////////////////////////////////////////////////////////////////// 151 | 152 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 153 | pub struct GlobId(pub usize); 154 | 155 | pub struct Glob { 156 | pub privacy: Privacy, 157 | pub path: PathId, 158 | } 159 | 160 | /////////////////////////////////////////////////////////////////////////// 161 | 162 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 163 | pub struct MacroDefId(pub usize); 164 | 165 | pub struct MacroDef { 166 | pub privacy: Privacy, 167 | pub name: InternedString, 168 | pub items: Vec, 169 | } 170 | 171 | /////////////////////////////////////////////////////////////////////////// 172 | 173 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 174 | pub struct MacroRefId(pub usize); 175 | 176 | pub struct MacroRef { 177 | pub path: PathId, 178 | } 179 | 180 | /////////////////////////////////////////////////////////////////////////// 181 | 182 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 183 | pub struct MacroHuskId(pub usize); 184 | 185 | pub struct MacroHusk { 186 | pub path: PathId, 187 | pub macro_def_id: MacroDefId, 188 | } 189 | 190 | /////////////////////////////////////////////////////////////////////////// 191 | 192 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 193 | pub struct CodeId(pub usize); 194 | 195 | pub struct Code { 196 | pub paths: Vec, 197 | } 198 | 199 | /////////////////////////////////////////////////////////////////////////// 200 | 201 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 202 | pub struct PathId(pub usize); 203 | 204 | #[derive(Clone)] 205 | pub enum Path { 206 | Root, 207 | This, 208 | Cons(PathId, InternedString), 209 | } 210 | 211 | 212 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), allow(dead_code, unused_imports))] 2 | 3 | extern crate lalrpop_intern as intern; 4 | 5 | macro_rules! debug { 6 | ($($args:tt)*) => { 7 | println!($($args)*); 8 | } 9 | } 10 | 11 | mod ast; 12 | mod parser; 13 | mod resolve; 14 | 15 | use ast::*; 16 | use intern::intern; 17 | use resolve::ResolutionError; 18 | use parser::parse_Krate; 19 | 20 | #[test] 21 | fn basic_example() { 22 | let mut krate = ast::Krate::new(); 23 | parse_Krate(&mut krate, r#" 24 | mod foo { use bar::Struct; } 25 | mod bar { struct Struct { } } 26 | "#).unwrap(); 27 | resolve::resolve_and_expand(&mut krate).unwrap(); 28 | } 29 | 30 | #[test] 31 | #[should_panic] 32 | fn basic_error() { 33 | let mut krate = ast::Krate::new(); 34 | parse_Krate(&mut krate, r#" 35 | mod foo { use bar::StructX; } 36 | mod bar { struct Struct { } } 37 | "#).unwrap(); 38 | resolve::resolve_and_expand(&mut krate).unwrap(); 39 | } 40 | 41 | #[test] 42 | fn precedence_over_glob() { 43 | let mut krate = ast::Krate::new(); 44 | parse_Krate(&mut krate, r#" 45 | mod foo { use bar::*; struct Struct { } } 46 | mod bar { struct Struct { } } 47 | "#).unwrap(); 48 | let result = resolve::resolve_and_expand(&mut krate); 49 | debug!("result = {:?}", result); 50 | assert!(result.is_ok()); 51 | } 52 | 53 | #[test] 54 | fn precedence_over_glob_macro() { 55 | let mut krate = ast::Krate::new(); 56 | // Here the macro m! generates a struct; this conflicts with 57 | // the struct we get from the glob, even though an explicit name would not.q 58 | parse_Krate(&mut krate, r#" 59 | mod foo { use bar::*; m!; } 60 | mod bar { macro_rules! m { struct Struct { } } struct Struct { } } 61 | "#).unwrap(); 62 | let result = resolve::resolve_and_expand(&mut krate); 63 | debug!("result = {:?}", result); 64 | assert!(result.is_err()); 65 | } 66 | 67 | #[test] 68 | fn precedence_over_glob_macro_2() { 69 | let mut krate = ast::Krate::new(); 70 | // Here the macro m! generates a struct; this conflicts with 71 | // the struct we get from the glob, even though an explicit name would not.q 72 | parse_Krate(&mut krate, r#" 73 | mod foo { use bar::*; m!; } 74 | mod bar { macro_rules! m { struct Struct { } } m!; } 75 | "#).unwrap(); 76 | let result = resolve::resolve_and_expand(&mut krate); 77 | debug!("result = {:?}", result); 78 | assert!(result.is_err()); 79 | } 80 | 81 | #[test] 82 | fn multiple_names() { 83 | let mut krate = ast::Krate::new(); 84 | parse_Krate(&mut krate, r#" 85 | mod foo { use bar::Struct; struct Struct { } } 86 | mod bar { struct Struct { } } 87 | "#).unwrap(); 88 | let result = resolve::resolve_and_expand(&mut krate); 89 | debug!("result = {:?}", result); 90 | assert!(result.is_err()); 91 | } 92 | 93 | #[test] 94 | fn with_macro() { 95 | let mut krate = ast::Krate::new(); 96 | parse_Krate(&mut krate, r#" 97 | mod foo { use bar::*; self::make_struct!; } 98 | mod bar { macro_rules! make_struct { struct Struct { } } } 99 | mod baz { use foo::Struct; } 100 | "#).unwrap(); 101 | let result = resolve::resolve_and_expand(&mut krate); 102 | debug!("result = {:?}", result); 103 | assert!(match result { 104 | Ok(_) => true, 105 | _ => false, 106 | }); 107 | } 108 | 109 | #[test] 110 | fn with_macro_multiple() { 111 | let mut krate = ast::Krate::new(); 112 | parse_Krate(&mut krate, r#" 113 | mod foo { use bar::*; self::make_struct!; struct Struct { } } 114 | mod bar { macro_rules! make_struct { struct Struct { } } } 115 | mod baz { use foo::Struct; } 116 | "#).unwrap(); 117 | let result = resolve::resolve_and_expand(&mut krate); 118 | debug!("result = {:?}", result); 119 | assert!(match result { 120 | Err(resolve::ResolutionError::MultipleNames { module_id, name }) => { 121 | module_id == ModuleId(1) && name == intern("Struct") 122 | } 123 | _ => { 124 | false 125 | } 126 | }); 127 | } 128 | 129 | #[test] 130 | fn with_macro_1() { 131 | let mut krate = ast::Krate::new(); 132 | parse_Krate(&mut krate, r#" 133 | mod foo { use bar::*; self::make_struct!; } 134 | mod bar { macro_rules! make_struct { struct Struct { } } } 135 | mod baz { use foo::Struct; } 136 | "#).unwrap(); 137 | let result = resolve::resolve_and_expand(&mut krate); 138 | debug!("result = {:?}", result); 139 | assert!(match result { 140 | Ok(_) => true, 141 | _ => false, 142 | }); 143 | } 144 | 145 | #[test] 146 | fn with_macro_making_mod_making_macro() { 147 | let mut krate = ast::Krate::new(); 148 | 149 | // Here, the macro m! generates mod x { n! }, and then invoking n! generates Struct 150 | parse_Krate(&mut krate, r#" 151 | mod foo { use bar::*; self::m!; foo::x::n!; } 152 | mod bar { macro_rules! m { mod x { macro_rules! n { struct Struct { } } } } } 153 | mod baz { use foo::Struct; } 154 | "#).unwrap(); 155 | let result = resolve::resolve_and_expand(&mut krate); 156 | debug!("result = {:?}", result); 157 | assert!(match result { 158 | Ok(_) => true, 159 | _ => false, 160 | }); 161 | } 162 | 163 | #[test] 164 | fn with_macro_making_mod_making_macro_that_conflicts() { 165 | let mut krate = ast::Krate::new(); 166 | 167 | // Here, the macro m! generates mod x { n! }, and then invoking n! 168 | // generates a (conflicting) definition for `m`. Mind-bending. 169 | parse_Krate(&mut krate, r#" 170 | mod foo { use bar::*; self::m!; foo::x::n!; } 171 | mod bar { macro_rules! m { mod x { macro_rules! n { macro_rules! m { } struct Struct { } } } } } 172 | mod baz { use foo::Struct; } 173 | "#).unwrap(); 174 | let result = resolve::resolve_and_expand(&mut krate); 175 | debug!("result = {:?}", result); 176 | assert!(result.is_err()); 177 | } 178 | 179 | #[test] 180 | fn simple_cycle() { 181 | let mut krate = ast::Krate::new(); 182 | 183 | // Here, the macro m! generates mod x { n! }, and then invoking n! 184 | // generates a (conflicting) definition for `m`. Mind-bending. 185 | parse_Krate(&mut krate, r#" 186 | mod foo { use bar::x; } 187 | mod bar { use foo::x; } 188 | "#).unwrap(); 189 | let result = resolve::resolve_and_expand(&mut krate); 190 | debug!("result = {:?}", result); 191 | assert!(match result { 192 | Err(resolve::ResolutionError::InvalidPath { .. }) => true, 193 | _ => false, 194 | }); 195 | } 196 | 197 | #[test] 198 | fn if_a_glob_conflicts_in_a_forest() { 199 | let mut krate = ast::Krate::new(); 200 | 201 | // Here, the macro m! generates mod x { n! }, and then invoking n! 202 | // generates a (conflicting) definition for `m`. Mind-bending. 203 | parse_Krate(&mut krate, r#" 204 | mod foo { use bar::*; use baz::*; { self::T; } } 205 | mod bar { struct S { } struct T { } } 206 | mod baz { struct S { } } 207 | "#).unwrap(); 208 | let result = resolve::resolve_and_expand(&mut krate); 209 | debug!("result = {:?}", result); 210 | assert!(result.is_ok()); 211 | } 212 | 213 | #[test] 214 | fn if_a_glob_conflicts_in_a_forest_but_someone_sees() { 215 | let mut krate = ast::Krate::new(); 216 | 217 | // Here, the macro m! generates mod x { n! }, and then invoking n! 218 | // generates a (conflicting) definition for `m`. Mind-bending. 219 | parse_Krate(&mut krate, r#" 220 | mod foo { use bar::*; use baz::*; { self::S; } } 221 | mod bar { struct S { } struct T { } } 222 | mod baz { struct S { } } 223 | "#).unwrap(); 224 | let result = resolve::resolve_and_expand(&mut krate); 225 | debug!("result = {:?}", result); 226 | assert!(match result { 227 | Err(resolve::ResolutionError::InvalidPath { .. }) => true, 228 | _ => false, 229 | }); 230 | } 231 | 232 | #[test] 233 | fn expand_to_conflicting_macro() { 234 | let mut krate = ast::Krate::new(); 235 | 236 | // Here the macro m! generates a conflict entry for m!. But we 237 | // have to make sure that this results in an error. 238 | 239 | parse_Krate(&mut krate, r#" 240 | mod foo { use bar::*; self::m!; } 241 | mod bar { macro_rules! m { macro_rules! m { struct S { } } } } 242 | "#).unwrap(); 243 | let result = resolve::resolve_and_expand(&mut krate); 244 | debug!("result = {:?}", result); 245 | assert!(result.is_err()); 246 | } 247 | 248 | 249 | #[test] 250 | fn expand_to_conflicting_globs() { 251 | let mut krate = ast::Krate::new(); 252 | 253 | // Here the macro m! generates a conflict entry for m!. But we 254 | // have to make sure that this results in an error. 255 | 256 | parse_Krate(&mut krate, r#" 257 | mod foo { use bar::*; self::m!; } 258 | mod bar { macro_rules! m { use baz::*; } } 259 | mod baz { macro_rules! m { } } 260 | "#).unwrap(); 261 | let result = resolve::resolve_and_expand(&mut krate); 262 | debug!("result = {:?}", result); 263 | assert!(result.is_err()); 264 | } 265 | 266 | #[test] 267 | fn banning_macro_globs_is_not_enough() { 268 | let mut krate = ast::Krate::new(); 269 | 270 | // Here the macro m! generates a conflict entry for m!. But we 271 | // have to make sure that this results in an error. 272 | 273 | parse_Krate(&mut krate, r#" 274 | mod a { 275 | use b::c::n; 276 | self::n!; 277 | } 278 | mod b { 279 | use c::*; 280 | macro_rules! m { 281 | mod d { 282 | macro_rules! n { } 283 | } 284 | } 285 | self::m!; 286 | } 287 | mod c { 288 | mod d { 289 | macro_rules! n { } 290 | } 291 | } 292 | "#).unwrap(); 293 | let result = resolve::resolve_and_expand(&mut krate); 294 | debug!("result = {:?}", result); 295 | assert!(match result { 296 | Err(ResolutionError::InvalidPath { .. }) => true, 297 | _ => false 298 | }); 299 | } 300 | 301 | #[test] 302 | fn cyclic_macro_defs() { 303 | let mut krate = ast::Krate::new(); 304 | 305 | // Check that no error results if `a` and `b` use each other's 306 | // macros. 307 | 308 | parse_Krate(&mut krate, r#" 309 | mod a { 310 | use b::*; 311 | pub macro_rules! n { 312 | struct B { } 313 | } 314 | self::m!; 315 | } 316 | mod b { 317 | use a::*; 318 | pub macro_rules! m { 319 | struct A { } 320 | } 321 | self::n!; 322 | } 323 | mod c { 324 | use b::B; 325 | use a::A; 326 | { self::B; self::A; } 327 | } 328 | "#).unwrap(); 329 | let result = resolve::resolve_and_expand(&mut krate); 330 | debug!("result = {:?}", result); 331 | assert!(result.is_ok()); 332 | } 333 | 334 | #[test] 335 | fn glob_brings_module_which_has_macro() { 336 | let mut krate = ast::Krate::new(); 337 | parse_Krate(&mut krate, r#" 338 | mod a { 339 | use b::*; 340 | self::c::m!; 341 | } 342 | mod b { 343 | mod c { 344 | pub macro_rules! m { } 345 | } 346 | } 347 | "#).unwrap(); 348 | let result = resolve::resolve_and_expand(&mut krate); 349 | debug!("result = {:?}", result); 350 | assert!(result.is_ok()); 351 | } 352 | 353 | #[test] 354 | fn named_import_takes_prec_over_glob_even_if_we_cannot_yet_resolve() { 355 | let mut krate = ast::Krate::new(); 356 | 357 | // Here the `use b::c` cannot be resolved in round 1, but it still 358 | // tells us not to bring in `d::c` from the `d::*` glob. 359 | 360 | parse_Krate(&mut krate, r#" 361 | mod a { 362 | use b::c; 363 | use d::*; 364 | self::c::m!; 365 | } 366 | mod b { 367 | macro_rules! make_c { 368 | mod c { pub macro_rules! m { } } 369 | } 370 | self::make_c!; 371 | } 372 | mod d { 373 | mod c { } 374 | } 375 | "#).unwrap(); 376 | let result = resolve::resolve_and_expand(&mut krate); 377 | debug!("result = {:?}", result); 378 | assert!(result.is_ok()); 379 | } 380 | 381 | #[test] 382 | fn macro_can_expand_to_fix_errors_observed_earlier() { 383 | let mut krate = ast::Krate::new(); 384 | 385 | // The globs are in conflict, and hence resolving `self::b::m!` 386 | // would be an error. We ignore this error during macro resolution 387 | // and instead expand `self::make_b`. This creates the `mod b` 388 | // that overrides the globs and hence we are happy. This is kind 389 | // of odd though because it's a sort of time-travel, but one in 390 | // which the error we observed was resolved happily. 391 | 392 | parse_Krate(&mut krate, r#" 393 | mod a { 394 | use c::*; 395 | use d::*; 396 | use e::*; 397 | 398 | self::b::m!; 399 | 400 | macro_rules! make_b { 401 | mod b { 402 | macro_rules! m { } 403 | } 404 | } 405 | 406 | self::make_b!; 407 | } 408 | mod c { 409 | mod b { } 410 | } 411 | mod d { 412 | mod b { } 413 | } 414 | mod e { 415 | mod b { } 416 | } 417 | "#).unwrap(); 418 | let result = resolve::resolve_and_expand(&mut krate); 419 | debug!("result = {:?}", result); 420 | assert!(result.is_ok()); 421 | } 422 | 423 | #[test] 424 | fn using_self_import_to_fix_time_travel() { 425 | let mut krate = ast::Krate::new(); 426 | 427 | // This is a time-travel violation. 428 | 429 | parse_Krate(&mut krate, r#" 430 | mod a { 431 | use c::*; 432 | 433 | self::b::m!; 434 | 435 | macro_rules! make_b { 436 | mod b { 437 | macro_rules! m { } 438 | } 439 | } 440 | 441 | self::make_b!; 442 | } 443 | mod c { 444 | mod b { 445 | macro_rules! m { } 446 | } 447 | } 448 | "#).unwrap(); 449 | let result = resolve::resolve_and_expand(&mut krate); 450 | debug!("result = {:?}", result); 451 | assert!(result.is_err()); 452 | 453 | // This is not, because the `use self::b` takes precedence over 454 | // the glob, even though it can't be resolved right away (until 455 | // after first round of macro expansion, in fact). This is handy, 456 | // if maybe a bit odd. 457 | 458 | let mut krate = Krate::new(); 459 | parse_Krate(&mut krate, r#" 460 | mod a { 461 | use c::*; 462 | use self::b; 463 | 464 | self::b::m!; 465 | 466 | macro_rules! make_b { 467 | mod b { 468 | macro_rules! m { } 469 | } 470 | } 471 | 472 | self::make_b!; 473 | } 474 | mod c { 475 | mod b { 476 | macro_rules! m { } 477 | } 478 | } 479 | "#).unwrap(); 480 | let result = resolve::resolve_and_expand(&mut krate); 481 | debug!("result = {:?}", result); 482 | assert!(result.is_ok()); 483 | } 484 | 485 | 486 | #[test] 487 | fn macro_can_expand_to_change_how_path_is_resolved() { 488 | let mut krate = ast::Krate::new(); 489 | 490 | // In phase 1, self::b::m resolves through the glob, 491 | // but after that the `b` is different, but the `m` 492 | // is the same. 493 | 494 | parse_Krate(&mut krate, r#" 495 | mod a { 496 | use c::*; 497 | self::b::m!; 498 | } 499 | mod c { 500 | mod b { 501 | macro_rules! m { 502 | mod b { 503 | pub use c::b::m; 504 | } 505 | } 506 | } 507 | } 508 | "#).unwrap(); 509 | let result = resolve::resolve_and_expand(&mut krate); 510 | debug!("result = {:?}", result); 511 | assert!(result.is_ok()); 512 | } 513 | -------------------------------------------------------------------------------- /src/resolve.rs: -------------------------------------------------------------------------------- 1 | use ast::*; 2 | use intern::InternedString; 3 | use std::collections::{HashMap, HashSet}; 4 | use std::mem; 5 | 6 | #[derive(Debug)] 7 | struct ModuleContentSets { 8 | ticker: usize, 9 | module_contents: HashMap, 10 | } 11 | 12 | #[derive(Clone, Debug)] 13 | pub struct ModuleContents { 14 | pub members: HashMap>, 15 | } 16 | 17 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 18 | pub enum NameResolution { 19 | Seed(ItemId), 20 | Placeholder, 21 | Glob(ItemId), 22 | } 23 | 24 | pub enum Resolution { 25 | Zero, 26 | One(ItemId), 27 | Many, 28 | Error, 29 | } 30 | 31 | #[derive(Debug)] 32 | pub enum ResolutionError { 33 | MultipleNames { 34 | module_id: ModuleId, 35 | name: InternedString, 36 | }, 37 | 38 | InvalidPath { 39 | path: PathId, 40 | source: ItemId, 41 | }, 42 | 43 | TimeTravel { 44 | path: PathId, 45 | was_macro: MacroDefId, 46 | now_item: ItemId, 47 | } 48 | } 49 | 50 | pub fn resolve_and_expand(krate: &mut Krate) -> Result, ResolutionError> { 51 | let mut resolutions; 52 | loop { 53 | let mut ticker = 0; 54 | resolutions = ModuleContentSets::new(); 55 | while resolutions.changed_since(&mut ticker) { 56 | seed_names(krate, &mut resolutions); 57 | glob_names(krate, &mut resolutions); 58 | } 59 | 60 | if !expand_macros(krate, &mut resolutions) { 61 | break; 62 | } 63 | } 64 | try!(verify_paths(krate, &resolutions)); 65 | Ok(resolutions.module_contents) 66 | } 67 | 68 | fn expand_macros(krate: &mut Krate, resolutions: &ModuleContentSets) -> bool { 69 | debug!("expand_macros()"); 70 | let module_ids = krate.module_ids(); 71 | let mut expanded = false; 72 | for container_id in module_ids { 73 | debug!("expand_macros: container_id={:?}", container_id); 74 | let mut new_items = vec![]; 75 | for &item_id in &krate.modules[container_id.0].items { 76 | match item_id { 77 | ItemId::MacroRef(macro_ref_id) => { 78 | let macro_path = krate.macro_refs[macro_ref_id.0].path; 79 | match resolutions.resolve_path(&krate.paths, container_id, macro_path) { 80 | Resolution::One(ItemId::MacroDef(macro_def_id)) => { 81 | debug!("expand_macros: macro traced to {:?}", macro_def_id); 82 | let macro_def = &krate.macro_defs[macro_def_id.0]; 83 | krate.macro_husks.push(MacroHusk { path: macro_path, 84 | macro_def_id: macro_def_id }); 85 | let macro_husk_id = MacroHuskId(krate.macro_husks.len() - 1); 86 | expanded = true; 87 | new_items.extend( 88 | macro_def.items.iter() 89 | .cloned() 90 | .chain(Some(ItemId::MacroHusk(macro_husk_id)))); 91 | } 92 | _ => { 93 | // don't really care about errors 94 | // right now, just don't expand 95 | new_items.push(item_id); 96 | } 97 | } 98 | } 99 | _ => { 100 | new_items.push(item_id); 101 | } 102 | } 103 | } 104 | 105 | krate.modules[container_id.0].items = new_items; 106 | } 107 | 108 | expanded 109 | } 110 | 111 | fn seed_names(krate: &Krate, resolutions: &mut ModuleContentSets) { 112 | debug!("seed_names()"); 113 | for container_id in krate.module_ids() { 114 | let module = &krate.modules[container_id.0]; 115 | debug!("seed_names: container_id={:?} module.name={:?}", container_id, module.name); 116 | for &item_id in &module.items { 117 | match item_id { 118 | ItemId::Module(module_id) => { 119 | let module = &krate.modules[module_id.0]; 120 | resolutions.seed(container_id, module.name, item_id); 121 | } 122 | 123 | ItemId::Structure(structure_id) => { 124 | let structure = &krate.structures[structure_id.0]; 125 | resolutions.seed(container_id, structure.name, item_id); 126 | } 127 | 128 | ItemId::Import(import_id) => { 129 | let import = &krate.imports[import_id.0]; 130 | let name = krate.import_name(import_id); 131 | match resolutions.resolve_path(&krate.paths, container_id, import.path) { 132 | Resolution::One(target_id) => { 133 | resolutions.seed(container_id, name, target_id); 134 | } 135 | _ => { 136 | // don't care about invalid or incomplete 137 | // paths right now, but we do want to 138 | // insert a placeholder so globs don't 139 | // take this name later 140 | resolutions.placeholder(container_id, name); 141 | } 142 | } 143 | } 144 | 145 | ItemId::MacroDef(macro_def_id) => { 146 | let macro_def = &krate.macro_defs[macro_def_id.0]; 147 | resolutions.seed(container_id, macro_def.name, item_id); 148 | } 149 | 150 | ItemId::Glob(_) | 151 | ItemId::MacroRef(_) | 152 | ItemId::MacroHusk(_) | 153 | ItemId::Code(_) => { 154 | } 155 | } 156 | } 157 | } 158 | } 159 | 160 | fn glob_names(krate: &Krate, resolutions: &mut ModuleContentSets) { 161 | let mut ticker = 0; 162 | while resolutions.changed_since(&mut ticker) { 163 | debug!("glob_names() ticker={:?} resolutions.tick={:?}", ticker, resolutions.ticker); 164 | for container_id in krate.module_ids() { 165 | let module = &krate.modules[container_id.0]; 166 | debug!("glob_names: container_id={:?}, module.name={:?}", 167 | container_id, module.name); 168 | 169 | for &item_id in &module.items { 170 | match item_id { 171 | ItemId::Glob(glob_id) => { 172 | let glob = &krate.globs[glob_id.0]; 173 | let glob_members = resolutions.resolve_glob(&krate.paths, 174 | container_id, 175 | glob.path); 176 | for (name, resolution) in glob_members { 177 | match resolution { 178 | NameResolution::Seed(target_id) | 179 | NameResolution::Glob(target_id) => { 180 | resolutions.glob(container_id, name, target_id); 181 | } 182 | NameResolution::Placeholder => { 183 | } 184 | } 185 | } 186 | } 187 | 188 | ItemId::Module(_) | 189 | ItemId::Structure(_) | 190 | ItemId::Import(_) | 191 | ItemId::MacroDef(_) | 192 | ItemId::MacroRef(_) | 193 | ItemId::MacroHusk(_) | 194 | ItemId::Code(_) => { 195 | // Nothing to do here but verify the paths at the end. 196 | } 197 | } 198 | } 199 | } 200 | } 201 | } 202 | 203 | fn check_path(krate: &Krate, 204 | resolutions: &ModuleContentSets, 205 | module_id: ModuleId, 206 | source: ItemId, 207 | path: PathId) 208 | -> Result { 209 | match resolutions.resolve_path(&krate.paths, module_id, path) { 210 | Resolution::One(item_id) => { 211 | // path can be successfully resolved 212 | Ok(item_id) 213 | } 214 | _ => { 215 | // invalid or incomplete path 216 | Err(ResolutionError::InvalidPath { 217 | path: path, 218 | source: source, 219 | }) 220 | } 221 | } 222 | } 223 | 224 | fn check_decl(_krate: &Krate, 225 | resolutions: &ModuleContentSets, 226 | module_id: ModuleId, 227 | source: ItemId, 228 | name: InternedString) 229 | -> Result<(), ResolutionError> { 230 | match resolutions.resolve_name(module_id, name) { 231 | Resolution::One(id) => { 232 | // path can be successfully resolved 233 | assert_eq!(id, source); 234 | Ok(()) 235 | } 236 | _ => { 237 | // invalid or incomplete path 238 | Err(ResolutionError::MultipleNames { 239 | module_id: module_id, 240 | name: name, 241 | }) 242 | } 243 | } 244 | } 245 | 246 | fn verify_paths(krate: &Krate, resolutions: &ModuleContentSets) -> Result<(), ResolutionError> { 247 | for container_id in krate.module_ids() { 248 | let module = &krate.modules[container_id.0]; 249 | for &item_id in &module.items { 250 | match item_id { 251 | ItemId::Module(module_id) => { 252 | // this declares a name `S`. Therefore, resolving the path 253 | // `use self::S` should lead to this module. Check that. 254 | let module = &krate.modules[module_id.0]; 255 | try!(check_decl(krate, resolutions, container_id, item_id, module.name)); 256 | } 257 | ItemId::Structure(structure_id) => { 258 | // this declares a name `S`. Therefore, resolving the path 259 | // `use self::S` should lead to this structure. Check that. 260 | let structure = &krate.structures[structure_id.0]; 261 | try!(check_decl(krate, resolutions, container_id, item_id, structure.name)); 262 | } 263 | ItemId::Import(import_id) => { 264 | let import = &krate.imports[import_id.0]; 265 | try!(check_path(krate, resolutions, container_id, item_id, import.path)); 266 | } 267 | 268 | ItemId::Glob(glob_id) => { 269 | let glob = &krate.globs[glob_id.0]; 270 | try!(check_path(krate, resolutions, container_id, item_id, glob.path)); 271 | } 272 | 273 | ItemId::MacroDef(macro_def_id) => { 274 | // this declares a name `S`. Therefore, resolving the path 275 | // `use self::S` should lead to this macro_def. Check that. 276 | let macro_def = &krate.macro_defs[macro_def_id.0]; 277 | try!(check_decl(krate, resolutions, container_id, item_id, macro_def.name)); 278 | } 279 | 280 | ItemId::MacroHusk(macro_husk_id) => { 281 | let macro_husk = &krate.macro_husks[macro_husk_id.0]; 282 | let target = try!(check_path(krate, 283 | resolutions, 284 | container_id, 285 | item_id, 286 | macro_husk.path)); 287 | if target != ItemId::MacroDef(macro_husk.macro_def_id) { 288 | return Err(ResolutionError::TimeTravel { 289 | path: macro_husk.path, 290 | was_macro: macro_husk.macro_def_id, 291 | now_item: target 292 | }); 293 | } 294 | } 295 | 296 | ItemId::MacroRef(macro_ref_id) => { 297 | // this should not occur, must be an invalid path 298 | let macro_ref = &krate.macro_refs[macro_ref_id.0]; 299 | try!(check_path(krate, resolutions, container_id, item_id, macro_ref.path)); 300 | unreachable!(); 301 | } 302 | 303 | ItemId::Code(code_id) => { 304 | let code = &krate.codes[code_id.0]; 305 | for &path in &code.paths { 306 | try!(check_path(krate, resolutions, container_id, item_id, path)); 307 | } 308 | } 309 | } 310 | } 311 | } 312 | Ok(()) 313 | } 314 | 315 | impl ModuleContents { 316 | fn new() -> ModuleContents { 317 | ModuleContents { 318 | members: HashMap::new() 319 | } 320 | } 321 | } 322 | 323 | 324 | impl ModuleContentSets { 325 | fn new() -> ModuleContentSets { 326 | ModuleContentSets { 327 | module_contents: HashMap::new(), 328 | ticker: 1, 329 | } 330 | } 331 | 332 | fn changed_since(&self, ticker: &mut usize) -> bool { 333 | let t = mem::replace(ticker, self.ticker); 334 | self.ticker > t 335 | } 336 | 337 | fn tick(&mut self) { 338 | self.ticker += 1; 339 | } 340 | 341 | fn module_contents(&mut self, module_id: ModuleId) -> &mut ModuleContents { 342 | self.module_contents.entry(module_id) 343 | .or_insert_with(|| ModuleContents::new()) 344 | } 345 | 346 | fn seed(&mut self, 347 | module_id: ModuleId, 348 | member_name: InternedString, 349 | target_id: ItemId) { 350 | debug!("seed(in {:?}, {:?} => {:?})", module_id, member_name, target_id); 351 | let changed = { 352 | let contents = self.module_contents(module_id); 353 | let members = contents.members.entry(member_name).or_insert_with(|| HashSet::new()); 354 | 355 | // if we are seeding with this name, we can't have added a glob name yet 356 | assert!(!members.iter().any(|m| match *m { 357 | NameResolution::Glob(_) => true, 358 | _ => false 359 | })); 360 | 361 | members.insert(NameResolution::Seed(target_id)) 362 | }; 363 | 364 | if changed { 365 | self.tick(); 366 | } 367 | } 368 | 369 | fn placeholder(&mut self, 370 | module_id: ModuleId, 371 | member_name: InternedString) { 372 | debug!("placeholder(in {:?}, {:?})", module_id, member_name); 373 | let changed = { 374 | let contents = self.module_contents(module_id); 375 | let members = contents.members.entry(member_name).or_insert_with(|| HashSet::new()); 376 | 377 | // if we are seeding with a placeholder, we can't have added a glob name yet 378 | assert!(!members.iter().any(|m| match *m { 379 | NameResolution::Glob(_) => true, 380 | _ => false 381 | })); 382 | 383 | if members.is_empty() { 384 | members.insert(NameResolution::Placeholder) 385 | } else { 386 | false 387 | } 388 | }; 389 | 390 | if changed { 391 | self.tick(); 392 | } 393 | } 394 | 395 | 396 | fn glob(&mut self, 397 | module_id: ModuleId, 398 | member_name: InternedString, 399 | target_id: ItemId) { 400 | debug!("glob(in {:?}, {:?} => {:?})", module_id, member_name, target_id); 401 | let changed = { 402 | let contents = self.module_contents(module_id); 403 | let members = contents.members.entry(member_name).or_insert_with(|| HashSet::new()); 404 | 405 | // if we have only added globs (i.e., nothing was added during 406 | // seed phase), we can add more globs 407 | if members.iter().all(|m| match *m { NameResolution::Glob(_) => true, _ => false }) { 408 | if members.insert(NameResolution::Glob(target_id)) { 409 | true 410 | } else { 411 | false 412 | } 413 | } else { 414 | false 415 | } 416 | }; 417 | 418 | if changed { 419 | self.tick(); 420 | } 421 | } 422 | 423 | /// Given that `use a::b::c::*` occurs in some module `M`, returns 424 | /// the pairs `(name, resolution)` that appear in `a::b::c`. 425 | /// 426 | /// - `paths`: the list of paths from the krate 427 | /// - `container_id`: the module M 428 | /// - `globbed_path`; the path `a::b::c` 429 | fn resolve_glob(&mut self, 430 | paths: &[Path], 431 | container_id: ModuleId, 432 | globbed_path: PathId) 433 | -> Vec<(InternedString, NameResolution)> { 434 | match self.resolve_path(paths, container_id, globbed_path) { 435 | Resolution::One(ItemId::Module(target_id)) => { 436 | self.module_contents(target_id) 437 | .members 438 | .iter() 439 | .flat_map(|(&name, resolutions)| { 440 | resolutions.iter() 441 | .cloned() 442 | .map(move |r| (name, r)) 443 | }) 444 | .collect() 445 | } 446 | _ => { 447 | vec![] 448 | } 449 | } 450 | } 451 | 452 | fn resolve_path(&self, paths: &[Path], context: ModuleId, path: PathId) -> Resolution { 453 | match paths[path.0] { 454 | Path::Root => Resolution::One(ItemId::Module(ROOT_ID)), 455 | Path::This => Resolution::One(ItemId::Module(context)), 456 | Path::Cons(base, id) => { 457 | match self.resolve_path(paths, context, base) { 458 | Resolution::One(ItemId::Module(base_module_id)) => 459 | self.resolve_name(base_module_id, id), 460 | Resolution::One(_) => 461 | // if you have a path `a::b`, `a` had better 462 | // be a module. 463 | Resolution::Error, 464 | Resolution::Zero => 465 | Resolution::Zero, 466 | Resolution::Many => 467 | Resolution::Many, 468 | Resolution::Error => 469 | Resolution::Many, 470 | } 471 | } 472 | } 473 | } 474 | 475 | fn resolve_name(&self, module: ModuleId, name: InternedString) -> Resolution { 476 | match self.module_contents.get(&module) { 477 | None => Resolution::Zero, 478 | Some(rs) => match rs.members.get(&name) { 479 | None => Resolution::Zero, 480 | Some(nrs) => { 481 | let mut target_ids = nrs.iter().filter_map(|nr| match *nr { 482 | NameResolution::Placeholder => 483 | None, 484 | NameResolution::Seed(target_id) | NameResolution::Glob(target_id) => 485 | Some(target_id), 486 | }); 487 | match target_ids.next() { 488 | None => Resolution::Zero, 489 | Some(target_id) => match target_ids.next() { 490 | None => Resolution::One(target_id), 491 | Some(_) => Resolution::Many, 492 | } 493 | } 494 | } 495 | } 496 | } 497 | } 498 | } 499 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains a prototype for the new name resolution 2 | algorithm I would like to propose (and eventually implement) for Rust. 3 | It is a very flexible algorithm: globs are fully supported; macros can 4 | be used and impored like anything else; references to macros do not 5 | have to come "after" the definition of the macro in any sense; macros 6 | can define items (even other macro definitions) that can then be 7 | imported and used (even via globs). 8 | 9 | Here are the basic guidelines, ignoring macros for a moment: 10 | 11 | 1. Explicit imports and structs take precedence over globs. 12 | - So if I have `use foo::*;` and `use bar::X;`, then the name `X` 13 | always resolves to `bar::X`, even if `foo` contains a member 14 | named `X`. 15 | 2. It is always legal to import the same item through multiple paths 16 | (e.g., `use foo::X` and `use bar::X` are legal, so long as they 17 | both ultimately refer to the same `X`). 18 | 3. It is legal for two globs to conflict so long as the conflicting 19 | item is not referenced (e.g., a module can have both `use foo::*` 20 | and `use bar::*` even if both `foo` and `bar` define a struct `X`, 21 | so long as the module never refers to `X`). 22 | 23 | When we add macros into the mix, things get interesting. This is 24 | because macro expansion generates new items, which can in turn affect 25 | how paths are resolved. The current algorithm takes a kind of greedy 26 | expansion strategy: we repeatedly iterate and expand, in any given 27 | round, all macros whose names we can successfully resolve. 28 | 29 | When we expand macros, we remember the path that we expanded the macro 30 | definition that we used, and then at the end we double check that the 31 | path still leads to the same macro definition. If not, an error is 32 | reported. The goal of this check is to avoid "time travel" violations, 33 | where expanding a macro created a new item which shadowed a name found 34 | via a glob, causing the macro expansion path to point at the wrong 35 | place. 36 | 37 | **CAVEAT:** The following text is out of date. 38 | 39 | ### Compatibility with Rust 40 | 41 | The core algorithm is (I think) backwards compatible with Rust today 42 | (modulo bugs in today's code that accept things they should not). But 43 | there are a few places that the algorithm as implemented here diverge 44 | from Rust which are probably not strictly backwards compatible (and 45 | may not be desirable, depending on who you ask): 46 | 47 | 1. `use` statements define links that can be referenced from other 48 | modules. In other words, if a parent module has `use foo::Bar`, 49 | then it is legal for one of its children to do `use super::Bar`. 50 | This makes `use` and `pub use` statements completely analogous, 51 | except for the privacy of the resulting item. 52 | 2. Globs from an ancestor module import both public and private items. 53 | In other words, `use super::*` will bring in private items defined 54 | in your parent as well as public items. Note that this combines 55 | with point 1 to mean that `use super::*` effectively recreates your 56 | parent's naming environment completely. 57 | 58 | ### Caveats with the current state 59 | 60 | There are also some caveats (as of the time of this writing, anyway, 61 | this summary may get out of date): 62 | 63 | 1. I don't model multiple namespaces yet. I'll get to that. 64 | 2. Privacy is not yet implemented. I may get to that. 65 | 3. Hygiene and macro arguments in general are not modeled. I probably 66 | won't get to that. 67 | 68 | I don't foresee any horrible problems with any of these 69 | things. However, multiple namespaces have surprised me in the past in 70 | terms of their complexity, so I do intend to actually implement 71 | THOSE. The others seem simple enough, I may do it if the whim strikes 72 | me. 73 | 74 | ### How the algorithm works at a high level 75 | 76 | The basic idea is that a fixed point expansion followed by a 77 | verification step. We basically progressively process the code, 78 | resolving macros, use statements, and globs until we reach a stable 79 | state. We then check that the final result was self-consistent and 80 | free of duplicate names and so forth. 81 | 82 | #### Output of the algorithm 83 | 84 | The output of the algorithm is a either an error or, upon success, a 85 | new AST tree (actually, the implementation mutates the existing one in 86 | place) and a set of bindings `{Name -> ItemId}` for each module. Here 87 | `ItemId` is an unambiguous identifier for everything in the AST. So if 88 | I have a module like `mod foo { struct Bar; struct Baz; }`, then the 89 | bindings for this module would be something like `{Bar -> 22, Baz -> 90 | 23}`, assuming that the ids assigned to the two structs `Bar` and 91 | `Baz` were 22 and 23 respectively. Note that there is nothing which 92 | says that this set of bindings must be unambigious. For example, we 93 | might have two bindings for the same name, like `{Bar -> 44, Bar -> 94 | 66}`. 95 | 96 | *Side note:* The actual implementation gives identifies a bit more 97 | structure; they carry tags identifying the sort of item they refer to, 98 | so the id of `Bar` would be something like `ItemId::Struct(22)`. This 99 | is defined in [`ast.rs`][ast], which is also a kind of experimental 100 | playground for other ways to struct rustc's own internal AST. 101 | 102 | #### Intermediate state 103 | 104 | The computation itself is a fixed-point computation that incrementally 105 | builds up these binding sets. During this process, the binding sets 106 | include tuples with slightly more information: `(Name, Precedence, 107 | ItemId)`, where `Precedence` is either high (explicit declarations) or 108 | low (glob). It also carries along a set called "fully expanded". This 109 | is just a set of modules whose contents are fully expanded, meaning 110 | that they contain no macro references. It is important to know this 111 | because it affects when we can start processing globs (read on). 112 | 113 | #### Algorithm pseudo-code 114 | 115 | Here is the algorithm in pseudo-code. Note that this definition maps 116 | quite cleanly to the function `resolve_and_expand` in the 117 | [actual implementation][the code]. 118 | 119 | ``` 120 | loop { 121 | expand macros where possible; 122 | seed names for all modules; 123 | glob names for all modules; 124 | } until fixed point is reached; 125 | verify all paths; 126 | ``` 127 | 128 | To see how the algorithm works, let's consider this example: 129 | 130 | ```rust 131 | mod foo { 132 | use bar::*; 133 | use std::collections::HashMap; 134 | 135 | some_macro! { ... }; 136 | 137 | struct MyStruct { } 138 | } 139 | mod bar { 140 | macro_rules! some_macro { 141 | ... 142 | } 143 | } 144 | ``` 145 | 146 | Now let's go over each of the steps above. However, I'm going to go 147 | over them in a slightly 148 | 149 | **Expand macros where possible.** The first we do is to try and expand 150 | macros. For every macro reference, e.g. `some_macro! { .. }` in our 151 | example above, we try to resolve the path (here, `some_macro`, 152 | resolved relative to the current module). This may or may not 153 | succeed. If it fails, it could be for a variety of reasons: there 154 | might be no binding for `some_macro` (yet); there might be *multiple* 155 | bindings for `b` (which indicates an error); or, the path might not 156 | lead to a macro. Whatever the cause, if path resolution fails, we 157 | don't report any errors just now, we just ignore it. 158 | 159 | If however we *can* resolve the path, and it leads to a macro, then we 160 | can do macro expansion here. However, and this is important, when we 161 | expand the macro, we don't just replace the macro reference 162 | completely. Instead, we leave behind a "husk" that contains the path 163 | of the expanded macro. You can think of this as being similar to the 164 | "expansion trace" that rustc tracks: for each bit of code that 165 | resulted from macro expansion, we also remember the name of the macro 166 | that was used to create it. We'll need this path in a later 167 | step. (Note that in an actual implementation, you might choose to just 168 | remember a set of paths for expanded macros, rather than leaving a 169 | marker in the AST itself.) 170 | 171 | This path also updates the `fully_expanded` set. 172 | 173 | In [the code], this is the function `expand_macros`. 174 | 175 | **Seed names for all modules.** The seed step walk creates bindings 176 | for everything except globs. It works by traversing all the modules. 177 | For declarations, it will add a simple binding from the declared name 178 | to the declaration. So, in our example above, if the `struct 179 | MyStruct` declaration has id `X`, then we would add `(MyStruct, High, 180 | StructId(X))` to the binding set for the module `foo`. Similarly, in 181 | the module `bar`, we would also add `(some_macro, High, ModuleId(Y))`, 182 | where `Y` is the id of the macro rules declaration for `some_macro`. 183 | 184 | Explicit import statements deserve special mention. If we see a 185 | non-glob `use`, such as `std::collections::HashMap`, then we first try 186 | to resolve the path. If that succeeds, it will yield an item id `Z`, 187 | and we can add an entry like `(HashMap, High, Z)`. However, if path 188 | resolution *fails* for whatever reason, then we still add a 189 | binding. But because we don't know the target id, this is a special 190 | "placeholder" binding `(HashMap, High, 0)`, where `0` is a special 191 | placeholder id. This placeholder binding doesn't count as a normal 192 | binding and will be ignored for path resolution: it's purpose is to 193 | ensure that we don't add glob bindings for `HashMap` in the next step. 194 | 195 | In [the code], this is the function `seed_names`. 196 | 197 | **Process globs.** Finally, we process globs. In the example, we see 198 | the module `foo` that contains a glob `use bar::*`. To process this 199 | glob, we would first try to resolve the path `bar` to a single, 200 | unambigious item (ignoring errors, as usual). Presuming this succeeds, 201 | and resolves to a module with id `M`, we then go over the bindings `{N 202 | -> ID}` found within this module `M` as follows: 203 | 204 | - *Macro definitions.* If `ID` refers to a macro definition, then we 205 | will add it to the containing module (e.g., `foo`) as a 206 | high-priority binding. 207 | - *Non-macros.* For all other kinds of bindings, we add the binding 208 | to the importing module as a low-priority binding, but only if the 209 | following conditions are met: 210 | - The macro `M` that we are importing from has no unexpanded macros. 211 | - There is no high-priority binding for `N` within the importing module. 212 | 213 | In [the code], this is the function `glob_names`. 214 | 215 | **Verify all paths.** Ok, once we've fully completed expanding macros 216 | and propagating names, we proceed to the last step, which is verifying 217 | paths. This is the place where we actually report some 218 | errors. Basically in this step we iterate down all the items and check 219 | that all the paths we find can be resolved correctly. We also check 220 | that definitions are unambigious. For example, continuing with our 221 | example function above: 222 | 223 | - For the `use a::b::c::*` import, we would verify that `a::b::c` resolves 224 | to a module. 225 | - For `use std::collections::HashMap`, we would verify that this 226 | resolves to a single item (when multiple namespaces are supported, 227 | this would have to resolve to a single item in at least one namespace, 228 | and possibly zero items in the other). 229 | - For `struct MyStruct`, we would verify that `self::MyStruct` leads 230 | to this struct definition. This ensures that there is no other 231 | `struct MyStruct` (for example). 232 | - The macro reference `some_macro!` would presumably have been 233 | expanded in a prior step into some code. If it has not, that would 234 | be because the path `some_macro` cannot be resolved, and we would 235 | report an error. If it *HAS* been expanded, however, there will 236 | still be a "macro husk" lying around, as described in the previous 237 | step, containing the path `some_macro`. So we would resolve that 238 | husk and check that the path `some_macro` leads unambiguously to a 239 | single macro definition. (If this is the case, it must be the same 240 | definition that we used to expand.) 241 | 242 | In [the code], this is the function `verify_paths`. 243 | 244 | [the code]: src/resolve.rs 245 | [ast]: src/ast.rs 246 | 247 | ### FAQ 248 | 249 | #### Is this algorithm correct? 250 | 251 | Well, correct is naturally a bit hard to define. I believe the 252 | algorithm meets the expectations of Rust users and does logical 253 | things. But I think we can also say something a bit stronger: the 254 | result of the algorithm is *coherent*. Let me try to define what I 255 | mean by that (I intend to make this more formal, but for now it's 256 | rather hand-wavy). Let's consider an expanded form of the AST in which 257 | all links are made explicit: 258 | 259 | - Every path has a link describing the item it refers to 260 | - Globs are expanded to include a list of explicit items that they 261 | bring into scope 262 | - All macros are expanded, and code derived from expanding a macro 263 | includes some means to determine the macro it was expanded from (the 264 | "husk" in the algorithm above) 265 | 266 | Note that this expanded form roughly corresponds to what the "semantic 267 | analysis" step of a compiler normally does (or "resolve", as rustc 268 | calls it). In this expanded, explicit form, we can define a fairly 269 | simple notion of correctness. Basically, every path should in fact 270 | lead to the destination that is specified (without encountering 271 | ambigiuities along the way) and so forth. (Here I am assuming as 272 | "obviously correct" a kind of naive path resolution algorithm that 273 | walks to each module, inspects all the bindings that are within, and 274 | selects the only matching one.) 275 | 276 | I think of the algorithm as analogous to type inference: it infers all 277 | this extra information that is elided from the original AST and 278 | creates an explicit form. I believe that, if this inference succeeds, 279 | then the final resulting program is guaranteed to be "resolve 280 | correctly" (in the sense I described in the previous paragraph). You 281 | can see that the "verify" step comes close to checking most of the 282 | correctness requirements (and reporting an error if any of them fail). 283 | For example, it checks that every path resolves to *something* 284 | unambiguously. But it doesn't check the *full* set of properties, 285 | because it doesn't check that the result of this resolution is in fact 286 | the item we expected. This is because I believe that it is not 287 | necessary to check what the path resolves to: if it resolves to just 288 | one thing, it MUST be the thing we want. But it'd be nice to prove 289 | that (or at least argue it in a somewhat formal fashion). 290 | 291 | It would also be nice to make an argument that the "inference" step 292 | preserves the user's intent. For example, we'd like to show that if a 293 | solution is found, there is no other possible solution. Ideally, we'd 294 | also show that if an error results, then there is no possible solution 295 | to be found that respects the criteria. 296 | 297 | #### Why do explicit items shadow globs? 298 | 299 | Because it's better for forwards compatibility. It makes it less 300 | likely that adding a public item will accidentally break a downstream 301 | client. In other words, if someone does this: 302 | 303 | ```rust 304 | use crate_x::*; 305 | use crate_y::Foo; 306 | struct Bar(Foo); 307 | ``` 308 | 309 | And in the meantime `crate_x` adds a new entry `Foo`, the downstream 310 | create does not break. Still, it's not a guarantee. For example, 311 | someone might do: 312 | 313 | ```rust 314 | use crate_x::*; 315 | use crate_y::*; 316 | struct Bar(Foo); 317 | ``` 318 | 319 | and, in that case, they will get an error if `crate_x` later adds a 320 | `Foo` binding. (Note though that if they were not referencing `Foo`, 321 | there would be no problem.) 322 | 323 | #### Why don't explicit macros take precedence over globs? 324 | 325 | This is to avoid time-travel-like paradoxes that can otherwise occur 326 | when expanding macros. The problem case is when we have a macro found 327 | with a path like `a::b!`. Suppose that the containing module has a 328 | `use foo::*;` which was the origin of the module `a`. But then the 329 | macro *generates* a module `a` as well. Now we are in a quandry: to 330 | load the macro in the first place, we had to use the `a` from the 331 | glob, but that `a` was shadowed by code that the macro generated. This 332 | is bad because that shadowed item should have *taken precedence* over 333 | the `a` we got from the glob, but we can't go back and *undo* the 334 | macro expansion we already did (and anyway, if we did so, we wouldn't 335 | have the new definition for `a` anymore)! So we call it an error. 336 | 337 | Now, there are other ways to resolve this. One would be to take a 338 | "mutable view" on the set of bindings. That is, when a module expands, 339 | we only consider the names that were in scope *at the time of 340 | expansion*, which might later change. I wanted to avoid this because 341 | it introduces *ordering* between macro expansions -- that is, suppose 342 | I have to two macro expansions in a row, like `foo!(); bar!();`. Now 343 | if I expand `foo` first, it may generate names that would conflict 344 | with expanding `bar` -- but it would have been fine if I had just 345 | expanded `bar` first! I want to preserve the Rust-y notion that the 346 | order of declaration for items doesn't matter. 347 | 348 | Another way of looking at is in terms of the expanded form I described 349 | in the question about correctness: a mutable view means that the 350 | "correctness" property of this expanded form would be a lot more 351 | complex, because we would have to reason not about all the bindings we 352 | see, but the bindings that were visible at the time of expansion. 353 | 354 | #### Can we just remove macros from globs instead? 355 | 356 | It has been proposed that we could keep the full inference rules if we 357 | just removed macros from globs. But I don't think that works. Consider this 358 | example: 359 | 360 | ```rust 361 | mod a { 362 | use b::c::n 363 | n! { } 364 | } 365 | mod b { 366 | use c::*; 367 | macro_rules! m { 368 | () => { 369 | mod d { 370 | macro_rules! n { ... } 371 | } 372 | } 373 | } 374 | m! { } 375 | } 376 | mod c { 377 | mod d { 378 | macro_rules! n { 379 | ... 380 | } 381 | } 382 | } 383 | ``` 384 | 385 | The problem here has to do with the macro reference `n!` invoked by 386 | the module `a`. It is imported with the path `b::d::n`. We can resolve 387 | this via the glob import from `c` but, once we expand the macro `m` in 388 | module `b`, we see that in fact this module generates a 389 | higher-precedence module that should have taken precedence over the 390 | glob. So we are stuck in that we cannot freely expand macros in any 391 | order. (Note: this is the test case 392 | `banning_macro_globs_is_not_enough`; it results in a compilation error 393 | with the current system.) 394 | 395 | #### Does the user ever have to care about this algorithm? 396 | 397 | There are one scenario where the user is sort of exposed to the 398 | limitations of the algorithm, in a sense -- that is, where there is a 399 | fully-expanded form that has no conflicts or errors, but the algorithm 400 | fails to find it. If you have a glob that brings in a module, but that 401 | module is needed to resolve a macro reference somewhere else, than the 402 | expansion algorithm gets "stuck". Here is an example: 403 | 404 | ``` 405 | mod a { 406 | pub use b::*; // (2) ...requires expanding this glob 407 | c::m! { } // (1) resolving this path... 408 | } 409 | mod b { 410 | mod c { 411 | pub macro_rules! m { } 412 | } 413 | } 414 | ``` 415 | 416 | This is the test case `unexpandable_macro`. (The fear of course is 417 | that the macro `m` may define a module `c` that would invalidate the 418 | path we committed to.) 419 | 420 | I think it should be possible to write a kind of diagnostic that walks 421 | the code and informs the user "please add `pub use b::c` to allow name 422 | expansion to proceed. But I have to think about it a bit. It also 423 | seems like "well, if you can write that, can we incorporate it into 424 | the algorithm?" 425 | 426 | My rough intution for how this would work is that once a fixed point 427 | is reached, we would start expanding globs, but if a macro should 428 | shadow one of those globs, we'd report the error to the user as a kind 429 | of "causality violation". Have to think about how to handle that. 430 | 431 | 432 | --------------------------------------------------------------------------------