Skip to content

Commit e9c25b4

Browse files
authored
Rollup merge of #105793 - lukas-code:circular-deps, r=Mark-Simulacrum
Add note for mismatched types because of circular dependencies If you have crate A with a dependency on crate B, and crate B with a dev-dependency on A, then you might see "mismatched types" errors on types that seem to be equal. This PR adds a note that explains that the types are different, because crate B is compiled twice, one time with `cfg(test)` and one time without. I haven't found a good way to create circular dependencies in UI tests, so I abused the incremental tests instead. As a bonus, incremental tests support "cpass" now. related to #22750
2 parents 993b775 + 97915ab commit e9c25b4

File tree

4 files changed

+96
-22
lines changed

4 files changed

+96
-22
lines changed

Diff for: compiler/rustc_infer/src/infer/error_reporting/mod.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -615,9 +615,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
615615
}
616616

617617
let report_path_match = |err: &mut Diagnostic, did1: DefId, did2: DefId| {
618-
// Only external crates, if either is from a local
619-
// module we could have false positives
620-
if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
618+
// Only report definitions from different crates. If both definitions
619+
// are from a local module we could have false positives, e.g.
620+
// let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
621+
if did1.krate != did2.krate {
621622
let abs_path =
622623
|def_id| AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]);
623624

@@ -629,10 +630,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
629630
};
630631
if same_path().unwrap_or(false) {
631632
let crate_name = self.tcx.crate_name(did1.krate);
632-
err.note(&format!(
633-
"perhaps two different versions of crate `{}` are being used?",
634-
crate_name
635-
));
633+
let msg = if did1.is_local() || did2.is_local() {
634+
format!(
635+
"the crate `{crate_name}` is compiled multiple times, possibly with different configurations"
636+
)
637+
} else {
638+
format!(
639+
"perhaps two different versions of crate `{crate_name}` are being used?"
640+
)
641+
};
642+
err.note(msg);
636643
}
637644
}
638645
};

Diff for: src/tools/compiletest/src/runtest.rs

+35-15
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,15 @@ impl<'test> TestCx<'test> {
278278
Incremental => {
279279
let revision =
280280
self.revision.expect("incremental tests require a list of revisions");
281-
if revision.starts_with("rpass") || revision.starts_with("rfail") {
281+
if revision.starts_with("cpass")
282+
|| revision.starts_with("rpass")
283+
|| revision.starts_with("rfail")
284+
{
282285
true
283286
} else if revision.starts_with("cfail") {
284-
// FIXME: would be nice if incremental revs could start with "cpass"
285287
pm.is_some()
286288
} else {
287-
panic!("revision name must begin with rpass, rfail, or cfail");
289+
panic!("revision name must begin with cpass, rpass, rfail, or cfail");
288290
}
289291
}
290292
mode => panic!("unimplemented for mode {:?}", mode),
@@ -384,6 +386,20 @@ impl<'test> TestCx<'test> {
384386
}
385387
}
386388

389+
fn run_cpass_test(&self) {
390+
let emit_metadata = self.should_emit_metadata(self.pass_mode());
391+
let proc_res = self.compile_test(WillExecute::No, emit_metadata);
392+
393+
if !proc_res.status.success() {
394+
self.fatal_proc_rec("compilation failed!", &proc_res);
395+
}
396+
397+
// FIXME(#41968): Move this check to tidy?
398+
if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
399+
self.fatal("compile-pass tests with expected warnings should be moved to ui/");
400+
}
401+
}
402+
387403
fn run_rpass_test(&self) {
388404
let emit_metadata = self.should_emit_metadata(self.pass_mode());
389405
let should_run = self.run_if_enabled();
@@ -393,17 +409,15 @@ impl<'test> TestCx<'test> {
393409
self.fatal_proc_rec("compilation failed!", &proc_res);
394410
}
395411

412+
// FIXME(#41968): Move this check to tidy?
413+
if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
414+
self.fatal("run-pass tests with expected warnings should be moved to ui/");
415+
}
416+
396417
if let WillExecute::Disabled = should_run {
397418
return;
398419
}
399420

