Skip to content

Commit

Permalink
Auto merge of #131275 - workingjubilee:rollup-4yxqio3, r=workingjubilee
Browse files Browse the repository at this point in the history
Rollup of 9 pull requests

Successful merges:

 - #129517 (Compute array length from type for unconditional panic lint. )
 - #130367 (Check elaborated projections from dyn don't mention unconstrained late bound lifetimes)
 - #130403 (Stabilize `const_slice_from_raw_parts_mut`)
 - #130633 (Add support for reborrowing pinned method receivers)
 - #131105 (update `Literal`'s intro)
 - #131194 (Fix needless_lifetimes in stable_mir)
 - #131260 (rustdoc: cleaner errors on disambiguator/namespace mismatches)
 - #131267 (Stabilize `BufRead::skip_until`)
 - #131273 (Account for `impl Trait {` when `impl Trait for Type {` was intended)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Oct 5, 2024
2 parents 495f75a + 08689af commit d30c392
Show file tree
Hide file tree
Showing 43 changed files with 559 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rustc_middle::ty::{
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
use rustc_type_ir::elaborate::ClauseWithSupertraitSpan;
use smallvec::{SmallVec, smallvec};
use tracing::{debug, instrument};

Expand Down Expand Up @@ -124,16 +125,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.into_iter()
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));

for (base_trait_ref, span) in regular_traits_refs_spans {
for (base_trait_ref, original_span) in regular_traits_refs_spans {
let base_pred: ty::Predicate<'tcx> = base_trait_ref.upcast(tcx);
for pred in traits::elaborate(tcx, [base_pred]).filter_only_self() {
for ClauseWithSupertraitSpan { pred, original_span, supertrait_span } in
traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(base_pred, original_span)])
.filter_only_self()
{
debug!("observing object predicate `{pred:?}`");

let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
let pred = bound_predicate.rebind(pred);
associated_types.entry(span).or_default().extend(
associated_types.entry(original_span).or_default().extend(
tcx.associated_items(pred.def_id())
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Type)
Expand Down Expand Up @@ -172,8 +176,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// the discussion in #56288 for alternatives.
if !references_self {
// Include projections defined on supertraits.
projection_bounds.push((pred, span));
projection_bounds.push((pred, original_span));
}

self.check_elaborated_projection_mentions_input_lifetimes(
pred,
original_span,
supertrait_span,
);
}
_ => (),
}
Expand Down Expand Up @@ -360,6 +370,56 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {

Ty::new_dynamic(tcx, existential_predicates, region_bound, representation)
}

/// Check that elaborating the principal of a trait ref doesn't lead to projections
/// that are unconstrained. This can happen because an otherwise unconstrained
/// *type variable* can be substituted with a type that has late-bound regions. See
/// `elaborated-predicates-unconstrained-late-bound.rs` for a test.
fn check_elaborated_projection_mentions_input_lifetimes(
&self,
pred: ty::PolyProjectionPredicate<'tcx>,
span: Span,
supertrait_span: Span,
) {
let tcx = self.tcx();

// Find any late-bound regions declared in `ty` that are not
// declared in the trait-ref or assoc_item. These are not well-formed.
//
// Example:
//
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
let late_bound_in_projection_term =
tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term));
let late_bound_in_term =
tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term));
debug!(?late_bound_in_projection_term);
debug!(?late_bound_in_term);

// FIXME: point at the type params that don't have appropriate lifetimes:
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
// ---- ---- ^^^^^^^
// NOTE(associated_const_equality): This error should be impossible to trigger
// with associated const equality constraints.
self.validate_late_bound_regions(
late_bound_in_projection_term,
late_bound_in_term,
|br_name| {
let item_name = tcx.item_name(pred.projection_def_id());
struct_span_code_err!(
self.dcx(),
span,
E0582,
"binding for associated type `{}` references {}, \
which does not appear in the trait input types",
item_name,
br_name
)
.with_span_label(supertrait_span, "due to this supertrait")
},
);
}
}

