1
Fork 0

Auto merge of #90945 - JohnTitor:rollup-wc35xss, r=JohnTitor

Rollup of 8 pull requests

Successful merges:

 - #86455 (check where-clause for explicit `Sized` before suggesting `?Sized`)
 - #90801 (Normalize both arguments of `equate_normalized_input_or_output`)
 - #90803 (Suggest `&str.chars()` on attempt to `&str.iter()`)
 - #90819 (Fixes incorrect handling of TraitRefs when emitting suggestions.)
 - #90910 (fix getting the discriminant of a zero-variant enum)
 - #90925 (rustc_mir_build: reorder bindings)
 - #90928 (Use a different server for checking clock drift)
 - #90936 (Add a regression test for #80772)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2021-11-16 08:22:55 +00:00
commit 934624fe5f
22 changed files with 342 additions and 96 deletions

View file

@ -7,13 +7,16 @@
//! `RETURN_PLACE` the MIR arguments) are always fully normalized (and
//! contain revealed `impl Trait` values).
use crate::type_check::constraint_conversion::ConstraintConversion;
use rustc_index::vec::Idx;
use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_middle::mir::*;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::Ty;
use rustc_span::Span;
use rustc_trait_selection::traits::query::normalize::AtExt;
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
use rustc_trait_selection::traits::query::Fallible;
use type_op::TypeOpOutput;
use crate::universal_regions::UniversalRegions;
@ -30,6 +33,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let (&normalized_output_ty, normalized_input_tys) =
normalized_inputs_and_output.split_last().unwrap();
debug!(?normalized_output_ty);
debug!(?normalized_input_tys);
let mir_def_id = body.source.def_id().expect_local();
// If the user explicitly annotated the input types, extract
@ -75,10 +81,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
.delay_span_bug(body.span, "found more normalized_input_ty than local_decls");
break;
}
// In MIR, argument N is stored in local N+1.
let local = Local::new(argument_index + 1);
let mir_input_ty = body.local_decls[local].ty;
let mir_input_span = body.local_decls[local].source_info.span;
self.equate_normalized_input_or_output(
normalized_input_ty,
@ -100,6 +108,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// If the user explicitly annotated the input types, enforce those.
let user_provided_input_ty =
self.normalize(user_provided_input_ty, Locations::All(mir_input_span));
self.equate_normalized_input_or_output(
user_provided_input_ty,
mir_input_ty,
@ -167,30 +176,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// `rustc_traits::normalize_after_erasing_regions`. Ideally, we'd
// like to normalize *before* inserting into `local_decls`, but
// doing so ends up causing some other trouble.
let b = match self
.infcx
.at(&ObligationCause::dummy(), ty::ParamEnv::empty())
.normalize(b)
{
Ok(n) => {
debug!("equate_inputs_and_outputs: {:?}", n);
if n.obligations.iter().all(|o| {
matches!(
o.predicate.kind().skip_binder(),
ty::PredicateKind::RegionOutlives(_)
| ty::PredicateKind::TypeOutlives(_)
)
}) {
n.value
} else {
b
}
}
let b = match self.normalize_and_add_constraints(b) {
Ok(n) => n,
Err(_) => {
debug!("equate_inputs_and_outputs: NoSolution");
b
}
};
// Note: if we have to introduce new placeholders during normalization above, then we won't have
// added those universes to the universe info, which we would want in `relate_tys`.
if let Err(terr) =
@ -207,4 +200,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
}
pub(crate) fn normalize_and_add_constraints(&mut self, t: Ty<'tcx>) -> Fallible<Ty<'tcx>> {
let TypeOpOutput { output: norm_ty, constraints, .. } =
self.param_env.and(type_op::normalize::Normalize::new(t)).fully_perform(self.infcx)?;
debug!("{:?} normalized to {:?}", t, norm_ty);
for data in constraints.into_iter().collect::<Vec<_>>() {
ConstraintConversion::new(
self.infcx,
&self.borrowck_context.universal_regions,
&self.region_bound_pairs,
Some(self.implicit_region_bound),
self.param_env,
Locations::All(DUMMY_SP),
ConstraintCategory::Internal,
&mut self.borrowck_context.constraints,
)
.convert_all(&*data);
}
Ok(norm_ty)
}
}

View file

@ -893,11 +893,11 @@ struct TypeChecker<'a, 'tcx> {
}
struct BorrowCheckContext<'a, 'tcx> {
universal_regions: &'a UniversalRegions<'tcx>,
pub(crate) universal_regions: &'a UniversalRegions<'tcx>,
location_table: &'a LocationTable,
all_facts: &'a mut Option<AllFacts>,
borrow_set: &'a BorrowSet<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
pub(crate) constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
upvars: &'a [Upvar<'tcx>],
}
@ -1157,6 +1157,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.relate_types(sup, ty::Variance::Contravariant, sub, locations, category)
}
#[instrument(skip(self, category), level = "debug")]
fn eq_types(
&mut self,
expected: Ty<'tcx>,

View file

@ -647,6 +647,22 @@ pub struct WhereBoundPredicate<'hir> {
pub bounds: GenericBounds<'hir>,
}
impl WhereBoundPredicate<'hir> {
/// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate.
pub fn is_param_bound(&self, param_def_id: DefId) -> bool {
let path = match self.bounded_ty.kind {
TyKind::Path(QPath::Resolved(None, path)) => path,
_ => return false,
};
match path.res {
Res::Def(DefKind::TyParam, def_id) | Res::SelfTy(Some(def_id), None) => {
def_id == param_def_id
}
_ => false,
}
}
}
/// A lifetime predicate (e.g., `'a: 'b + 'c`).
#[derive(Debug, HashStable_Generic)]
pub struct WhereRegionPredicate<'hir> {

