@@ -6,17 +6,16 @@ use crate::typing::user::{
66 lookup_builtin_type, TypeId , UserType , ERROR_TYPE , STRING_TYPE , UNIT_TYPE ,
77} ;
88use crate :: typing:: { Parameter , TypeChecker , TypeErrorKind } ;
9- use crate :: { Reef , SourceLocation , TypeError } ;
9+ use crate :: { Reef , SourceLocation , TypeError , UnitKey } ;
1010use ast:: function:: { FunctionDeclaration , FunctionParameter } ;
1111use ast:: r#struct:: { StructDeclaration , StructImpl } ;
1212use ast:: r#type:: Type ;
13- use ast:: r#use:: { Import , ImportedSymbol , InclusionPathItem } ;
13+ use ast:: r#use:: { Import , ImportList , ImportedSymbol , InclusionPathItem } ;
1414use ast:: variable:: TypedVariable ;
1515use ast:: Expr ;
1616use context:: source:: { SourceSegmentHolder , Span } ;
17- use parser:: Root ;
18- use std:: collections:: HashMap ;
19- use std:: ffi:: OsString ;
17+ use std:: collections:: { HashMap , HashSet } ;
18+ use std:: ffi:: { OsStr , OsString } ;
2019use std:: path:: { Path , PathBuf } ;
2120
2221/// Places functions and types at the top level of the symbol table.
@@ -34,15 +33,26 @@ use std::path::{Path, PathBuf};
3433/// This step performs multiple passes where types are added to the symbol table, and then their
3534/// fields are added to the type. A third pass is done to hoist functions, now that all parameters
3635/// and return types may be known.
36+ ///
37+ /// # Exploration order
38+ ///
39+ /// The hoisting phase creates an identity for each module and outlines the dependencies between
40+ /// them. It does then compute a satisfying order to analyse each module, one at a time, that should
41+ /// not have cyclic dependencies for variables.
3742pub ( super ) fn hoist_files (
3843 foreign : & HashMap < OsString , ModuleTree > ,
3944 reef : & mut Reef ,
4045 checker : & mut TypeChecker ,
46+ mut keys : Vec < UnitKey > ,
4147) -> HoistingResult {
4248 let mut errors = Vec :: < TypeError > :: new ( ) ;
4349 let mut graph = HashMap :: new ( ) ;
44- for ( path, root) in & reef. files {
45- let mut table = SymbolTable :: new ( path. clone ( ) ) ;
50+ for key @ UnitKey { path, .. } in & keys {
51+ let root = reef. files . get ( key) . expect ( "unit should exist" ) ;
52+ let mut table = reef
53+ . symbols
54+ . remove ( path)
55+ . unwrap_or_else ( || SymbolTable :: new ( path. clone ( ) ) ) ;
4656 let mut exports = reef. exports . take_exports ( path) ; // This is not problematic if the export is not found, but it shouldn't happen
4757 let modules = ModuleView :: new ( & reef. exports , foreign) ;
4858 let mut deps = Dependencies {
@@ -63,17 +73,24 @@ pub(super) fn hoist_files(
6373 reef. symbols . insert ( path. clone ( ) , table) ;
6474 }
6575
66- let mut sorted: Vec < PathBuf > = Vec :: new ( ) ;
76+ let mut sorted: Vec < UnitKey > = Vec :: new ( ) ;
6777 let mut frontier: Vec < PathBuf > = graph
6878 . keys ( )
6979 . filter ( |module| graph. values ( ) . all ( |deps| !deps. contains ( module) ) )
7080 . cloned ( )
7181 . collect ( ) ;
82+ let mut seen: HashSet < PathBuf > = frontier. iter ( ) . cloned ( ) . collect ( ) ; // Deduplicate when the same file is imported multiple times
7283 while let Some ( module) = frontier. pop ( ) {
73- sorted. push ( module. clone ( ) ) ;
84+ let key = keys. swap_remove (
85+ keys. iter ( )
86+ . position ( |key| key. path == module)
87+ . expect ( "module should exist" ) ,
88+ ) ;
89+ sorted. push ( key) ;
7490 if let Some ( requires) = graph. remove ( & module) {
7591 for require in requires {
76- if !graph. values ( ) . any ( |deps| deps. contains ( & require) ) {
92+ if !seen. contains ( & require) && !graph. values ( ) . any ( |deps| deps. contains ( & require) ) {
93+ seen. insert ( require. clone ( ) ) ;
7794 frontier. push ( require) ;
7895 }
7996 }
@@ -99,7 +116,7 @@ pub(super) fn hoist_files(
99116
100117pub ( crate ) struct HoistingResult {
101118 pub ( crate ) errors : Vec < TypeError > ,
102- pub ( crate ) sorted : Vec < PathBuf > ,
119+ pub ( crate ) sorted : Vec < UnitKey > ,
103120}
104121
105122struct Dependencies < ' a > {
@@ -108,12 +125,12 @@ struct Dependencies<'a> {
108125}
109126
110127fn hoist_type_names (
111- root : & Root ,
128+ root : & [ Expr ] ,
112129 checker : & mut TypeChecker ,
113130 table : & mut SymbolTable ,
114131 exports : & mut [ Export ] ,
115132) {
116- for expr in & root. expressions {
133+ for expr in root. iter ( ) {
117134 if let Expr :: StructDeclaration ( StructDeclaration {
118135 name,
119136 segment : span,
@@ -139,14 +156,14 @@ fn hoist_type_names(
139156}
140157
141158fn hoist_functions (
142- root : & Root ,
159+ root : & [ Expr ] ,
143160 checker : & mut TypeChecker ,
144161 table : & mut SymbolTable ,
145162 exports : & mut [ Export ] ,
146163 deps : & mut Dependencies ,
147164 errors : & mut Vec < TypeError > ,
148165) {
149- for expr in & root. expressions {
166+ for expr in root. iter ( ) {
150167 match expr {
151168 Expr :: FunctionDeclaration ( fn_decl) => {
152169 hoist_fn_decl ( fn_decl, None , checker, table, exports, errors) ;
@@ -164,6 +181,9 @@ fn hoist_functions(
164181 alias,
165182 segment : span,
166183 } ) => {
184+ if matches ! ( path. first( ) , Some ( InclusionPathItem :: Symbol ( _) ) ) {
185+ return ; // Exclude inter-reefs dependencies
186+ }
167187 let ( last, rest) = path. split_last ( ) . expect ( "at least one item" ) ;
168188 if let Some ( module) = deps. modules . get_direct ( rest) {
169189 for export in & module. exports {
@@ -192,10 +212,55 @@ fn hoist_functions(
192212 }
193213 }
194214 }
195- Import :: AllIn ( path, _) => todo ! ( ) ,
215+ Import :: AllIn ( path, _) => {
216+ if matches ! ( path. first( ) , Some ( InclusionPathItem :: Symbol ( _) ) ) {
217+ return ; // Exclude inter-reefs dependencies
218+ }
219+ if let Some ( module) = deps. modules . get_direct ( path) {
220+ for export in & module. exports {
221+ table. insert_remote ( export. name . clone ( ) , import. segment ( ) , export) ;
222+ }
223+ }
224+ }
196225 Import :: Environment ( _) => { }
197- Import :: List ( list) => {
198- todo ! ( )
226+ Import :: List ( ImportList {
227+ root,
228+ imports,
229+ segment : span,
230+ } ) => {
231+ if matches ! ( root. first( ) , Some ( InclusionPathItem :: Symbol ( _) ) ) {
232+ return ; // Exclude inter-reefs dependencies
233+ }
234+ let base = root
235+ . iter ( )
236+ . skip_while ( |item| matches ! ( item, InclusionPathItem :: Reef ( _) ) )
237+ . map ( |item| item. name ( ) )
238+ . collect :: < PathBuf > ( ) ;
239+ if let Some ( mut module) = deps. modules . get_direct ( root) {
240+ for import in imports {
241+ match import {
242+ Import :: Symbol ( symbol) => {
243+ let mut path = base. clone ( ) ;
244+ for part in symbol. path . iter ( ) {
245+ if let InclusionPathItem :: Symbol ( ident) = part {
246+ if let Some ( tree) =
247+ module. get ( OsStr :: new ( ident. value . as_str ( ) ) )
248+ {
249+ path. push ( ident. value . as_str ( ) ) ;
250+ module = tree;
251+ } else {
252+ deps. requires . push ( path) ;
253+ break ;
254+ }
255+ }
256+ }
257+ }
258+ Import :: AllIn ( _, _) => { }
259+ Import :: Environment ( _) => { }
260+ Import :: List ( _) => { }
261+ }
262+ }
263+ }
199264 }
200265 }
201266 }
@@ -214,6 +279,7 @@ fn hoist_fn_decl(
214279 name,
215280 type_parameters,
216281 parameters,
282+ body,
217283 return_type,
218284 ..
219285 } : & FunctionDeclaration ,
@@ -271,15 +337,37 @@ fn hoist_fn_decl(
271337 }
272338 None => UNIT_TYPE ,
273339 } ;
274- let mut fqn = table. path . clone ( ) ;
275- fqn. push ( name. value . to_string ( ) ) ;
340+ table. exit_scope ( ) ;
341+ let fqn = if let Some ( current_ty) = current_ty {
342+ let UserType :: Parametrized { schema, .. } = checker. types [ current_ty] else {
343+ panic ! (
344+ "the current type should be a struct, got {:?}" ,
345+ checker. types[ current_ty]
346+ ) ;
347+ } ;
348+ let Schema {
349+ name : ref type_name,
350+ ..
351+ } = checker. registry [ schema] ;
352+ let mut fqn = PathBuf :: from ( type_name) ;
353+ fqn. push ( name. value . to_string ( ) ) ;
354+ fqn
355+ } else {
356+ let mut fqn = table. path . clone ( ) ;
357+ fqn. push ( name. value . to_string ( ) ) ;
358+ fqn
359+ } ;
276360 let function = checker. registry . define_function ( Function {
277361 declared_at : table. path . clone ( ) ,
278362 fqn,
279363 generic_variables,
280364 param_types,
281365 return_type,
282- kind : FunctionKind :: Function ,
366+ kind : if body. is_some ( ) {
367+ FunctionKind :: Function
368+ } else {
369+ FunctionKind :: Intrinsic
370+ } ,
283371 } ) ;
284372 let function_type = checker. types . alloc ( UserType :: Function ( function) ) ;
285373 match current_ty {
@@ -548,12 +636,15 @@ mod tests {
548636
549637 fn hoist_files ( fs : MemoryFilesystem , entrypoint : & str ) -> Vec < TypeError > {
550638 let mut reef = Reef :: new ( OsString :: from ( "test" ) ) ;
551- assert_eq ! (
552- import_multi( & mut reef, & fs, entrypoint) ,
553- [ ] ,
554- "no import errors should be found"
555- ) ;
556- super :: hoist_files ( & mut HashMap :: new ( ) , & mut reef, & mut TypeChecker :: default ( ) ) . errors
639+ let import_result = import_multi ( & mut reef, & fs, entrypoint) ;
640+ assert_eq ! ( import_result. errors, [ ] , "no import errors should be found" ) ;
641+ super :: hoist_files (
642+ & mut HashMap :: new ( ) ,
643+ & mut reef,
644+ & mut TypeChecker :: default ( ) ,
645+ import_result. keys ,
646+ )
647+ . errors
557648 }
558649
559650 fn hoist ( source : & str ) -> Vec < TypeError > {
0 commit comments