fn replace_dummy_self_with_error<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
Expand Down
17 changes: 10 additions & 7 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let tcx = self.tcx();
let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
if let hir::Node::Item(hir::Item {
kind:
hir::ItemKind::Impl(hir::Impl {
self_ty: impl_self_ty,
of_trait: Some(of_trait_ref),
generics,
..
}),
kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }),
..
}) = tcx.hir_node_by_def_id(parent_id)
&& self_ty.hir_id == impl_self_ty.hir_id
{
let Some(of_trait_ref) = of_trait else {
diag.span_suggestion_verbose(
impl_self_ty.span.shrink_to_hi(),
"you might have intended to implement this trait for a given type",
format!(" for /* Type */"),
Applicability::HasPlaceholders,
);
return;
};
if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) {
return;
}
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,23 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
target,
});
}

Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => {
let region = self.next_region_var(infer::Autoref(self.span));

target = match target.kind() {
ty::Adt(pin, args) if self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => {
let inner_ty = match args[0].expect_ty().kind() {
ty::Ref(_, ty, _) => *ty,
_ => bug!("Expected a reference type for argument to Pin"),
};
Ty::new_pinned_ref(self.tcx, region, inner_ty, mutbl)
}
_ => bug!("Cannot adjust receiver type for reborrowing pin of {target:?}"),
};

adjustments.push(Adjustment { kind: Adjust::ReborrowPin(region, mutbl), target });
}
None => {}
}

Expand Down
32 changes: 26 additions & 6 deletions compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
mutbl.ref_prefix_str()
}
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl {
hir::Mutability::Mut => "Pin<&mut ",
hir::Mutability::Not => "Pin<&",
},
};
if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
{
let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
let mut self_adjusted =
if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
pick.autoref_or_ptr_adjustment
{
format!("{derefs}{self_expr} as *const _")
} else {
format!("{autoref}{derefs}{self_expr}")
};

if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) =
pick.autoref_or_ptr_adjustment
{
format!("{derefs}{self_expr} as *const _")
} else {
format!("{autoref}{derefs}{self_expr}")
};
self_adjusted.push('>');
}

lint.span_suggestion(
sp,
Expand Down Expand Up @@ -400,6 +411,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let autoref = match pick.autoref_or_ptr_adjustment {
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(),
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl {
hir::Mutability::Mut => "Pin<&mut ",
hir::Mutability::Not => "Pin<&",
},
};

let (expr_text, precise) = if let Some(expr_text) = expr
Expand All @@ -412,14 +427,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
("(..)".to_string(), false)
};

let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
let mut adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
pick.autoref_or_ptr_adjustment
{
format!("{derefs}{expr_text} as *const _")
} else {
format!("{autoref}{derefs}{expr_text}")
};

if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) = pick.autoref_or_ptr_adjustment
{
adjusted_text.push('>');
}

(adjusted_text, precise)
}
}
79 changes: 71 additions & 8 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ enum ProbeResult {
/// `mut`), or it has type `*mut T` and we convert it to `*const T`.
#[derive(Debug, PartialEq, Copy, Clone)]
pub(crate) enum AutorefOrPtrAdjustment {
/// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it.
/// Receiver has type `T`, add `&` or `&mut` (if `T` is `mut`), and maybe also "unsize" it.
/// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing.
Autoref {
mutbl: hir::Mutability,
Expand All @@ -147,13 +147,17 @@ pub(crate) enum AutorefOrPtrAdjustment {
},
/// Receiver has type `*mut T`, convert to `*const T`
ToConstPtr,

/// Reborrow a `Pin<&mut T>` or `Pin<&T>`.
ReborrowPin(hir::Mutability),
}

impl AutorefOrPtrAdjustment {
fn get_unsize(&self) -> bool {
match self {
AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize,
AutorefOrPtrAdjustment::ToConstPtr => false,
AutorefOrPtrAdjustment::ReborrowPin(_) => false,
}
}
}
Expand Down Expand Up @@ -1103,6 +1107,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
unstable_candidates.as_deref_mut(),
)
})
.or_else(|| {
self.pick_reborrow_pin_method(
step,
self_ty,
unstable_candidates.as_deref_mut(),
)
})
})
})
}
Expand All @@ -1127,13 +1138,28 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;

