summaryrefslogtreecommitdiffstats
path: root/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs')
-rw-r--r--src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs74
1 files changed, 74 insertions, 0 deletions
diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs b/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs
new file mode 100644
index 000000000..fc53acf75
--- /dev/null
+++ b/src/test/incremental/thinlto/cgu_invalidated_when_import_removed.rs
@@ -0,0 +1,74 @@
+// revisions: cfail1 cfail2
+// compile-flags: -O -Zhuman-readable-cgu-names -Cllvm-args=-import-instr-limit=10
+// build-pass
+
+// rust-lang/rust#59535:
+//
+// Consider a call-graph like `[A] -> [B -> D] <- [C]` (where the letters are
+// functions and the modules are enclosed in `[]`)
+//
+// In our specific instance, the earlier compilations were inlining the call
+// to`B` into `A`; thus `A` ended up with an external reference to the symbol `D`
+// in its object code, to be resolved at subsequent link time. The LTO import
+// information provided by LLVM for those runs reflected that information: it
+// explicitly says during those runs, `B` definition and `D` declaration were
+// imported into `[A]`.
+//
+// The change between incremental builds was that the call `D <- C` was removed.
+//
+// That change, coupled with other decisions within `rustc`, made the compiler
+// decide to make `D` an internal symbol (since it was no longer accessed from
+// other codegen units, this makes sense locally). And then the definition of
+// `D` was inlined into `B` and `D` itself was eliminated entirely.
+//
+// The current LTO import information reported that `B` alone is imported into
+// `[A]` for the *current compilation*. So when the Rust compiler surveyed the
+// dependence graph, it determined that nothing `[A]` imports changed since the
+// last build (and `[A]` itself has not changed either), so it chooses to reuse
+// the object code generated during the previous compilation.
+//
+// But that previous object code has an unresolved reference to `D`, and that
+// causes a link time failure!
+
+fn main() {
+ foo::foo();
+ bar::baz();
+}
+
+mod foo {
+
+ // In cfail1, foo() gets inlined into main.
+ // In cfail2, ThinLTO decides that foo() does not get inlined into main, and
+ // instead bar() gets inlined into foo(). But faulty logic in our incr.
+ // ThinLTO implementation thought that `main()` is unchanged and thus reused
+ // the object file still containing a call to the now non-existent bar().
+ pub fn foo(){
+ bar()
+ }
+
+ // This function needs to be big so that it does not get inlined by ThinLTO
+ // but *does* get inlined into foo() once it is declared `internal` in
+ // cfail2.
+ pub fn bar(){
+ println!("quux1");
+ println!("quux2");
+ println!("quux3");
+ println!("quux4");
+ println!("quux5");
+ println!("quux6");
+ println!("quux7");
+ println!("quux8");
+ println!("quux9");
+ }
+}
+
+mod bar {
+
+ #[inline(never)]
+ pub fn baz() {
+ #[cfg(cfail1)]
+ {
+ crate::foo::bar();
+ }
+ }
+}