400-
// FIXME(#41968): Move this check to tidy?
401-
let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
402-
assert!(
403-
expected_errors.is_empty(),
404-
"run-pass tests with expected warnings should be moved to ui/"
405-
);
406-
407421
let proc_res = self.exec_compiled_test();
408422
if !proc_res.status.success() {
409423
self.fatal_proc_rec("test run failed!", &proc_res);
@@ -2913,10 +2927,11 @@ impl<'test> TestCx<'test> {
29132927
fn run_incremental_test(&self) {
29142928
// Basic plan for a test incremental/foo/bar.rs:
29152929
// - load list of revisions rpass1, cfail2, rpass3
2916-
// - each should begin with `rpass`, `cfail`, or `rfail`
2917-
// - if `rpass`, expect compile and execution to succeed
2930+
// - each should begin with `cpass`, `rpass`, `cfail`, or `rfail`
2931+
// - if `cpass`, expect compilation to succeed, don't execute
2932+
// - if `rpass`, expect compilation and execution to succeed
29182933
// - if `cfail`, expect compilation to fail
2919-
// - if `rfail`, expect execution to fail
2934+
// - if `rfail`, expect compilation to succeed and execution to fail
29202935
// - create a directory build/foo/bar.incremental
29212936
// - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass1
29222937
// - because name of revision starts with "rpass", expect success
@@ -2940,7 +2955,12 @@ impl<'test> TestCx<'test> {
29402955
print!("revision={:?} props={:#?}", revision, self.props);
29412956
}
29422957

2943-
if revision.starts_with("rpass") {
2958+
if revision.starts_with("cpass") {
2959+
if self.props.should_ice {
2960+
self.fatal("can only use should-ice in cfail tests");
2961+
}
2962+
self.run_cpass_test();
2963+
} else if revision.starts_with("rpass") {
29442964
if self.props.should_ice {
29452965
self.fatal("can only use should-ice in cfail tests");
29462966
}
@@ -2953,7 +2973,7 @@ impl<'test> TestCx<'test> {
29532973
} else if revision.starts_with("cfail") {
29542974
self.run_cfail_test();
29552975
} else {
2956-
self.fatal("revision name must begin with rpass, rfail, or cfail");
2976+
self.fatal("revision name must begin with cpass, rpass, rfail, or cfail");
29572977
}
29582978
}
29592979

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// edition: 2021
2+
// compile-flags: --crate-type lib --extern circular_dependencies={{build-base}}/circular-dependencies/libcircular_dependencies.rmeta --emit dep-info,metadata
3+
4+
use circular_dependencies::Foo;
5+
6+
pub fn consume_foo(_: Foo) {}
7+
8+
pub fn produce_foo() -> Foo {
9+
Foo
10+
}

Diff for: tests/incremental/circular-dependencies.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// ignore-tidy-linelength
2+
// revisions: cpass1 cfail2
3+
// edition: 2021
4+
// [cpass1] compile-flags: --crate-type lib --emit dep-info,metadata
5+
// [cfail2] aux-build: circular-dependencies-aux.rs
6+
// [cfail2] compile-flags: --test --extern aux={{build-base}}/circular-dependencies/auxiliary/libcircular_dependencies_aux.rmeta -L dependency={{build-base}}/circular-dependencies
7+
8+
pub struct Foo;
9+
//[cfail2]~^ NOTE `Foo` is defined in the current crate
10+
//[cfail2]~| NOTE `Foo` is defined in the current crate
11+
//[cfail2]~| NOTE `circular_dependencies::Foo` is defined in crate `circular_dependencies`
12+
//[cfail2]~| NOTE `circular_dependencies::Foo` is defined in crate `circular_dependencies`
13+
14+
pub fn consume_foo(_: Foo) {}
15+
//[cfail2]~^ NOTE function defined here
16+
17+
pub fn produce_foo() -> Foo {
18+
Foo
19+
}
20+
21+
#[test]
22+
fn test() {
23+
aux::consume_foo(produce_foo());
24+
//[cfail2]~^ ERROR mismatched types [E0308]
25+
//[cfail2]~| NOTE expected `circular_dependencies::Foo`, found `Foo`
26+
//[cfail2]~| NOTE arguments to this function are incorrect
27+
//[cfail2]~| NOTE `Foo` and `circular_dependencies::Foo` have similar names, but are actually distinct types
28+
//[cfail2]~| NOTE the crate `circular_dependencies` is compiled multiple times, possibly with different configurations
29+
//[cfail2]~| NOTE function defined here
30+
31+
consume_foo(aux::produce_foo());
32+
//[cfail2]~^ ERROR mismatched types [E0308]
33+
//[cfail2]~| NOTE expected `Foo`, found `circular_dependencies::Foo`
34+
//[cfail2]~| NOTE arguments to this function are incorrect
35+
//[cfail2]~| NOTE `circular_dependencies::Foo` and `Foo` have similar names, but are actually distinct types
36+
//[cfail2]~| NOTE the crate `circular_dependencies` is compiled multiple times, possibly with different configurations
37+
}

0 commit comments

Comments
 (0)