View file

@ -2067,7 +2067,9 @@ impl<'tcx> TyS<'tcx> {
) -> Option<Discr<'tcx>> {
match self.kind() {
TyKind::Adt(adt, _) if adt.variants.is_empty() => {
bug!("discriminant_for_variant called on zero variant enum");
// This can actually happen during CTFE, see
// https://github.com/rust-lang/rust/issues/89765.
None
}
TyKind::Adt(adt, _) if adt.is_enum() => {
Some(adt.discriminant_for_variant(tcx, variant_index))

View file

@ -1762,8 +1762,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) -> BlockAnd<()> {
let expr_span = expr.span;
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr, expr_span));
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false);
let wildcard = Pat::wildcard_from_ty(pat.ty);
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false);
let mut otherwise_candidate = Candidate::new(expr_place_builder.clone(), &wildcard, false);
let fake_borrow_temps = self.lower_match_tree(
block,

View file

@ -2009,6 +2009,19 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
Some(param) => param,
_ => return,
};
let param_def_id = self.tcx.hir().local_def_id(param.hir_id).to_def_id();
let preds = generics.where_clause.predicates.iter();
let explicitly_sized = preds
.filter_map(|pred| match pred {
hir::WherePredicate::BoundPredicate(bp) => Some(bp),
_ => None,
})
.filter(|bp| bp.is_param_bound(param_def_id))
.flat_map(|bp| bp.bounds)
.any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
if explicitly_sized {
return;
}
debug!("maybe_suggest_unsized_generics: param={:?}", param);
match node {
hir::Node::Item(

View file

@ -706,36 +706,29 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
}
let param_env = obligation.param_env;
let trait_ref = poly_trait_ref.skip_binder();
let found_ty = trait_ref.self_ty();
let found_ty_str = found_ty.to_string();
let imm_borrowed_found_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, found_ty);
let imm_substs = self.tcx.mk_substs_trait(imm_borrowed_found_ty, &[]);
let mut_borrowed_found_ty = self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, found_ty);
let mut_substs = self.tcx.mk_substs_trait(mut_borrowed_found_ty, &[]);
// Try to apply the original trait binding obligation by borrowing.
let mut try_borrowing = |new_imm_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
new_mut_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
let mut try_borrowing = |old_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
blacklist: &[DefId]|
-> bool {
if blacklist.contains(&expected_trait_ref.def_id()) {
if blacklist.contains(&old_ref.def_id()) {
return false;
}
let imm_result = self.predicate_must_hold_modulo_regions(&Obligation::new(
ObligationCause::dummy(),
param_env,
new_imm_trait_ref.without_const().to_predicate(self.tcx),
));
let mut_result = self.predicate_must_hold_modulo_regions(&Obligation::new(
ObligationCause::dummy(),
param_env,
new_mut_trait_ref.without_const().to_predicate(self.tcx),
));
let orig_ty = old_ref.self_ty().skip_binder();
let mk_result = |new_ty| {
let new_ref = old_ref.rebind(ty::TraitRef::new(
old_ref.def_id(),
self.tcx.mk_substs_trait(new_ty, &old_ref.skip_binder().substs[1..]),
));
self.predicate_must_hold_modulo_regions(&Obligation::new(
ObligationCause::dummy(),
param_env,
new_ref.without_const().to_predicate(self.tcx),
))
};
let imm_result = mk_result(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, orig_ty));
let mut_result = mk_result(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, orig_ty));
if imm_result || mut_result {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
@ -747,8 +740,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let msg = format!(
"the trait bound `{}: {}` is not satisfied",
found_ty_str,
expected_trait_ref.print_only_trait_path(),
orig_ty.to_string(),
old_ref.print_only_trait_path(),
);
if has_custom_message {
err.note(&msg);
@ -764,7 +757,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
span,
&format!(
"expected an implementor of trait `{}`",
expected_trait_ref.print_only_trait_path(),
old_ref.print_only_trait_path(),
),
);
@ -807,21 +800,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
};
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &*code {
let expected_trait_ref = obligation.parent_trait_ref;
let new_imm_trait_ref = poly_trait_ref
.rebind(ty::TraitRef::new(obligation.parent_trait_ref.def_id(), imm_substs));
let new_mut_trait_ref = poly_trait_ref
.rebind(ty::TraitRef::new(obligation.parent_trait_ref.def_id(), mut_substs));
return try_borrowing(new_imm_trait_ref, new_mut_trait_ref, expected_trait_ref, &[]);
try_borrowing(obligation.parent_trait_ref, &[])
} else if let ObligationCauseCode::BindingObligation(_, _)
| ObligationCauseCode::ItemObligation(_) = &*code
{
return try_borrowing(
poly_trait_ref.rebind(ty::TraitRef::new(trait_ref.def_id, imm_substs)),
poly_trait_ref.rebind(ty::TraitRef::new(trait_ref.def_id, mut_substs)),
*poly_trait_ref,
&never_suggest_borrow[..],
);
try_borrowing(*poly_trait_ref, &never_suggest_borrow[..])
} else {
false
}

