| author | bors <bors@rust-lang.org> 2024-09-17 03:40:29 UTC |
| committer | bors <bors@rust-lang.org> 2024-09-17 03:40:29 UTC |
| log | c8dff289a0c931e138350f3baef0fab6d3309f80 |
| tree | e1dc41383b1d8f2168cd94185804e66dc8b38b94 |
| parent | e2dc1a1c0f97a90319181a721ab317210307617a |
| parent | 558b302af739045bb2685707947169ad853645ea |
Rollup of 4 pull requests
Successful merges:
- #130380 (coverage: Clarify some parts of coverage counter creation)
- #130427 (run_make_support: rectify symlink handling)
- #130447 (rustc_llvm: update for llvm/llvm-project@2ae968a0d9fb61606b020e898d88…)
- #130448 (fix: Remove duplicate `LazyLock` example.)
r? `@ghost`
`@rustbot` modify labels: rollup10 files changed, 334 insertions(+), 235 deletions(-)
compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h+6-1| ... | ... | @@ -25,7 +25,6 @@ |
| 25 | 25 | #include "llvm/Target/TargetMachine.h" |
| 26 | 26 | #include "llvm/Target/TargetOptions.h" |
| 27 | 27 | #include "llvm/Transforms/IPO.h" |
| 28 | #include "llvm/Transforms/Instrumentation.h" | |
| 29 | 28 | #include "llvm/Transforms/Scalar.h" |
| 30 | 29 | |
| 31 | 30 | #define LLVM_VERSION_GE(major, minor) \ |
| ... | ... | @@ -34,6 +33,12 @@ |
| 34 | 33 | |
| 35 | 34 | #define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor))) |
| 36 | 35 | |
| 36 | #if LLVM_VERSION_GE(20, 0) | |
| 37 | #include "llvm/Transforms/Utils/Instrumentation.h" | |
| 38 | #else | |
| 39 | #include "llvm/Transforms/Instrumentation.h" | |
| 40 | #endif | |
| 41 | ||
| 37 | 42 | #include "llvm/IR/LegacyPassManager.h" |
| 38 | 43 | |
| 39 | 44 | #include "llvm/Bitcode/BitcodeReader.h" |
compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp-1| ... | ... | @@ -39,7 +39,6 @@ |
| 39 | 39 | #include "llvm/TargetParser/Host.h" |
| 40 | 40 | #endif |
| 41 | 41 | #include "llvm/Support/TimeProfiler.h" |
| 42 | #include "llvm/Transforms/Instrumentation.h" | |
| 43 | 42 | #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" |
| 44 | 43 | #include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h" |
| 45 | 44 | #if LLVM_VERSION_GE(19, 0) |
compiler/rustc_mir_transform/src/coverage/counters.rs+109-122| ... | ... | @@ -95,11 +95,33 @@ impl CoverageCounters { |
| 95 | 95 | this |
| 96 | 96 | } |
| 97 | 97 | |
| 98 | fn make_counter(&mut self, site: CounterIncrementSite) -> BcbCounter { | |
| 98 | /// Shared helper used by [`Self::make_phys_node_counter`] and | |
| 99 | /// [`Self::make_phys_edge_counter`]. Don't call this directly. | |
| 100 | fn make_counter_inner(&mut self, site: CounterIncrementSite) -> BcbCounter { | |
| 99 | 101 | let id = self.counter_increment_sites.push(site); |
| 100 | 102 | BcbCounter::Counter { id } |
| 101 | 103 | } |
| 102 | 104 | |
| 105 | /// Creates a new physical counter attached a BCB node. | |
| 106 | /// The node must not already have a counter. | |
| 107 | fn make_phys_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { | |
| 108 | let counter = self.make_counter_inner(CounterIncrementSite::Node { bcb }); | |
| 109 | debug!(?bcb, ?counter, "node gets a physical counter"); | |
| 110 | self.set_bcb_counter(bcb, counter) | |
| 111 | } | |
| 112 | ||
| 113 | /// Creates a new physical counter attached to a BCB edge. | |
| 114 | /// The edge must not already have a counter. | |
| 115 | fn make_phys_edge_counter( | |
| 116 | &mut self, | |
| 117 | from_bcb: BasicCoverageBlock, | |
| 118 | to_bcb: BasicCoverageBlock, | |
| 119 | ) -> BcbCounter { | |
| 120 | let counter = self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb }); | |
| 121 | debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter"); | |
| 122 | self.set_bcb_edge_counter(from_bcb, to_bcb, counter) | |
| 123 | } | |
| 124 | ||
| 103 | 125 | fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter { |
| 104 | 126 | let new_expr = BcbExpression { lhs, op, rhs }; |
| 105 | 127 | *self |
| ... | ... | @@ -294,25 +316,27 @@ impl<'a> MakeBcbCounters<'a> { |
| 294 | 316 | |
| 295 | 317 | let successors = self.basic_coverage_blocks.successors[from_bcb].as_slice(); |
| 296 | 318 | |
| 297 | // If this node doesn't have multiple out-edges, or all of its out-edges | |
| 298 | // already have counters, then we don't need to create edge counters. | |
| 299 | let needs_out_edge_counters = successors.len() > 1 | |
| 300 | && successors.iter().any(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb)); | |
| 301 | if !needs_out_edge_counters { | |
| 319 | // If this node's out-edges won't sum to the node's counter, | |
| 320 | // then there's no reason to create edge counters here. | |
| 321 | if !self.basic_coverage_blocks[from_bcb].is_out_summable { | |
| 302 | 322 | return; |
| 303 | 323 | } |
| 304 | 324 | |
| 305 | if tracing::enabled!(tracing::Level::DEBUG) { | |
| 306 | let _span = | |
| 307 | debug_span!("node has some out-edges without counters", ?from_bcb).entered(); | |
| 308 | for &to_bcb in successors { | |
| 309 | debug!(?to_bcb, counter=?self.edge_counter(from_bcb, to_bcb)); | |
| 310 | } | |
| 311 | } | |
| 325 | // Determine the set of out-edges that don't yet have edge counters. | |
| 326 | let candidate_successors = self.basic_coverage_blocks.successors[from_bcb] | |
| 327 | .iter() | |
| 328 | .copied() | |
| 329 | .filter(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb)) | |
| 330 | .collect::<Vec<_>>(); | |
| 331 | debug!(?candidate_successors); | |
| 312 | 332 | |
| 313 | // Of the out-edges that don't have counters yet, one can be given an expression | |
| 314 | // (computed from the other out-edges) instead of a dedicated counter. | |
| 315 | let expression_to_bcb = self.choose_out_edge_for_expression(traversal, from_bcb); | |
| 333 | // If there are out-edges without counters, choose one to be given an expression | |
| 334 | // (computed from this node and the other out-edges) instead of a physical counter. | |
| 335 | let Some(expression_to_bcb) = | |
| 336 | self.choose_out_edge_for_expression(traversal, &candidate_successors) | |
| 337 | else { | |
| 338 | return; | |
| 339 | }; | |
| 316 | 340 | |
| 317 | 341 | // For each out-edge other than the one that was chosen to get an expression, |
| 318 | 342 | // ensure that it has a counter (existing counter/expression or a new counter), |
| ... | ... | @@ -324,10 +348,11 @@ impl<'a> MakeBcbCounters<'a> { |
| 324 | 348 | .filter(|&to_bcb| to_bcb != expression_to_bcb) |
| 325 | 349 | .map(|to_bcb| self.get_or_make_edge_counter(from_bcb, to_bcb)) |
| 326 | 350 | .collect::<Vec<_>>(); |
| 327 | let sum_of_all_other_out_edges: BcbCounter = self | |
| 328 | .coverage_counters | |
| 329 | .make_sum(&other_out_edge_counters) | |
| 330 | .expect("there must be at least one other out-edge"); | |
| 351 | let Some(sum_of_all_other_out_edges) = | |
| 352 | self.coverage_counters.make_sum(&other_out_edge_counters) | |
| 353 | else { | |
| 354 | return; | |
| 355 | }; | |
| 331 | 356 | |
| 332 | 357 | // Now create an expression for the chosen edge, by taking the counter |
| 333 | 358 | // for its source node and subtracting the sum of its sibling out-edges. |
| ... | ... | @@ -338,10 +363,13 @@ impl<'a> MakeBcbCounters<'a> { |
| 338 | 363 | ); |
| 339 | 364 | |
| 340 | 365 | debug!("{expression_to_bcb:?} gets an expression: {expression:?}"); |
| 341 | if self.basic_coverage_blocks.bcb_has_multiple_in_edges(expression_to_bcb) { | |
| 342 | self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression); | |
| 343 | } else { | |
| 366 | if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(expression_to_bcb) { | |
| 367 | // This edge normally wouldn't get its own counter, so attach the expression | |
| 368 | // to its target node instead, so that `edge_has_no_counter` can see it. | |
| 369 | assert_eq!(sole_pred, from_bcb); | |
| 344 | 370 | self.coverage_counters.set_bcb_counter(expression_to_bcb, expression); |
| 371 | } else { | |
| 372 | self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression); | |
| 345 | 373 | } |
| 346 | 374 | } |
| 347 | 375 | |
| ... | ... | @@ -353,28 +381,21 @@ impl<'a> MakeBcbCounters<'a> { |
| 353 | 381 | return counter_kind; |
| 354 | 382 | } |
| 355 | 383 | |
| 356 | // A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`). | |
| 357 | // Also, a BCB that loops back to itself gets a simple `Counter`. This may indicate the | |
| 358 | // program results in a tight infinite loop, but it should still compile. | |
| 359 | let one_path_to_target = !self.basic_coverage_blocks.bcb_has_multiple_in_edges(bcb); | |
| 360 | if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) { | |
| 361 | let counter_kind = | |
| 362 | self.coverage_counters.make_counter(CounterIncrementSite::Node { bcb }); | |
| 363 | if one_path_to_target { | |
| 364 | debug!("{bcb:?} gets a new counter: {counter_kind:?}"); | |
| 365 | } else { | |
| 366 | debug!( | |
| 367 | "{bcb:?} has itself as its own predecessor. It can't be part of its own \ | |
| 368 | Expression sum, so it will get its own new counter: {counter_kind:?}. \ | |
| 369 | (Note, the compiled code will generate an infinite loop.)", | |
| 370 | ); | |
| 371 | } | |
| 372 | return self.coverage_counters.set_bcb_counter(bcb, counter_kind); | |
| 384 | let predecessors = self.basic_coverage_blocks.predecessors[bcb].as_slice(); | |
| 385 | ||
| 386 | // Handle cases where we can't compute a node's count from its in-edges: | |
| 387 | // - START_BCB has no in-edges, so taking the sum would panic (or be wrong). | |
| 388 | // - For nodes with one in-edge, or that directly loop to themselves, | |
| 389 | // trying to get the in-edge counts would require this node's counter, | |
| 390 | // leading to infinite recursion. | |
| 391 | if predecessors.len() <= 1 || predecessors.contains(&bcb) { | |
| 392 | debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor"); | |
| 393 | return self.coverage_counters.make_phys_node_counter(bcb); | |
| 373 | 394 | } |
| 374 | 395 | |
| 375 | 396 | // A BCB with multiple incoming edges can compute its count by ensuring that counters |
| 376 | 397 | // exist for each of those edges, and then adding them up to get a total count. |
| 377 | let in_edge_counters = self.basic_coverage_blocks.predecessors[bcb] | |
| 398 | let in_edge_counters = predecessors | |
| 378 | 399 | .iter() |
| 379 | 400 | .copied() |
| 380 | 401 | .map(|from_bcb| self.get_or_make_edge_counter(from_bcb, bcb)) |
| ... | ... | @@ -394,16 +415,19 @@ impl<'a> MakeBcbCounters<'a> { |
| 394 | 415 | from_bcb: BasicCoverageBlock, |
| 395 | 416 | to_bcb: BasicCoverageBlock, |
| 396 | 417 | ) -> BcbCounter { |
| 397 | // If the target BCB has only one in-edge (i.e. this one), then create | |
| 398 | // a node counter instead, since it will have the same value. | |
| 399 | if !self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) { | |
| 400 | assert_eq!([from_bcb].as_slice(), self.basic_coverage_blocks.predecessors[to_bcb]); | |
| 418 | // If the target node has exactly one in-edge (i.e. this one), then just | |
| 419 | // use the node's counter, since it will have the same value. | |
| 420 | if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) { | |
| 421 | assert_eq!(sole_pred, from_bcb); | |
| 422 | // This call must take care not to invoke `get_or_make_edge` for | |
| 423 | // this edge, since that would result in infinite recursion! | |
| 401 | 424 | return self.get_or_make_node_counter(to_bcb); |
| 402 | 425 | } |
| 403 | 426 | |
| 404 | // If the source BCB has only one successor (assumed to be the given target), an edge | |
| 405 | // counter is unnecessary. Just get or make a counter for the source BCB. | |
| 406 | if self.bcb_successors(from_bcb).len() == 1 { | |
| 427 | // If the source node has exactly one out-edge (i.e. this one) and would have | |
| 428 | // the same execution count as that edge, then just use the node's counter. | |
| 429 | if let Some(simple_succ) = self.basic_coverage_blocks.simple_successor(from_bcb) { | |
| 430 | assert_eq!(simple_succ, to_bcb); | |
| 407 | 431 | return self.get_or_make_node_counter(from_bcb); |
| 408 | 432 | } |
| 409 | 433 | |
| ... | ... | @@ -416,118 +440,81 @@ impl<'a> MakeBcbCounters<'a> { |
| 416 | 440 | } |
| 417 | 441 | |
| 418 | 442 | // Make a new counter to count this edge. |
| 419 | let counter_kind = | |
| 420 | self.coverage_counters.make_counter(CounterIncrementSite::Edge { from_bcb, to_bcb }); | |
| 421 | debug!("Edge {from_bcb:?}->{to_bcb:?} gets a new counter: {counter_kind:?}"); | |
| 422 | self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind) | |
| 443 | self.coverage_counters.make_phys_edge_counter(from_bcb, to_bcb) | |
| 423 | 444 | } |
| 424 | 445 | |
| 425 | /// Choose one of the out-edges of `from_bcb` to receive an expression | |
| 426 | /// instead of a physical counter, and returns that edge's target node. | |
| 427 | /// | |
| 428 | /// - Precondition: The node must have at least one out-edge without a counter. | |
| 429 | /// - Postcondition: The selected edge does not have an edge counter. | |
| 446 | /// Given a set of candidate out-edges (represented by their successor node), | |
| 447 | /// choose one to be given a counter expression instead of a physical counter. | |
| 430 | 448 | fn choose_out_edge_for_expression( |
| 431 | 449 | &self, |
| 432 | 450 | traversal: &TraverseCoverageGraphWithLoops<'_>, |
| 433 | from_bcb: BasicCoverageBlock, | |
| 434 | ) -> BasicCoverageBlock { | |
| 435 | if let Some(reloop_target) = self.find_good_reloop_edge(traversal, from_bcb) { | |
| 436 | assert!(self.edge_has_no_counter(from_bcb, reloop_target)); | |
| 451 | candidate_successors: &[BasicCoverageBlock], | |
| 452 | ) -> Option<BasicCoverageBlock> { | |
| 453 | // Try to find a candidate that leads back to the top of a loop, | |
| 454 | // because reloop edges tend to be executed more times than loop-exit edges. | |
| 455 | if let Some(reloop_target) = self.find_good_reloop_edge(traversal, &candidate_successors) { | |
| 437 | 456 | debug!("Selecting reloop target {reloop_target:?} to get an expression"); |
| 438 | return reloop_target; | |
| 457 | return Some(reloop_target); | |
| 439 | 458 | } |
| 440 | 459 | |
| 441 | // We couldn't identify a "good" edge, so just choose any edge that | |
| 442 | // doesn't already have a counter. | |
| 443 | let arbitrary_target = self | |
| 444 | .bcb_successors(from_bcb) | |
| 445 | .iter() | |
| 446 | .copied() | |
| 447 | .find(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb)) | |
| 448 | .expect("precondition: at least one out-edge without a counter"); | |
| 460 | // We couldn't identify a "good" edge, so just choose an arbitrary one. | |
| 461 | let arbitrary_target = candidate_successors.first().copied()?; | |
| 449 | 462 | debug!(?arbitrary_target, "selecting arbitrary out-edge to get an expression"); |
| 450 | arbitrary_target | |
| 463 | Some(arbitrary_target) | |
| 451 | 464 | } |
| 452 | 465 | |
| 453 | /// Tries to find an edge that leads back to the top of a loop, and that | |
| 454 | /// doesn't already have a counter. Such edges are good candidates to | |
| 455 | /// be given an expression (instead of a physical counter), because they | |
| 456 | /// will tend to be executed more times than a loop-exit edge. | |
| 466 | /// Given a set of candidate out-edges (represented by their successor node), | |
| 467 | /// tries to find one that leads back to the top of a loop. | |
| 468 | /// | |
| 469 | /// Reloop edges are good candidates for counter expressions, because they | |
| 470 | /// will tend to be executed more times than a loop-exit edge, so it's nice | |
| 471 | /// for them to be able to avoid a physical counter increment. | |
| 457 | 472 | fn find_good_reloop_edge( |
| 458 | 473 | &self, |
| 459 | 474 | traversal: &TraverseCoverageGraphWithLoops<'_>, |
| 460 | from_bcb: BasicCoverageBlock, | |
| 475 | candidate_successors: &[BasicCoverageBlock], | |
| 461 | 476 | ) -> Option<BasicCoverageBlock> { |
| 462 | let successors = self.bcb_successors(from_bcb); | |
| 477 | // If there are no candidates, avoid iterating over the loop stack. | |
| 478 | if candidate_successors.is_empty() { | |
| 479 | return None; | |
| 480 | } | |
| 463 | 481 | |
| 464 | 482 | // Consider each loop on the current traversal context stack, top-down. |
| 465 | 483 | for reloop_bcbs in traversal.reloop_bcbs_per_loop() { |
| 466 | let mut all_edges_exit_this_loop = true; | |
| 467 | ||
| 468 | // Try to find an out-edge that doesn't exit this loop and doesn't | |
| 469 | // already have a counter. | |
| 470 | for &target_bcb in successors { | |
| 484 | // Try to find a candidate edge that doesn't exit this loop. | |
| 485 | for &target_bcb in candidate_successors { | |
| 471 | 486 | // An edge is a reloop edge if its target dominates any BCB that has |
| 472 | 487 | // an edge back to the loop header. (Otherwise it's an exit edge.) |
| 473 | 488 | let is_reloop_edge = reloop_bcbs.iter().any(|&reloop_bcb| { |
| 474 | 489 | self.basic_coverage_blocks.dominates(target_bcb, reloop_bcb) |
| 475 | 490 | }); |
| 476 | ||
| 477 | 491 | if is_reloop_edge { |
| 478 | all_edges_exit_this_loop = false; | |
| 479 | if self.edge_has_no_counter(from_bcb, target_bcb) { | |
| 480 | // We found a good out-edge to be given an expression. | |
| 481 | return Some(target_bcb); | |
| 482 | } | |
| 483 | // Keep looking for another reloop edge without a counter. | |
| 484 | } else { | |
| 485 | // This edge exits the loop. | |
| 492 | // We found a good out-edge to be given an expression. | |
| 493 | return Some(target_bcb); | |
| 486 | 494 | } |
| 487 | 495 | } |
| 488 | 496 | |
| 489 | if !all_edges_exit_this_loop { | |
| 490 | // We found one or more reloop edges, but all of them already | |
| 491 | // have counters. Let the caller choose one of the other edges. | |
| 492 | debug!("All reloop edges had counters; skipping the other loops"); | |
| 493 | return None; | |
| 494 | } | |
| 495 | ||
| 496 | // All of the out-edges exit this loop, so keep looking for a good | |
| 497 | // reloop edge for one of the outer loops. | |
| 497 | // All of the candidate edges exit this loop, so keep looking | |
| 498 | // for a good reloop edge for one of the outer loops. | |
| 498 | 499 | } |
| 499 | 500 | |
| 500 | 501 | None |
| 501 | 502 | } |
| 502 | 503 | |
| 503 | #[inline] | |
| 504 | fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] { | |
| 505 | &self.basic_coverage_blocks.predecessors[bcb] | |
| 506 | } | |
| 507 | ||
| 508 | #[inline] | |
| 509 | fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] { | |
| 510 | &self.basic_coverage_blocks.successors[bcb] | |
| 511 | } | |
| 512 | ||
| 513 | 504 | #[inline] |
| 514 | 505 | fn edge_has_no_counter( |
| 515 | 506 | &self, |
| 516 | 507 | from_bcb: BasicCoverageBlock, |
| 517 | 508 | to_bcb: BasicCoverageBlock, |
| 518 | 509 | ) -> bool { |
| 519 | self.edge_counter(from_bcb, to_bcb).is_none() | |
| 520 | } | |
| 510 | let edge_counter = | |
| 511 | if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) { | |
| 512 | assert_eq!(sole_pred, from_bcb); | |
| 513 | self.coverage_counters.bcb_counters[to_bcb] | |
| 514 | } else { | |
| 515 | self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)).copied() | |
| 516 | }; | |
| 521 | 517 | |
| 522 | fn edge_counter( | |
| 523 | &self, | |
| 524 | from_bcb: BasicCoverageBlock, | |
| 525 | to_bcb: BasicCoverageBlock, | |
| 526 | ) -> Option<&BcbCounter> { | |
| 527 | if self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) { | |
| 528 | self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)) | |
| 529 | } else { | |
| 530 | self.coverage_counters.bcb_counters[to_bcb].as_ref() | |
| 531 | } | |
| 518 | edge_counter.is_none() | |
| 532 | 519 | } |
| 533 | 520 | } |
compiler/rustc_mir_transform/src/coverage/graph.rs+61-29| ... | ... | @@ -87,7 +87,11 @@ impl CoverageGraph { |
| 87 | 87 | for &bb in basic_blocks.iter() { |
| 88 | 88 | bb_to_bcb[bb] = Some(bcb); |
| 89 | 89 | } |
| 90 | let bcb_data = BasicCoverageBlockData::from(basic_blocks); | |
| 90 | ||
| 91 | let is_out_summable = basic_blocks.last().map_or(false, |&bb| { | |
| 92 | bcb_filtered_successors(mir_body[bb].terminator()).is_out_summable() | |
| 93 | }); | |
| 94 | let bcb_data = BasicCoverageBlockData { basic_blocks, is_out_summable }; | |
| 91 | 95 | debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); |
| 92 | 96 | bcbs.push(bcb_data); |
| 93 | 97 | }; |
| ... | ... | @@ -161,23 +165,33 @@ impl CoverageGraph { |
| 161 | 165 | self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b) |
| 162 | 166 | } |
| 163 | 167 | |
| 164 | /// Returns true if the given node has 2 or more in-edges, i.e. 2 or more | |
| 165 | /// predecessors. | |
| 166 | /// | |
| 167 | /// This property is interesting to code that assigns counters to nodes and | |
| 168 | /// edges, because if a node _doesn't_ have multiple in-edges, then there's | |
| 169 | /// no benefit in having a separate counter for its in-edge, because it | |
| 170 | /// would have the same value as the node's own counter. | |
| 171 | /// | |
| 172 | /// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]? | |
| 173 | #[inline(always)] | |
| 174 | pub(crate) fn bcb_has_multiple_in_edges(&self, bcb: BasicCoverageBlock) -> bool { | |
| 175 | // Even though bcb0 conceptually has an extra virtual in-edge due to | |
| 176 | // being the entry point, we've already asserted that it has no _other_ | |
| 177 | // in-edges, so there's no possibility of it having _multiple_ in-edges. | |
| 178 | // (And since its virtual in-edge doesn't exist in the graph, that edge | |
| 179 | // can't have a separate counter anyway.) | |
| 180 | self.predecessors[bcb].len() > 1 | |
| 168 | /// Returns the source of this node's sole in-edge, if it has exactly one. | |
| 169 | /// That edge can be assumed to have the same execution count as the node | |
| 170 | /// itself (in the absence of panics). | |
| 171 | pub(crate) fn sole_predecessor( | |
| 172 | &self, | |
| 173 | to_bcb: BasicCoverageBlock, | |
| 174 | ) -> Option<BasicCoverageBlock> { | |
| 175 | // Unlike `simple_successor`, there is no need for extra checks here. | |
| 176 | if let &[from_bcb] = self.predecessors[to_bcb].as_slice() { Some(from_bcb) } else { None } | |
| 177 | } | |
| 178 | ||
| 179 | /// Returns the target of this node's sole out-edge, if it has exactly | |
| 180 | /// one, but only if that edge can be assumed to have the same execution | |
| 181 | /// count as the node itself (in the absence of panics). | |
| 182 | pub(crate) fn simple_successor( | |
| 183 | &self, | |
| 184 | from_bcb: BasicCoverageBlock, | |
| 185 | ) -> Option<BasicCoverageBlock> { | |
| 186 | // If a node's count is the sum of its out-edges, and it has exactly | |
| 187 | // one out-edge, then that edge has the same count as the node. | |
| 188 | if self.bcbs[from_bcb].is_out_summable | |
| 189 | && let &[to_bcb] = self.successors[from_bcb].as_slice() | |
| 190 | { | |
| 191 | Some(to_bcb) | |
| 192 | } else { | |
| 193 | None | |
| 194 | } | |
| 181 | 195 | } |
| 182 | 196 | } |
| 183 | 197 | |
| ... | ... | @@ -266,14 +280,16 @@ rustc_index::newtype_index! { |
| 266 | 280 | #[derive(Debug, Clone)] |
| 267 | 281 | pub(crate) struct BasicCoverageBlockData { |
| 268 | 282 | pub(crate) basic_blocks: Vec<BasicBlock>, |
| 283 | ||
| 284 | /// If true, this node's execution count can be assumed to be the sum of the | |
| 285 | /// execution counts of all of its **out-edges** (assuming no panics). | |
| 286 | /// | |
| 287 | /// Notably, this is false for a node ending with [`TerminatorKind::Yield`], | |
| 288 | /// because the yielding coroutine might not be resumed. | |
| 289 | pub(crate) is_out_summable: bool, | |
| 269 | 290 | } |
| 270 | 291 | |
| 271 | 292 | impl BasicCoverageBlockData { |
| 272 | fn from(basic_blocks: Vec<BasicBlock>) -> Self { | |
| 273 | assert!(basic_blocks.len() > 0); | |
| 274 | Self { basic_blocks } | |
| 275 | } | |
| 276 | ||
| 277 | 293 | #[inline(always)] |
| 278 | 294 | pub(crate) fn leader_bb(&self) -> BasicBlock { |
| 279 | 295 | self.basic_blocks[0] |
| ... | ... | @@ -295,6 +311,9 @@ enum CoverageSuccessors<'a> { |
| 295 | 311 | Chainable(BasicBlock), |
| 296 | 312 | /// The block cannot be combined into the same BCB as its successor(s). |
| 297 | 313 | NotChainable(&'a [BasicBlock]), |
| 314 | /// Yield terminators are not chainable, and their execution count can also | |
| 315 | /// differ from the execution count of their out-edge. | |
| 316 | Yield(BasicBlock), | |
| 298 | 317 | } |
| 299 | 318 | |
| 300 | 319 | impl CoverageSuccessors<'_> { |
| ... | ... | @@ -302,6 +321,17 @@ impl CoverageSuccessors<'_> { |
| 302 | 321 | match self { |
| 303 | 322 | Self::Chainable(_) => true, |
| 304 | 323 | Self::NotChainable(_) => false, |
| 324 | Self::Yield(_) => false, | |
| 325 | } | |
| 326 | } | |
| 327 | ||
| 328 | /// Returns true if the terminator itself is assumed to have the same | |
| 329 | /// execution count as the sum of its out-edges (assuming no panics). | |
| 330 | fn is_out_summable(&self) -> bool { | |
| 331 | match self { | |
| 332 | Self::Chainable(_) => true, | |
| 333 | Self::NotChainable(_) => true, | |
| 334 | Self::Yield(_) => false, | |
| 305 | 335 | } |
| 306 | 336 | } |
| 307 | 337 | } |
| ... | ... | @@ -312,7 +342,9 @@ impl IntoIterator for CoverageSuccessors<'_> { |
| 312 | 342 | |
| 313 | 343 | fn into_iter(self) -> Self::IntoIter { |
| 314 | 344 | match self { |
| 315 | Self::Chainable(bb) => Some(bb).into_iter().chain((&[]).iter().copied()), | |
| 345 | Self::Chainable(bb) | Self::Yield(bb) => { | |
| 346 | Some(bb).into_iter().chain((&[]).iter().copied()) | |
| 347 | } | |
| 316 | 348 | Self::NotChainable(bbs) => None.into_iter().chain(bbs.iter().copied()), |
| 317 | 349 | } |
| 318 | 350 | } |
| ... | ... | @@ -331,7 +363,7 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera |
| 331 | 363 | |
| 332 | 364 | // A yield terminator has exactly 1 successor, but should not be chained, |
| 333 | 365 | // because its resume edge has a different execution count. |
| 334 | Yield { ref resume, .. } => CoverageSuccessors::NotChainable(std::slice::from_ref(resume)), | |
| 366 | Yield { resume, .. } => CoverageSuccessors::Yield(resume), | |
| 335 | 367 | |
| 336 | 368 | // These terminators have exactly one coverage-relevant successor, |
| 337 | 369 | // and can be chained into it. |
| ... | ... | @@ -341,15 +373,15 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera |
| 341 | 373 | | FalseUnwind { real_target: target, .. } |
| 342 | 374 | | Goto { target } => CoverageSuccessors::Chainable(target), |
| 343 | 375 | |
| 344 | // A call terminator can normally be chained, except when they have no | |
| 345 | // successor because they are known to diverge. | |
| 376 | // A call terminator can normally be chained, except when it has no | |
| 377 | // successor because it is known to diverge. | |
| 346 | 378 | Call { target: maybe_target, .. } => match maybe_target { |
| 347 | 379 | Some(target) => CoverageSuccessors::Chainable(target), |
| 348 | 380 | None => CoverageSuccessors::NotChainable(&[]), |
| 349 | 381 | }, |
| 350 | 382 | |
| 351 | // An inline asm terminator can normally be chained, except when it diverges or uses asm | |
| 352 | // goto. | |
| 383 | // An inline asm terminator can normally be chained, except when it | |
| 384 | // diverges or uses asm goto. | |
| 353 | 385 | InlineAsm { ref targets, .. } => { |
| 354 | 386 | if let [target] = targets[..] { |
| 355 | 387 | CoverageSuccessors::Chainable(target) |
library/std/src/sync/lazy_lock.rs-2| ... | ... | @@ -44,8 +44,6 @@ union Data<T, F> { |
| 44 | 44 | /// |
| 45 | 45 | /// // The `String` is built, stored in the `LazyLock`, and returned as `&String`. |
| 46 | 46 | /// let _ = &*DEEP_THOUGHT; |
| 47 | /// // The `String` is retrieved from the `LazyLock` and returned as `&String`. | |
| 48 | /// let _ = &*DEEP_THOUGHT; | |
| 49 | 47 | /// ``` |
| 50 | 48 | /// |
| 51 | 49 | /// Initialize fields with `LazyLock`. |
src/tools/run-make-support/src/fs.rs+130-50| ... | ... | @@ -1,42 +1,6 @@ |
| 1 | 1 | use std::io; |
| 2 | 2 | use std::path::{Path, PathBuf}; |
| 3 | 3 | |
| 4 | // FIXME(jieyouxu): modify create_symlink to panic on windows. | |
| 5 | ||
| 6 | /// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix. | |
| 7 | #[cfg(target_family = "windows")] | |
| 8 | pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { | |
| 9 | if link.as_ref().exists() { | |
| 10 | std::fs::remove_dir(link.as_ref()).unwrap(); | |
| 11 | } | |
| 12 | if original.as_ref().is_file() { | |
| 13 | std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()).expect(&format!( | |
| 14 | "failed to create symlink {:?} for {:?}", | |
| 15 | link.as_ref().display(), | |
| 16 | original.as_ref().display(), | |
| 17 | )); | |
| 18 | } else { | |
| 19 | std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref()).expect(&format!( | |
| 20 | "failed to create symlink {:?} for {:?}", | |
| 21 | link.as_ref().display(), | |
| 22 | original.as_ref().display(), | |
| 23 | )); | |
| 24 | } | |
| 25 | } | |
| 26 | ||
| 27 | /// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix. | |
| 28 | #[cfg(target_family = "unix")] | |
| 29 | pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { | |
| 30 | if link.as_ref().exists() { | |
| 31 | std::fs::remove_dir(link.as_ref()).unwrap(); | |
| 32 | } | |
| 33 | std::os::unix::fs::symlink(original.as_ref(), link.as_ref()).expect(&format!( | |
| 34 | "failed to create symlink {:?} for {:?}", | |
| 35 | link.as_ref().display(), | |
| 36 | original.as_ref().display(), | |
| 37 | )); | |
| 38 | } | |
| 39 | ||
| 40 | 4 | /// Copy a directory into another. |
| 41 | 5 | pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) { |
| 42 | 6 | fn copy_dir_all_inner(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> { |
| ... | ... | @@ -50,7 +14,31 @@ pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) { |
| 50 | 14 | if ty.is_dir() { |
| 51 | 15 | copy_dir_all_inner(entry.path(), dst.join(entry.file_name()))?; |
| 52 | 16 | } else if ty.is_symlink() { |
| 53 | copy_symlink(entry.path(), dst.join(entry.file_name()))?; | |
| 17 | // Traverse symlink once to find path of target entity. | |
| 18 | let target_path = std::fs::read_link(entry.path())?; | |
| 19 | ||
| 20 | let new_symlink_path = dst.join(entry.file_name()); | |
| 21 | #[cfg(windows)] | |
| 22 | { | |
| 23 | use std::os::windows::fs::FileTypeExt; | |
| 24 | if ty.is_symlink_dir() { | |
| 25 | std::os::windows::fs::symlink_dir(&target_path, new_symlink_path)?; | |
| 26 | } else { | |
| 27 | // Target may be a file or another symlink, in any case we can use | |
| 28 | // `symlink_file` here. | |
| 29 | std::os::windows::fs::symlink_file(&target_path, new_symlink_path)?; | |
| 30 | } | |
| 31 | } | |
| 32 | #[cfg(unix)] | |
| 33 | { | |
| 34 | std::os::unix::fs::symlink(target_path, new_symlink_path)?; | |
| 35 | } | |
| 36 | #[cfg(not(any(windows, unix)))] | |
| 37 | { | |
| 38 | // Technically there's also wasi, but I have no clue about wasi symlink | |
| 39 | // semantics and which wasi targets / environment support symlinks. | |
| 40 | unimplemented!("unsupported target"); | |
| 41 | } | |
| 54 | 42 | } else { |
| 55 | 43 | std::fs::copy(entry.path(), dst.join(entry.file_name()))?; |
| 56 | 44 | } |
| ... | ... | @@ -69,12 +57,6 @@ pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) { |
| 69 | 57 | } |
| 70 | 58 | } |
| 71 | 59 | |
| 72 | fn copy_symlink<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { | |
| 73 | let target_path = std::fs::read_link(from).unwrap(); | |
| 74 | create_symlink(target_path, to); | |
| 75 | Ok(()) | |
| 76 | } | |
| 77 | ||
| 78 | 60 | /// Helper for reading entries in a given directory. |
| 79 | 61 | pub fn read_dir_entries<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F) { |
| 80 | 62 | for entry in read_dir(dir) { |
| ... | ... | @@ -85,8 +67,17 @@ pub fn read_dir_entries<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F |
| 85 | 67 | /// A wrapper around [`std::fs::remove_file`] which includes the file path in the panic message. |
| 86 | 68 | #[track_caller] |
| 87 | 69 | pub fn remove_file<P: AsRef<Path>>(path: P) { |
| 88 | std::fs::remove_file(path.as_ref()) | |
| 89 | .expect(&format!("the file in path \"{}\" could not be removed", path.as_ref().display())); | |
| 70 | if let Err(e) = std::fs::remove_file(path.as_ref()) { | |
| 71 | panic!("failed to remove file at `{}`: {e}", path.as_ref().display()); | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | /// A wrapper around [`std::fs::remove_dir`] which includes the directory path in the panic message. | |
| 76 | #[track_caller] | |
| 77 | pub fn remove_dir<P: AsRef<Path>>(path: P) { | |
| 78 | if let Err(e) = std::fs::remove_dir(path.as_ref()) { | |
| 79 | panic!("failed to remove directory at `{}`: {e}", path.as_ref().display()); | |
| 80 | } | |
| 90 | 81 | } |
| 91 | 82 | |
| 92 | 83 | /// A wrapper around [`std::fs::copy`] which includes the file path in the panic message. |
| ... | ... | @@ -165,13 +156,32 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) { |
| 165 | 156 | )); |
| 166 | 157 | } |
| 167 | 158 | |
| 168 | /// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message. | |
| 159 | /// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message. Note | |
| 160 | /// that this will traverse symlinks and will return metadata about the target file. Use | |
| 161 | /// [`symlink_metadata`] if you don't want to traverse symlinks. | |
| 162 | /// | |
| 163 | /// See [`std::fs::metadata`] docs for more details. | |
| 169 | 164 | #[track_caller] |
| 170 | 165 | pub fn metadata<P: AsRef<Path>>(path: P) -> std::fs::Metadata { |
| 171 | std::fs::metadata(path.as_ref()).expect(&format!( | |
| 172 | "the file's metadata in path \"{}\" could not be read", | |
| 173 | path.as_ref().display() | |
| 174 | )) | |
| 166 | match std::fs::metadata(path.as_ref()) { | |
| 167 | Ok(m) => m, | |
| 168 | Err(e) => panic!("failed to read file metadata at `{}`: {e}", path.as_ref().display()), | |
| 169 | } | |
| 170 | } | |
| 171 | ||
| 172 | /// A wrapper around [`std::fs::symlink_metadata`] which includes the file path in the panic | |
| 173 | /// message. Note that this will not traverse symlinks and will return metadata about the filesystem | |
| 174 | /// entity itself. Use [`metadata`] if you want to traverse symlinks. | |
| 175 | /// | |
| 176 | /// See [`std::fs::symlink_metadata`] docs for more details. | |
| 177 | #[track_caller] | |
| 178 | pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> std::fs::Metadata { | |
| 179 | match std::fs::symlink_metadata(path.as_ref()) { | |
| 180 | Ok(m) => m, | |
| 181 | Err(e) => { | |
| 182 | panic!("failed to read file metadata (shallow) at `{}`: {e}", path.as_ref().display()) | |
| 183 | } | |
| 184 | } | |
| 175 | 185 | } |
| 176 | 186 | |
| 177 | 187 | /// A wrapper around [`std::fs::rename`] which includes the file path in the panic message. |
| ... | ... | @@ -205,3 +215,73 @@ pub fn shallow_find_dir_entries<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> { |
| 205 | 215 | } |
| 206 | 216 | output |
| 207 | 217 | } |
| 218 | ||
| 219 | /// Create a new symbolic link to a directory. | |
| 220 | /// | |
| 221 | /// # Removing the symlink | |
| 222 | /// | |
| 223 | /// - On Windows, a symlink-to-directory needs to be removed with a corresponding [`fs::remove_dir`] | |
| 224 | /// and not [`fs::remove_file`]. | |
| 225 | /// - On Unix, remove the symlink with [`fs::remove_file`]. | |
| 226 | /// | |
| 227 | /// [`fs::remove_dir`]: crate::fs::remove_dir | |
| 228 | /// [`fs::remove_file`]: crate::fs::remove_file | |
| 229 | pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { | |
| 230 | #[cfg(unix)] | |
| 231 | { | |
| 232 | if let Err(e) = std::os::unix::fs::symlink(original.as_ref(), link.as_ref()) { | |
| 233 | panic!( | |
| 234 | "failed to create symlink: original=`{}`, link=`{}`: {e}", | |
| 235 | original.as_ref().display(), | |
| 236 | link.as_ref().display() | |
| 237 | ); | |
| 238 | } | |
| 239 | } | |
| 240 | #[cfg(windows)] | |
| 241 | { | |
| 242 | if let Err(e) = std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref()) { | |
| 243 | panic!( | |
| 244 | "failed to create symlink-to-directory: original=`{}`, link=`{}`: {e}", | |
| 245 | original.as_ref().display(), | |
| 246 | link.as_ref().display() | |
| 247 | ); | |
| 248 | } | |
| 249 | } | |
| 250 | #[cfg(not(any(windows, unix)))] | |
| 251 | { | |
| 252 | unimplemented!("target family not currently supported") | |
| 253 | } | |
| 254 | } | |
| 255 | ||
| 256 | /// Create a new symbolic link to a file. | |
| 257 | /// | |
| 258 | /// # Removing the symlink | |
| 259 | /// | |
| 260 | /// On both Windows and Unix, a symlink-to-file needs to be removed with a corresponding | |
| 261 | /// [`fs::remove_file`](crate::fs::remove_file) and not [`fs::remove_dir`](crate::fs::remove_dir). | |
| 262 | pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { | |
| 263 | #[cfg(unix)] | |
| 264 | { | |
| 265 | if let Err(e) = std::os::unix::fs::symlink(original.as_ref(), link.as_ref()) { | |
| 266 | panic!( | |
| 267 | "failed to create symlink: original=`{}`, link=`{}`: {e}", | |
| 268 | original.as_ref().display(), | |
| 269 | link.as_ref().display() | |
| 270 | ); | |
| 271 | } | |
| 272 | } | |
| 273 | #[cfg(windows)] | |
| 274 | { | |
| 275 | if let Err(e) = std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()) { | |
| 276 | panic!( | |
| 277 | "failed to create symlink-to-file: original=`{}`, link=`{}`: {e}", | |
| 278 | original.as_ref().display(), | |
| 279 | link.as_ref().display() | |
| 280 | ); | |
| 281 | } | |
| 282 | } | |
| 283 | #[cfg(not(any(windows, unix)))] | |
| 284 | { | |
| 285 | unimplemented!("target family not currently supported") | |
| 286 | } | |
| 287 | } |
tests/run-make/invalid-symlink-search-path/rmake.rs+9-8| ... | ... | @@ -1,13 +1,14 @@ |
| 1 | // In this test, the symlink created is invalid (valid relative to the root, but not | |
| 2 | // relatively to where it is located), and used to cause an internal | |
| 3 | // compiler error (ICE) when passed as a library search path. This was fixed in #26044, | |
| 4 | // and this test checks that the invalid symlink is instead simply ignored. | |
| 1 | // In this test, the symlink created is invalid (valid relative to the root, but not relatively to | |
| 2 | // where it is located), and used to cause an internal compiler error (ICE) when passed as a library | |
| 3 | // search path. This was fixed in #26044, and this test checks that the invalid symlink is instead | |
| 4 | // simply ignored. | |
| 5 | // | |
| 5 | 6 | // See https://github.com/rust-lang/rust/issues/26006 |
| 6 | 7 | |
| 7 | 8 | //@ needs-symlink |
| 8 | 9 | //Reason: symlink requires elevated permission in Windows |
| 9 | 10 | |
| 10 | use run_make_support::{rfs, rustc}; | |
| 11 | use run_make_support::{path, rfs, rustc}; | |
| 11 | 12 | |
| 12 | 13 | fn main() { |
| 13 | 14 | // We create two libs: `bar` which depends on `foo`. We need to compile `foo` first. |
| ... | ... | @@ -20,9 +21,9 @@ fn main() { |
| 20 | 21 | .metadata("foo") |
| 21 | 22 | .output("out/foo/libfoo.rlib") |
| 22 | 23 | .run(); |
| 23 | rfs::create_dir("out/bar"); | |
| 24 | rfs::create_dir("out/bar/deps"); | |
| 25 | rfs::create_symlink("out/foo/libfoo.rlib", "out/bar/deps/libfoo.rlib"); | |
| 24 | rfs::create_dir_all("out/bar/deps"); | |
| 25 | rfs::symlink_file(path("out/foo/libfoo.rlib"), path("out/bar/deps/libfoo.rlib")); | |
| 26 | ||
| 26 | 27 | // Check that the invalid symlink does not cause an ICE |
| 27 | 28 | rustc() |
| 28 | 29 | .input("in/bar/lib.rs") |
tests/run-make/symlinked-extern/rmake.rs+12-12| ... | ... | @@ -1,22 +1,22 @@ |
| 1 | // Crates that are resolved normally have their path canonicalized and all | |
| 2 | // symlinks resolved. This did not happen for paths specified | |
| 3 | // using the --extern option to rustc, which could lead to rustc thinking | |
| 4 | // that it encountered two different versions of a crate, when it's | |
| 5 | // actually the same version found through different paths. | |
| 6 | // See https://github.com/rust-lang/rust/pull/16505 | |
| 7 | ||
| 8 | // This test checks that --extern and symlinks together | |
| 9 | // can result in successful compilation. | |
| 1 | // Crates that are resolved normally have their path canonicalized and all symlinks resolved. This | |
| 2 | // did not happen for paths specified using the `--extern` option to rustc, which could lead to | |
| 3 | // rustc thinking that it encountered two different versions of a crate, when it's actually the same | |
| 4 | // version found through different paths. | |
| 5 | // | |
| 6 | // This test checks that `--extern` and symlinks together can result in successful compilation. | |
| 7 | // | |
| 8 | // See <https://github.com/rust-lang/rust/pull/16505>. | |
| 10 | 9 | |
| 11 | 10 | //@ ignore-cross-compile |
| 12 | 11 | //@ needs-symlink |
| 13 | 12 | |
| 14 | use run_make_support::{cwd, rfs, rustc}; | |
| 13 | use run_make_support::{cwd, path, rfs, rustc}; | |
| 15 | 14 | |
| 16 | 15 | fn main() { |
| 17 | 16 | rustc().input("foo.rs").run(); |
| 18 | 17 | rfs::create_dir_all("other"); |
| 19 | rfs::create_symlink("libfoo.rlib", "other"); | |
| 18 | rfs::symlink_file(path("libfoo.rlib"), path("other").join("libfoo.rlib")); | |
| 19 | ||
| 20 | 20 | rustc().input("bar.rs").library_search_path(cwd()).run(); |
| 21 | rustc().input("baz.rs").extern_("foo", "other").library_search_path(cwd()).run(); | |
| 21 | rustc().input("baz.rs").extern_("foo", "other/libfoo.rlib").library_search_path(cwd()).run(); | |
| 22 | 22 | } |
tests/run-make/symlinked-libraries/rmake.rs+6-9| ... | ... | @@ -1,18 +1,15 @@ |
| 1 | // When a directory and a symlink simultaneously exist with the same name, | |
| 2 | // setting that name as the library search path should not cause rustc | |
| 3 | // to avoid looking in the symlink and cause an error. This test creates | |
| 4 | // a directory and a symlink named "other", and places the library in the symlink. | |
| 5 | // If it succeeds, the library was successfully found. | |
| 6 | // See https://github.com/rust-lang/rust/issues/12459 | |
| 1 | // Avoid erroring on symlinks pointing to the same file that are present in the library search path. | |
| 2 | // | |
| 3 | // See <https://github.com/rust-lang/rust/issues/12459>. | |
| 7 | 4 | |
| 8 | 5 | //@ ignore-cross-compile |
| 9 | 6 | //@ needs-symlink |
| 10 | 7 | |
| 11 | use run_make_support::{dynamic_lib_name, rfs, rustc}; | |
| 8 | use run_make_support::{cwd, dynamic_lib_name, path, rfs, rustc}; | |
| 12 | 9 | |
| 13 | 10 | fn main() { |
| 14 | 11 | rustc().input("foo.rs").arg("-Cprefer-dynamic").run(); |
| 15 | 12 | rfs::create_dir_all("other"); |
| 16 | rfs::create_symlink(dynamic_lib_name("foo"), "other"); | |
| 17 | rustc().input("bar.rs").library_search_path("other").run(); | |
| 13 | rfs::symlink_file(dynamic_lib_name("foo"), path("other").join(dynamic_lib_name("foo"))); | |
| 14 | rustc().input("bar.rs").library_search_path(cwd()).library_search_path("other").run(); | |
| 18 | 15 | } |
tests/run-make/symlinked-rlib/rmake.rs+1-1| ... | ... | @@ -12,6 +12,6 @@ use run_make_support::{cwd, rfs, rustc}; |
| 12 | 12 | |
| 13 | 13 | fn main() { |
| 14 | 14 | rustc().input("foo.rs").crate_type("rlib").output("foo.xxx").run(); |
| 15 | rfs::create_symlink("foo.xxx", "libfoo.rlib"); | |
| 15 | rfs::symlink_file("foo.xxx", "libfoo.rlib"); | |
| 16 | 16 | rustc().input("bar.rs").library_search_path(cwd()).run(); |
| 17 | 17 | } |