Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / call_graph.rs
19 kB 584 lines
1//! Graphs that represent the relationships between entities in a Gleam module, 2//! such as module functions or constants. 3 4#[cfg(test)] 5mod into_dependency_order_tests; 6 7use crate::{ 8 Result, 9 ast::{ 10 AssignName, AssignmentKind, BitArrayOption, BitArraySize, ClauseGuard, Constant, Pattern, 11 SrcSpan, Statement, UntypedClauseGuard, UntypedConstant, UntypedExpr, UntypedFunction, 12 UntypedModuleConstant, UntypedPattern, UntypedStatement, 13 }, 14 type_::Error, 15}; 16use itertools::Itertools; 17use petgraph::{Directed, stable_graph::NodeIndex, stable_graph::StableGraph}; 18 19#[derive(Debug, Default)] 20struct CallGraphBuilder<'a> { 21 names: im::HashMap<&'a str, Option<(NodeIndex, SrcSpan)>>, 22 graph: StableGraph<(), (), Directed>, 23 current_function: NodeIndex, 24} 25 26#[derive(Debug, Clone, PartialEq, Eq)] 27pub enum CallGraphNode { 28 Function(UntypedFunction), 29 30 ModuleConstant(UntypedModuleConstant), 31} 32 33impl<'a> CallGraphBuilder<'a> { 34 fn into_graph(self) -> StableGraph<(), (), Directed> { 35 self.graph 36 } 37 38 /// Add each function to the graph, storing the index of the node under the 39 /// name of the function. 40 fn register_module_function_existence( 41 &mut self, 42 function: &'a UntypedFunction, 43 ) -> Result<(), Error> { 44 let (_, name) = function 45 .name 46 .as_ref() 47 .expect("A module's function must be named"); 48 let location = function.location; 49 50 let index = self.graph.add_node(()); 51 let previous = self.names.insert(name, Some((index, location))); 52 53 if let Some(Some((_, previous_location))) = previous { 54 return Err(Error::DuplicateName { 55 location_a: location, 56 location_b: previous_location, 57 name: name.clone(), 58 }); 59 } 60 Ok(()) 61 } 62 63 /// Add each constant to the graph, storing the index of the node under the 64 /// name of the constant. 65 fn register_module_const_existence( 66 &mut self, 67 constant: &'a UntypedModuleConstant, 68 ) -> Result<(), Error> { 69 let name = &constant.name; 70 let location = constant.location; 71 72 let index = self.graph.add_node(()); 73 let previous = self.names.insert(name, Some((index, location))); 74 75 if let Some(Some((_, previous_location))) = previous { 76 return Err(Error::DuplicateName { 77 location_a: location, 78 location_b: previous_location, 79 name: name.clone(), 80 }); 81 } 82 Ok(()) 83 } 84 85 fn register_references_constant(&mut self, constant: &'a UntypedModuleConstant) { 86 self.current_function = self 87 .names 88 .get(constant.name.as_str()) 89 .expect("Constant must already have been registered as existing") 90 .expect("Constant must not be shadowed at module level") 91 .0; 92 self.constant(&constant.value); 93 } 94 95 fn register_references(&mut self, function: &'a UntypedFunction) { 96 let names = self.names.clone(); 97 98 self.current_function = self 99 .names 100 .get( 101 function 102 .name 103 .as_ref() 104 .map(|(_, name)| name.as_str()) 105 .expect("A module's function must be named"), 106 ) 107 .expect("Function must already have been registered as existing") 108 .expect("Function must not be shadowed at module level") 109 .0; 110 for name in function 111 .arguments 112 .iter() 113 .flat_map(|a| a.get_variable_name()) 114 { 115 self.define(name); 116 } 117 118 self.statements(&function.body); 119 120 self.names = names; 121 } 122 123 fn referenced(&mut self, name: &str) { 124 // If we don't know what the target is then it's either a programmer 125 // error to be detected later, or it's not a module function and as such 126 // is not a value we are tracking. 127 let Some(target) = self.names.get(name) else { 128 return; 129 }; 130 131 // If the target is known but registered as None then it's local value 132 // that shadows a module function. 133 let Some((target, _)) = target else { return }; 134 135 _ = self.graph.add_edge(self.current_function, *target, ()); 136 } 137 138 fn statements(&mut self, statements: &'a [UntypedStatement]) { 139 let names = self.names.clone(); 140 for statement in statements { 141 self.statement(statement); 142 } 143 self.names = names; 144 } 145 146 fn statement(&mut self, statement: &'a UntypedStatement) { 147 match statement { 148 Statement::Expression(expression) => { 149 self.expression(expression); 150 } 151 Statement::Assignment(assignment) => { 152 self.expression(&assignment.value); 153 self.pattern(&assignment.pattern); 154 match &assignment.kind { 155 AssignmentKind::Assert { 156 message: Some(message), 157 .. 158 } => self.expression(message), 159 AssignmentKind::Let 160 | AssignmentKind::Generated 161 | AssignmentKind::Assert { message: None, .. } => {} 162 } 163 } 164 Statement::Use(use_) => { 165 self.expression(&use_.call); 166 for assignment in &use_.assignments { 167 self.pattern(&assignment.pattern); 168 } 169 } 170 Statement::Assert(assert) => { 171 self.expression(&assert.value); 172 if let Some(message) = &assert.message { 173 self.expression(message) 174 } 175 } 176 }; 177 } 178 179 fn expression(&mut self, expression: &'a UntypedExpr) { 180 match expression { 181 UntypedExpr::Int { .. } | UntypedExpr::Float { .. } | UntypedExpr::String { .. } => (), 182 183 UntypedExpr::Todo { message, .. } => { 184 if let Some(msg_expr) = message { 185 self.expression(msg_expr) 186 } 187 } 188 189 UntypedExpr::Panic { message, .. } => { 190 if let Some(msg_expr) = message { 191 self.expression(msg_expr) 192 } 193 } 194 195 UntypedExpr::Echo { 196 expression, 197 location: _, 198 keyword_end: _, 199 message, 200 } => { 201 if let Some(expression) = expression { 202 self.expression(expression); 203 } 204 if let Some(message) = message { 205 self.expression(message); 206 } 207 } 208 209 // Aha! A variable is being referenced. 210 UntypedExpr::Var { name, .. } => { 211 self.referenced(name); 212 } 213 214 UntypedExpr::Call { fun, arguments, .. } => { 215 self.expression(fun); 216 for argument in arguments { 217 self.expression(&argument.value); 218 } 219 } 220 221 UntypedExpr::PipeLine { expressions } => { 222 for expression in expressions { 223 self.expression(expression); 224 } 225 } 226 227 UntypedExpr::Tuple { elements, .. } => { 228 for expression in elements { 229 self.expression(expression); 230 } 231 } 232 233 UntypedExpr::Block { statements, .. } => { 234 let names = self.names.clone(); 235 self.statements(statements); 236 self.names = names; 237 } 238 239 UntypedExpr::BinOp { left, right, .. } => { 240 self.expression(left); 241 self.expression(right); 242 } 243 244 UntypedExpr::List { elements, tail, .. } => { 245 for element in elements { 246 self.expression(element); 247 } 248 if let Some(tail) = tail { 249 self.expression(tail); 250 } 251 } 252 253 UntypedExpr::NegateInt { 254 value: expression, .. 255 } 256 | UntypedExpr::NegateBool { 257 value: expression, .. 258 } 259 | UntypedExpr::TupleIndex { 260 tuple: expression, .. 261 } 262 | UntypedExpr::FieldAccess { 263 container: expression, 264 .. 265 } => { 266 self.expression(expression); 267 } 268 269 UntypedExpr::BitArray { segments, .. } => { 270 for segment in segments { 271 self.expression(&segment.value); 272 for option in &segment.options { 273 if let BitArrayOption::Size { value, .. } = option { 274 self.expression(value); 275 } 276 } 277 } 278 } 279 280 UntypedExpr::RecordUpdate { 281 record, arguments, .. 282 } => { 283 self.expression(&record.base); 284 for argument in arguments { 285 self.expression(&argument.value); 286 } 287 } 288 289 UntypedExpr::Fn { 290 arguments, body, .. 291 } => { 292 let names = self.names.clone(); 293 for argument in arguments { 294 if let Some(name) = argument.names.get_variable_name() { 295 self.define(name) 296 } 297 } 298 self.statements(body); 299 self.names = names; 300 } 301 302 UntypedExpr::Case { 303 subjects, clauses, .. 304 } => { 305 for subject in subjects { 306 self.expression(subject); 307 } 308 for clause in clauses.as_deref().unwrap_or_default() { 309 let names = self.names.clone(); 310 for pattern in &clause.pattern { 311 self.pattern(pattern); 312 } 313 if let Some(guard) = &clause.guard { 314 self.guard(guard); 315 } 316 self.expression(&clause.then); 317 self.names = names; 318 } 319 } 320 } 321 } 322 323 fn pattern(&mut self, pattern: &'a UntypedPattern) { 324 match pattern { 325 Pattern::Discard { .. } 326 | Pattern::Int { .. } 327 | Pattern::Float { .. } 328 | Pattern::String { .. } 329 | Pattern::StringPrefix { 330 right_side_assignment: AssignName::Discard(_), 331 .. 332 } 333 | Pattern::Invalid { .. } => (), 334 335 Pattern::StringPrefix { 336 right_side_assignment: AssignName::Variable(name), 337 .. 338 } 339 | Pattern::Variable { name, .. } => { 340 self.define(name); 341 } 342 343 Pattern::Tuple { 344 elements: patterns, .. 345 } => { 346 for pattern in patterns { 347 self.pattern(pattern); 348 } 349 } 350 351 Pattern::List { elements, tail, .. } => { 352 for element in elements { 353 self.pattern(element); 354 } 355 if let Some(tail) = tail { 356 self.pattern(&tail.pattern); 357 } 358 } 359 360 Pattern::BitArraySize(size) => { 361 self.bit_array_size(size); 362 } 363 364 Pattern::Assign { name, pattern, .. } => { 365 self.define(name); 366 self.pattern(pattern); 367 } 368 369 Pattern::Constructor { arguments, .. } => { 370 for argument in arguments { 371 self.pattern(&argument.value); 372 } 373 } 374 375 Pattern::BitArray { segments, .. } => { 376 for segment in segments { 377 for option in &segment.options { 378 self.bit_array_option(option, |s, p| s.pattern(p)); 379 } 380 self.pattern(&segment.value); 381 } 382 } 383 } 384 } 385 386 fn bit_array_size(&mut self, size: &'a BitArraySize<()>) { 387 match size { 388 BitArraySize::Int { .. } => {} 389 BitArraySize::Variable { name, .. } => self.referenced(name), 390 BitArraySize::BinaryOperator { left, right, .. } => { 391 self.bit_array_size(left); 392 self.bit_array_size(right); 393 } 394 BitArraySize::Block { inner, .. } => self.bit_array_size(inner), 395 } 396 } 397 398 fn define(&mut self, name: &'a str) { 399 _ = self.names.insert(name, None); 400 } 401 402 fn bit_array_option<T>( 403 &mut self, 404 option: &'a BitArrayOption<T>, 405 process: impl Fn(&mut Self, &'a T), 406 ) { 407 match option { 408 BitArrayOption::Big { .. } 409 | BitArrayOption::Bytes { .. } 410 | BitArrayOption::Bits { .. } 411 | BitArrayOption::Float { .. } 412 | BitArrayOption::Int { .. } 413 | BitArrayOption::Little { .. } 414 | BitArrayOption::Native { .. } 415 | BitArrayOption::Signed { .. } 416 | BitArrayOption::Unit { .. } 417 | BitArrayOption::Unsigned { .. } 418 | BitArrayOption::Utf16 { .. } 419 | BitArrayOption::Utf16Codepoint { .. } 420 | BitArrayOption::Utf32 { .. } 421 | BitArrayOption::Utf32Codepoint { .. } 422 | BitArrayOption::Utf8 { .. } 423 | BitArrayOption::Utf8Codepoint { .. } => (), 424 425 BitArrayOption::Size { value: pattern, .. } => { 426 process(self, pattern); 427 } 428 } 429 } 430 431 fn guard(&mut self, guard: &'a UntypedClauseGuard) { 432 match guard { 433 ClauseGuard::Invalid { .. } => (), 434 435 ClauseGuard::BinaryOperator { left, right, .. } => { 436 self.guard(left); 437 self.guard(right); 438 } 439 440 ClauseGuard::Block { value, .. } => self.guard(value), 441 442 ClauseGuard::Not { expression, .. } => self.guard(expression), 443 444 ClauseGuard::Var { name, .. } => self.referenced(name), 445 446 ClauseGuard::TupleIndex { tuple, .. } => self.guard(tuple), 447 448 ClauseGuard::FieldAccess { container, .. } => self.guard(container), 449 450 ClauseGuard::ModuleSelect { module_name, .. } => self.referenced(module_name), 451 452 ClauseGuard::Constant(constant) => self.constant(constant), 453 } 454 } 455 456 fn constant(&mut self, constant: &'a UntypedConstant) { 457 match constant { 458 Constant::Int { .. } 459 | Constant::Float { .. } 460 | Constant::String { .. } 461 | Constant::Invalid { .. } 462 | Constant::Var { 463 module: Some(_), .. 464 } => (), 465 466 Constant::Todo { message, .. } => { 467 if let Some(message) = message { 468 self.constant(message) 469 } 470 } 471 472 Constant::List { elements, tail, .. } => { 473 for element in elements { 474 self.constant(element); 475 } 476 if let Some(tail) = tail { 477 self.constant(tail); 478 } 479 } 480 481 Constant::Tuple { elements, .. } => { 482 for element in elements { 483 self.constant(element); 484 } 485 } 486 487 Constant::Record { arguments, .. } => { 488 for argument in arguments.iter().flatten() { 489 self.constant(&argument.value); 490 } 491 } 492 493 Constant::RecordUpdate { 494 record, arguments, .. 495 } => { 496 self.constant(&record.base); 497 498 for argument in arguments { 499 self.constant(&argument.value); 500 } 501 } 502 503 Constant::Var { 504 module: None, name, .. 505 } => self.referenced(name), 506 507 Constant::BitArray { segments, .. } => { 508 for segment in segments { 509 for option in &segment.options { 510 self.bit_array_option(option, |s, c| s.constant(c)); 511 } 512 self.constant(&segment.value); 513 } 514 } 515 516 Constant::StringConcatenation { left, right, .. } => { 517 self.constant(left); 518 self.constant(right); 519 } 520 } 521 } 522} 523 524/// Determine the order in which functions and constants should be compiled and if any 525/// mutually recursive functions need to be compiled together. 526/// 527pub fn into_dependency_order( 528 functions: Vec<UntypedFunction>, 529 constants: Vec<UntypedModuleConstant>, 530) -> Result<Vec<Vec<CallGraphNode>>, Error> { 531 let mut grapher = CallGraphBuilder::default(); 532 533 for function in &functions { 534 grapher.register_module_function_existence(function)?; 535 } 536 537 for constant in &constants { 538 grapher.register_module_const_existence(constant)?; 539 } 540 541 // Build the call graph between the module functions. 542 for function in &functions { 543 grapher.register_references(function); 544 } 545 546 for constant in &constants { 547 grapher.register_references_constant(constant); 548 } 549 550 // Consume the grapher to get the graph 551 let graph = grapher.into_graph(); 552 553 // Determine the order in which the functions should be compiled by looking 554 // at which other functions they depend on. 555 let indices = petgraph::algo::tarjan_scc(&graph); 556 557 // We got node indices back, so we need to map them back to the functions 558 // they represent. 559 // We wrap them each with `Some` so we can use `.take()`. 560 let mut definitions = functions 561 .into_iter() 562 .map(CallGraphNode::Function) 563 .chain(constants.into_iter().map(CallGraphNode::ModuleConstant)) 564 .map(Some) 565 .collect_vec(); 566 567 let ordered = indices 568 .into_iter() 569 .map(|level| { 570 level 571 .into_iter() 572 .map(|index| { 573 definitions 574 .get_mut(index.index()) 575 .expect("Index out of bounds") 576 .take() 577 .expect("Function already taken") 578 }) 579 .collect_vec() 580 }) 581 .collect_vec(); 582 583 Ok(ordered) 584}