View file

@ -478,6 +478,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut label_span_not_found = || {
if unsatisfied_predicates.is_empty() {
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
let is_string_or_ref_str = match actual.kind() {
ty::Ref(_, ty, _) => {
ty.is_str()
|| matches!(
ty.kind(),
ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did)
)
}
ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did),
_ => false,
};
if is_string_or_ref_str && item_name.name == sym::iter {
err.span_suggestion_verbose(
item_name.span,
"because of the in-memory representation of `&str`, to obtain \
an `Iterator` over each of its codepoint use method `chars`",
String::from("chars"),
Applicability::MachineApplicable,
);
}
if let ty::Adt(adt, _) = rcvr_ty.kind() {
let mut inherent_impls_candidate = self
.tcx

View file

@ -28,7 +28,7 @@ use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::weak_lang_items;
@ -668,6 +668,7 @@ impl ItemCtxt<'tcx> {
})
.flat_map(|b| predicates_from_bound(self, ty, b));
let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id();
let from_where_clauses = ast_generics
.where_clause
.predicates
@ -677,7 +678,7 @@ impl ItemCtxt<'tcx> {
_ => None,
})
.flat_map(|bp| {
let bt = if is_param(self.tcx, bp.bounded_ty, param_id) {
let bt = if bp.is_param_bound(param_def_id) {
Some(ty)
} else if !only_self_bounds.0 {
Some(self.to_ty(bp.bounded_ty))
@ -714,23 +715,6 @@ impl ItemCtxt<'tcx> {
}
}
/// Tests whether this is the AST for a reference to the type
/// parameter with ID `param_id`. We use this so as to avoid running
/// `ast_ty_to_ty`, because we want to avoid triggering an all-out
/// conversion of the type to avoid inducing unnecessary cycles.
fn is_param(tcx: TyCtxt<'_>, ast_ty: &hir::Ty<'_>, param_id: hir::HirId) -> bool {
if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ast_ty.kind {
match path.res {
Res::SelfTy(Some(def_id), None) | Res::Def(DefKind::TyParam, def_id) => {
def_id == tcx.hir().local_def_id(param_id).to_def_id()
}
_ => false,
}
} else {
false
}
}
fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
let it = tcx.hir().item(item_id);
debug!("convert: item {} with id {}", it.ident, it.hir_id());

View file

@ -117,7 +117,7 @@ datecheck() {
echo -n " local time: "
date
echo -n " network time: "
curl -fs --head http://detectportal.firefox.com/success.txt | grep ^Date: \
curl -fs --head http://ci-caches.rust-lang.org | grep ^Date: \
| sed 's/Date: //g' || true
echo "== end clock drift check =="
}

View file

@ -0,0 +1,21 @@
// check-pass
trait SomeTrait {}
pub struct Exhibit {
constant: usize,
factory: fn(&usize) -> Box<dyn SomeTrait>,
}
pub const A_CONSTANT: &[Exhibit] = &[
Exhibit {
constant: 1,
factory: |_| unimplemented!(),
},
Exhibit {
constant: "Hello world".len(),
factory: |_| unimplemented!(),
},
];
fn main() {}

View file

@ -25,6 +25,13 @@ enum SingleVariant {
const TEST_V: Discriminant<SingleVariant> = discriminant(&SingleVariant::V);
pub const TEST_VOID: () = {
// This is UB, but CTFE does not check validity so it does not detect this.
// This is a regression test for https://github.com/rust-lang/rust/issues/89765.
unsafe { std::mem::discriminant(&*(&() as *const () as *const Void)); };
};
fn main() {
assert_eq!(TEST_A, TEST_A_OTHER);
assert_eq!(TEST_A, discriminant(black_box(&Test::A(17))));

View file

@ -1,4 +1,4 @@
error[E0277]: the trait bound `C: Copy` is not satisfied
error[E0277]: the trait bound `B<C>: Copy` is not satisfied
--> $DIR/deriving-copyclone.rs:31:13
|
LL | is_copy(B { a: 1, b: C });
@ -22,7 +22,7 @@ help: consider borrowing here
LL | is_copy(&B { a: 1, b: C });
| +
error[E0277]: the trait bound `C: Clone` is not satisfied
error[E0277]: the trait bound `B<C>: Clone` is not satisfied
--> $DIR/deriving-copyclone.rs:32:14
|
LL | is_clone(B { a: 1, b: C });
@ -46,7 +46,7 @@ help: consider borrowing here
LL | is_clone(&B { a: 1, b: C });
| +
error[E0277]: the trait bound `D: Copy` is not satisfied
error[E0277]: the trait bound `B<D>: Copy` is not satisfied
--> $DIR/deriving-copyclone.rs:35:13
|
LL | is_copy(B { a: 1, b: D });

View file

@ -0,0 +1,43 @@
// check-pass
#![feature(generic_associated_types)]
use std::marker::PhantomData;
trait Family: Sized {
type Item<'a>;
fn apply_all<F>(&self, f: F)
where
F: FamilyItemFn<Self> { }
}
struct Array<T>(PhantomData<T>);
impl<T: 'static> Family for Array<T> {
type Item<'a> = &'a T;
}
trait FamilyItemFn<T: Family> {
fn apply(&self, item: T::Item<'_>);
}
impl<T, F> FamilyItemFn<T> for F
where
T: Family,
for<'a> F: Fn(T::Item<'a>)
{
fn apply(&self, item: T::Item<'_>) {
(*self)(item);
}
}
fn process<T: 'static>(array: Array<T>) {
// Works
array.apply_all(|x: &T| { });
// ICE: NoSolution
array.apply_all(|x: <Array<T> as Family>::Item<'_>| { });
}
fn main() {}

View file

@ -0,0 +1,37 @@
//check-pass
#![feature(generic_associated_types)]
trait Yokeable<'a>: 'static {
type Output: 'a;
}
trait IsCovariant<'a> {}
struct Yoke<Y: for<'a> Yokeable<'a>> {
data: Y,
}
impl<Y: for<'a> Yokeable<'a>> Yoke<Y> {
fn project<Y2: for<'a> Yokeable<'a>>(&self, _f: for<'a> fn(<Y as Yokeable<'a>>::Output, &'a ())
-> <Y2 as Yokeable<'a>>::Output) -> Yoke<Y2> {
unimplemented!()
}
}
fn _upcast<Y>(x: Yoke<Y>) -> Yoke<Box<dyn IsCovariant<'static> + 'static>> where
Y: for<'a> Yokeable<'a>,
for<'a> <Y as Yokeable<'a>>::Output: IsCovariant<'a>
{
x.project(|data, _| {
Box::new(data)
})
}
impl<'a> Yokeable<'a> for Box<dyn IsCovariant<'static> + 'static> {
type Output = Box<dyn IsCovariant<'a> + 'a>;
}
fn main() {}

View file

@ -0,0 +1,7 @@
pub fn main() {
let _ = "foo".iter(); //~ ERROR no method named `iter` found for reference `&'static str` in the current scope
let _ = "foo".foo(); //~ ERROR no method named `foo` found for reference `&'static str` in the current scope
let _ = String::from("bar").iter(); //~ ERROR no method named `iter` found for struct `String` in the current scope
let _ = (&String::from("bar")).iter(); //~ ERROR no method named `iter` found for reference `&String` in the current scope
let _ = 0.iter(); //~ ERROR no method named `iter` found for type `{integer}` in the current scope
}

View file

@ -0,0 +1,48 @@
error[E0599]: no method named `iter` found for reference `&'static str` in the current scope
--> $DIR/suggest-using-chars.rs:2:19
|
LL | let _ = "foo".iter();
| ^^^^ method not found in `&'static str`
|
help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars`
|
LL | let _ = "foo".chars();
| ~~~~~
error[E0599]: no method named `foo` found for reference `&'static str` in the current scope
--> $DIR/suggest-using-chars.rs:3:19
|
LL | let _ = "foo".foo();
| ^^^ method not found in `&'static str`
error[E0599]: no method named `iter` found for struct `String` in the current scope
--> $DIR/suggest-using-chars.rs:4:33
|
LL | let _ = String::from("bar").iter();
| ^^^^ method not found in `String`
|
help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars`
|
LL | let _ = String::from("bar").chars();
| ~~~~~
error[E0599]: no method named `iter` found for reference `&String` in the current scope
--> $DIR/suggest-using-chars.rs:5:36
|
LL | let _ = (&String::from("bar")).iter();
| ^^^^ method not found in `&String`
|
help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars`
|
LL | let _ = (&String::from("bar")).chars();
| ~~~~~
error[E0599]: no method named `iter` found for type `{integer}` in the current scope
--> $DIR/suggest-using-chars.rs:6:15
|
LL | let _ = 0.iter();
| ^^^^ method not found in `{integer}`
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0599`.

View file

@ -0,0 +1,8 @@
// Regression test for #85945: Don't suggest `?Sized` bound if an explicit
// `Sized` bound is already in a `where` clause.
fn foo<T>(_: &T) where T: Sized {}
fn bar() { foo(""); }
//~^ERROR the size for values of type
pub fn main() {
}

View file

@ -0,0 +1,18 @@
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/issue-85945-check-where-clause-before-suggesting-unsized.rs:4:16
|
LL | fn bar() { foo(""); }
| --- ^^ doesn't have a size known at compile-time
| |
| required by a bound introduced by this call
|
= help: the trait `Sized` is not implemented for `str`
note: required by a bound in `foo`
--> $DIR/issue-85945-check-where-clause-before-suggesting-unsized.rs:3:8
|
LL | fn foo<T>(_: &T) where T: Sized {}
| ^ required by this bound in `foo`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View file

@ -65,7 +65,7 @@ LL | is_send(Box::new(TestType));
| |
| required by a bound introduced by this call
|
= note: the trait bound `dummy2::TestType: Send` is not satisfied
= note: the trait bound `Unique<dummy2::TestType>: Send` is not satisfied
= note: required because of the requirements on the impl of `Send` for `Unique<dummy2::TestType>`
= note: required because it appears within the type `Box<dummy2::TestType>`
note: required by a bound in `is_send`
@ -104,11 +104,11 @@ error[E0277]: `main::TestType` cannot be sent between threads safely
--> $DIR/negated-auto-traits-error.rs:66:13
|
LL | is_sync(Outer2(TestType));
| ------- ^^^^^^^^^^^^^^^^ expected an implementor of trait `Sync`
| ------- ^^^^^^^^^^^^^^^^ `main::TestType` cannot be sent between threads safely
| |
| required by a bound introduced by this call
|
= note: the trait bound `main::TestType: Sync` is not satisfied
= help: the trait `Send` is not implemented for `main::TestType`
note: required because of the requirements on the impl of `Sync` for `Outer2<main::TestType>`
--> $DIR/negated-auto-traits-error.rs:14:22
|
@ -119,12 +119,6 @@ note: required by a bound in `is_sync`
|
LL | fn is_sync<T: Sync>(_: T) {}
| ^^^^ required by this bound in `is_sync`
help: consider borrowing here
|
LL | is_sync(&Outer2(TestType));
| +
LL | is_sync(&mut Outer2(TestType));
| ++++
error: aborting due to 7 previous errors

View file

@ -0,0 +1,11 @@
// Do not suggest referencing the parameter to `check`
trait Marker<T> {}
impl<T> Marker<i32> for T {}
pub fn check<T: Marker<u32>>(_: T) {}
pub fn main() {
check::<()>(()); //~ ERROR [E0277]
}

View file

@ -0,0 +1,17 @@
error[E0277]: the trait bound `(): Marker<u32>` is not satisfied
--> $DIR/issue-90804-incorrect-reference-suggestion.rs:10:17
|
LL | check::<()>(());
| ----------- ^^ the trait `Marker<u32>` is not implemented for `()`
| |
| required by a bound introduced by this call
|
note: required by a bound in `check`
--> $DIR/issue-90804-incorrect-reference-suggestion.rs:7:17
|
LL | pub fn check<T: Marker<u32>>(_: T) {}
| ^^^^^^^^^^^ required by this bound in `check`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.