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:
commit
934624fe5f
22 changed files with 342 additions and 96 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue