Skip to content
29 changes: 20 additions & 9 deletions compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,22 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
let mut result = None;
let Some(items) = args.list() else {
cx.expected_list(cx.attr_span);
return result;
let items = match args {
ArgParser::List(list) => list,
// This is an edgecase added because making this a hard error would break too many crates
// Specifically `#[link = "dl"]` is accepted with a FCW
// For more information, see https://github.com/rust-lang/rust/pull/143193
ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
let suggestions = <Self as CombineAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "link");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
}
_ => {
cx.expected_list(cx.attr_span);
return None;
}
};

let sess = cx.sess();
Expand Down Expand Up @@ -113,7 +125,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
}
};
if !cont {
return result;
return None;
}
}

Expand Down Expand Up @@ -202,7 +214,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
}
let Some((name, name_span)) = name else {
cx.emit_err(LinkRequiresName { span: cx.attr_span });
return result;
return None;
};

// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
Expand All @@ -218,15 +230,14 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
cx.emit_err(RawDylibNoNul { span: name_span });
}

result = Some(LinkEntry {
Some(LinkEntry {
span: cx.attr_span,
kind: kind.unwrap_or(NativeLibKind::Unspecified),
name,
cfg,
verbatim,
import_name_type,
});
result
})
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,7 @@ symbols! {
div,
div_assign,
diverging_block_default,
dl,
do_not_recommend,
doc,
doc_alias,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
span,
leaf_trait_predicate,
);
self.note_version_mismatch(&mut err, leaf_trait_predicate);
self.note_trait_version_mismatch(&mut err, leaf_trait_predicate);
self.note_adt_version_mismatch(&mut err, leaf_trait_predicate);
self.suggest_remove_await(&obligation, &mut err);
self.suggest_derive(&obligation, &mut err, leaf_trait_predicate);

Expand Down Expand Up @@ -2424,7 +2425,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
/// with the same path as `trait_ref`, a help message about
/// a probable version mismatch is added to `err`
fn note_version_mismatch(
fn note_trait_version_mismatch(
&self,
err: &mut Diag<'_>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
Expand Down Expand Up @@ -2464,15 +2465,87 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
impl_spans,
format!("trait impl{} with same name found", pluralize!(trait_impls.len())),
);
let trait_crate = self.tcx.crate_name(trait_with_same_path.krate);
let crate_msg =
format!("perhaps two different versions of crate `{trait_crate}` are being used?");
err.note(crate_msg);
self.note_two_crate_versions(trait_with_same_path, err);
suggested = true;
}
suggested
}

fn note_two_crate_versions(&self, did: DefId, err: &mut Diag<'_>) {
let crate_name = self.tcx.crate_name(did.krate);
let crate_msg =
format!("perhaps two different versions of crate `{crate_name}` are being used?");
err.note(crate_msg);
}

fn note_adt_version_mismatch(
&self,
err: &mut Diag<'_>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
let ty::Adt(impl_self_def, _) = trait_pred.self_ty().skip_binder().peel_refs().kind()
else {
return;
};

let impl_self_did = impl_self_def.did();

// We only want to warn about different versions of a dependency.
// If no dependency is involved, bail.
if impl_self_did.krate == LOCAL_CRATE {
return;
}

let impl_self_path = self.comparable_path(impl_self_did);
let impl_self_crate_name = self.tcx.crate_name(impl_self_did.krate);
let similar_items: UnordSet<_> = self
.tcx
.visible_parent_map(())
.items()
.filter_map(|(&item, _)| {
// If we found ourselves, ignore.
if impl_self_did == item {
return None;
}
// We only want to warn about different versions of a dependency.
// Ignore items from our own crate.
if item.krate == LOCAL_CRATE {
return None;
}
// We want to warn about different versions of a dependency.
// So make sure the crate names are the same.
if impl_self_crate_name != self.tcx.crate_name(item.krate) {
return None;
}
// Filter out e.g. constructors that often have the same path
// str as the relevant ADT.
if !self.tcx.def_kind(item).is_adt() {
return None;
}
let path = self.comparable_path(item);
// We don't know if our item or the one we found is the re-exported one.
// Check both cases.
let is_similar = path.ends_with(&impl_self_path) || impl_self_path.ends_with(&path);
is_similar.then_some((item, path))
})
.collect();

let mut similar_items =
similar_items.into_items().into_sorted_stable_ord_by_key(|(_, path)| path);
similar_items.dedup();

for (similar_item, _) in similar_items {
err.span_help(self.tcx.def_span(similar_item), "item with same name found");
self.note_two_crate_versions(similar_item, err);
}
}

/// Add a `::` prefix when comparing paths so that paths with just one item
/// like "Foo" does not equal the end of "OtherFoo".
fn comparable_path(&self, did: DefId) -> String {
format!("::{}", self.tcx.def_path_str(did))
}

/// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
/// `trait_ref`.
///
Expand Down
31 changes: 13 additions & 18 deletions tests/codegen-llvm/autodiff/autodiffv2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,14 @@
// but each shadow argument is `width` times larger (thus 16 and 20 elements here).
// `d_square3` instead takes `width` (4) shadow arguments, which are all the same size as the
// original function arguments.
//
// FIXME(autodiff): We currently can't test `d_square1` and `d_square3` in the same file, since they
// generate the same dummy functions which get merged by LLVM, breaking pieces of our pipeline which
// try to rewrite the dummy functions later. We should consider to change to pure declarations both
// in our frontend and in the llvm backend to avoid these issues.

#![feature(autodiff)]

use std::autodiff::autodiff_forward;

// CHECK: ;
#[no_mangle]
//#[autodiff(d_square1, Forward, Dual, Dual)]
#[autodiff_forward(d_square1, Dual, Dual)]
#[autodiff_forward(d_square2, 4, Dualv, Dualv)]
#[autodiff_forward(d_square3, 4, Dual, Dual)]
fn square(x: &[f32], y: &mut [f32]) {
Expand Down Expand Up @@ -79,25 +74,25 @@ fn main() {
let mut dy3_4 = std::hint::black_box(vec![0.0; 5]);

// scalar.
//d_square1(&x1, &z1, &mut y1, &mut dy1_1);
//d_square1(&x1, &z2, &mut y2, &mut dy1_2);
//d_square1(&x1, &z3, &mut y3, &mut dy1_3);
//d_square1(&x1, &z4, &mut y4, &mut dy1_4);
d_square1(&x1, &z1, &mut y1, &mut dy1_1);
d_square1(&x1, &z2, &mut y2, &mut dy1_2);
d_square1(&x1, &z3, &mut y3, &mut dy1_3);
d_square1(&x1, &z4, &mut y4, &mut dy1_4);

// assert y1 == y2 == y3 == y4
//for i in 0..5 {
// assert_eq!(y1[i], y2[i]);
// assert_eq!(y1[i], y3[i]);
// assert_eq!(y1[i], y4[i]);
//}
for i in 0..5 {
assert_eq!(y1[i], y2[i]);
assert_eq!(y1[i], y3[i]);
assert_eq!(y1[i], y4[i]);
}

// batch mode A)
d_square2(&x1, &z5, &mut y5, &mut dy2);

// assert y1 == y2 == y3 == y4 == y5
//for i in 0..5 {
// assert_eq!(y1[i], y5[i]);
//}
for i in 0..5 {
assert_eq!(y1[i], y5[i]);
}

// batch mode B)
d_square3(&x1, &z1, &z2, &z3, &z4, &mut y6, &mut dy3_1, &mut dy3_2, &mut dy3_3, &mut dy3_4);
Expand Down
4 changes: 2 additions & 2 deletions tests/codegen-llvm/autodiff/identical_fnc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ fn square2(x: &f64) -> f64 {
// CHECK-NOT:br
// CHECK-NOT:ret
// CHECK:; call identical_fnc::d_square
// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square17hcb5768e95528c35fE(double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1)
// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1)
// CHECK:; call identical_fnc::d_square
// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square17hcb5768e95528c35fE(double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2)
// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2)

fn main() {
let x = std::hint::black_box(3.0);
Expand Down
1 change: 1 addition & 0 deletions tests/run-make/duplicate-dependency/foo-v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub struct Foo;
1 change: 1 addition & 0 deletions tests/run-make/duplicate-dependency/foo-v2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub struct Foo;
15 changes: 15 additions & 0 deletions tests/run-make/duplicate-dependency/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
struct Bar;

impl From<Bar> for foo::Foo {
fn from(_: Bar) -> Self {
foo::Foo
}
}

fn main() {
// The user might wrongly expect this to work since From<Bar> for Foo
// implies Into<Foo> for Bar. What the user missed is that different
// versions of Foo exist in the dependency graph, and the impl is for the
// wrong version.
re_export_foo::into_foo(Bar);
}
24 changes: 24 additions & 0 deletions tests/run-make/duplicate-dependency/main.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0277]: the trait bound `re_export_foo::foo::Foo: From<Bar>` is not satisfied
--> main.rs:14:29
|
LL | re_export_foo::into_foo(Bar);
| ----------------------- ^^^ the trait `From<Bar>` is not implemented for `re_export_foo::foo::Foo`
| |
| required by a bound introduced by this call
|
help: item with same name found
--> $DIR/foo-v1.rs:1:1
|
LL | pub struct Foo;
| ^^^^^^^^^^^^^^
= note: perhaps two different versions of crate `foo` are being used?
= note: required for `Bar` to implement `Into<re_export_foo::foo::Foo>`
note: required by a bound in `into_foo`
--> $DIR/re-export-foo.rs:3:25
|
LL | pub fn into_foo(_: impl Into<foo::Foo>) {}
| ^^^^^^^^^^^^^^ required by this bound in `into_foo`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
3 changes: 3 additions & 0 deletions tests/run-make/duplicate-dependency/re-export-foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub use foo;

pub fn into_foo(_: impl Into<foo::Foo>) {}
45 changes: 45 additions & 0 deletions tests/run-make/duplicate-dependency/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//@ needs-target-std

use run_make_support::{Rustc, cwd, diff, rust_lib_name, rustc};

fn rustc_with_common_args() -> Rustc {
let mut rustc = rustc();
rustc.remap_path_prefix(cwd(), "$DIR");
rustc.edition("2018"); // Don't require `extern crate`
rustc
}

fn main() {
rustc_with_common_args()
.input("foo-v1.rs")
.crate_type("rlib")
.crate_name("foo")
.extra_filename("-v1")
.metadata("-v1")
.run();

rustc_with_common_args()
.input("foo-v2.rs")
.crate_type("rlib")
.crate_name("foo")
.extra_filename("-v2")
.metadata("-v2")
.run();

rustc_with_common_args()
.input("re-export-foo.rs")
.crate_type("rlib")
.extern_("foo", rust_lib_name("foo-v2"))
.run();

let stderr = rustc_with_common_args()
.input("main.rs")
.extern_("foo", rust_lib_name("foo-v1"))
.extern_("re_export_foo", rust_lib_name("re_export_foo"))
.library_search_path(cwd())
.ui_testing()
.run_fail()
.stderr_utf8();

diff().expected_file("main.stderr").actual_text("(rustc)", &stderr).run();
}
10 changes: 10 additions & 0 deletions tests/ui/attributes/link-dl.allowed.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Future incompatibility report: Future breakage diagnostic:
warning: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", wasm_import_module = "...")]`
--> $DIR/link-dl.rs:14:1
|
LL | #[link="dl"]
| ^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>

23 changes: 23 additions & 0 deletions tests/ui/attributes/link-dl.default_fcw.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", wasm_import_module = "...")]`
--> $DIR/link-dl.rs:14:1
|
LL | #[link="dl"]
| ^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default

error: aborting due to 1 previous error

Future incompatibility report: Future breakage diagnostic:
error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", wasm_import_module = "...")]`
--> $DIR/link-dl.rs:14:1
|
LL | #[link="dl"]
| ^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default

Loading
Loading