// Insert a `&*` or `&mut *` if this is a reference type:
if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() {
pick.autoderefs += 1;
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
mutbl,
unsize: pick.autoref_or_ptr_adjustment.is_some_and(|a| a.get_unsize()),
})
match *step.self_ty.value.value.kind() {
// Insert a `&*` or `&mut *` if this is a reference type:
ty::Ref(_, _, mutbl) => {
pick.autoderefs += 1;
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
mutbl,
unsize: pick.autoref_or_ptr_adjustment.is_some_and(|a| a.get_unsize()),
})
}

ty::Adt(def, args)
if self.tcx.features().pin_ergonomics
&& self.tcx.is_lang_item(def.did(), hir::LangItem::Pin) =>
{
// make sure this is a pinned reference (and not a `Pin<Box>` or something)
if let ty::Ref(_, _, mutbl) = args[0].expect_ty().kind() {
pick.autoref_or_ptr_adjustment =
Some(AutorefOrPtrAdjustment::ReborrowPin(*mutbl));
}
}

_ => (),
}

pick
Expand Down Expand Up @@ -1164,6 +1190,43 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
})
}

/// Looks for applicable methods if we reborrow a `Pin<&mut T>` as a `Pin<&T>`.
#[instrument(level = "debug", skip(self, step, unstable_candidates))]
fn pick_reborrow_pin_method(
&self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
) -> Option<PickResult<'tcx>> {
if !self.tcx.features().pin_ergonomics {
return None;
}

// make sure self is a Pin<&mut T>
let inner_ty = match self_ty.kind() {
ty::Adt(def, args) if self.tcx.is_lang_item(def.did(), hir::LangItem::Pin) => {
match args[0].expect_ty().kind() {
ty::Ref(_, ty, hir::Mutability::Mut) => *ty,
_ => {
return None;
}
}
}
_ => return None,
};

let region = self.tcx.lifetimes.re_erased;
let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not);
self.pick_method(autopin_ty, unstable_candidates).map(|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
pick.autoref_or_ptr_adjustment =
Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not));
pick
})
})
}

/// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a
/// special case for this is because going from `*mut T` to `*const T` with autoderefs and
/// autorefs would require dereferencing the pointer, which is not safe.
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ pub struct Clause<'tcx>(
);

impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {
fn as_predicate(self) -> Predicate<'tcx> {
self.as_predicate()
}

fn instantiate_supertrait(self, tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> Self {
self.instantiate_supertrait(tcx, trait_ref)
}
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,16 @@ impl<'tcx> Ty<'tcx> {
Ty::new_ref(tcx, r, ty, hir::Mutability::Not)
}

pub fn new_pinned_ref(
tcx: TyCtxt<'tcx>,
r: Region<'tcx>,
ty: Ty<'tcx>,
mutbl: ty::Mutability,
) -> Ty<'tcx> {
let pin = tcx.adt_def(tcx.require_lang_item(LangItem::Pin, None));
Ty::new_adt(tcx, pin, tcx.mk_args(&[Ty::new_ref(tcx, r, ty, mutbl).into()]))
}

#[inline]
pub fn new_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mutbl: ty::Mutability) -> Ty<'tcx> {
Ty::new(tcx, ty::RawPtr(ty, mutbl))
Expand Down
16 changes: 9 additions & 7 deletions compiler/rustc_mir_transform/src/known_panics_lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,13 +600,15 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}

Len(place) => {
let len = match self.get_const(place)? {
Value::Immediate(src) => src.len(&self.ecx).discard_err()?,
Value::Aggregate { fields, .. } => fields.len() as u64,
Value::Uninit => match place.ty(self.local_decls(), self.tcx).ty.kind() {
ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?,
_ => return None,
},
let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind()
{
n.try_eval_target_usize(self.tcx, self.param_env)?
} else {
match self.get_const(place)? {
Value::Immediate(src) => src.len(&self.ecx).discard_err()?,
Value::Aggregate { fields, .. } => fields.len() as u64,
Value::Uninit => return None,
}
};
ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into()
}
Expand Down
Loading

0 comments on commit d30c392

Please sign in to comment.