Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::SubregionOrigin;
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::canonical::{QueryRegionConstraint, QueryRegionConstraints};
use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
Expand Down Expand Up @@ -74,9 +74,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
let assumptions =
elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());

for &(constraint, constraint_category, _) in constraints {
for &QueryRegionConstraint { constraint, category, .. } in constraints {
constraint.iter_outlives().for_each(|predicate| {
self.convert(predicate, constraint_category, &assumptions);
self.convert(predicate, category, &assumptions);
});
}
}
Expand Down Expand Up @@ -296,7 +296,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
// FIXME(higher_ranked_auto): What should we do with the assumptions here?
if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints {
next_outlives_predicates.extend(constraints.iter().flat_map(
|(constraint, category, _)| {
|QueryRegionConstraint { constraint, category, .. }| {
constraint.iter_outlives().map(|outlives| (outlives, *category))
},
));
Expand Down
19 changes: 18 additions & 1 deletion compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4796,7 +4796,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let hir::Node::Expr(rcvr) = self.tcx.hir_node(hir_id) else {
return false;
};
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, rcvr_ty.into_iter());
// The trait may have generic parameters beyond `Self` (e.g. `Borrow<Borrowed>`), and
// `rcvr_ty` may even be unknown. We only ever know the receiver type (the `Self` arg),
// so fill `Self` from `rcvr_ty` when available and the remaining parameters with fresh
// inference variables; building a `TraitRef` with a partial arg list would otherwise trip
// `debug_assert_args_compatible` and ICE. See #157189.
let trait_ref = ty::TraitRef::new_from_args(
self.tcx,
trait_def_id,
ty::GenericArgs::for_item(self.tcx, trait_def_id, |param, _| {
if param.index == 0
&& let Some(rcvr_ty) = rcvr_ty
{
rcvr_ty.into()
} else {
self.var_for_def(rcvr.span, param)
}
}),
);
let trait_pred = ty::Binder::dummy(ty::TraitPredicate {
trait_ref,
polarity: ty::PredicatePolarity::Positive,
Expand Down
43 changes: 27 additions & 16 deletions compiler/rustc_infer/src/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::iter;
use rustc_index::{Idx, IndexVec};
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::bug;
use rustc_middle::infer::canonical::CanonicalVarKind;
use rustc_middle::infer::canonical::{CanonicalVarKind, QueryRegionConstraint};
use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable};
use tracing::{debug, instrument};

Expand Down Expand Up @@ -188,7 +188,9 @@ impl<'tcx> InferCtxt<'tcx> {
let InferOk { value: result_args, obligations } =
self.query_response_instantiation(cause, param_env, original_values, query_response)?;

for (constraint, _category, vis) in &query_response.value.region_constraints.constraints {
for QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in
&query_response.value.region_constraints.constraints
{
let constraint = instantiate_value(self.tcx, &result_args, *constraint);
match constraint {
ty::RegionConstraint::Outlives(predicate) => {
Expand Down Expand Up @@ -285,11 +287,12 @@ impl<'tcx> InferCtxt<'tcx> {

(GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => {
if v_o != v_r {
output_query_region_constraints.constraints.push((
ty::RegionEqPredicate(v_o, v_r).into(),
constraint_category,
ty::VisibleForLeakCheck::Yes,
));
let constraint = QueryRegionConstraint {
constraint: ty::RegionEqPredicate(v_o, v_r).into(),
category: constraint_category,
visible_for_leak_check: ty::VisibleForLeakCheck::Yes,
};
output_query_region_constraints.constraints.push(constraint);
}
}

Expand Down Expand Up @@ -321,7 +324,7 @@ impl<'tcx> InferCtxt<'tcx> {
let r_c = instantiate_value(self.tcx, &result_args, r_c);

// Screen out `'a: 'a` or `'a == 'a` cases.
if r_c.0.is_trivial() { None } else { Some(r_c) }
if r_c.constraint.is_trivial() { None } else { Some(r_c) }
}),
);

Expand Down Expand Up @@ -616,7 +619,7 @@ pub fn make_query_region_constraints<'tcx>(

debug!(?constraints);

let constraints: Vec<_> = constraints
let constraints: Vec<QueryRegionConstraint<'tcx>> = constraints
.iter()
.map(|(c, origin)| match c.kind {
ConstraintKind::VarSubVar
Expand All @@ -625,22 +628,30 @@ pub fn make_query_region_constraints<'tcx>(
| ConstraintKind::RegSubReg => {
// Swap regions because we are going from sub (<=) to outlives (>=).
let constraint = ty::OutlivesPredicate(c.sup.into(), c.sub).into();
(constraint, origin.to_constraint_category(), c.visible_for_leak_check)
QueryRegionConstraint {
constraint,
category: origin.to_constraint_category(),
visible_for_leak_check: c.visible_for_leak_check,
}
}

ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
let constraint = ty::RegionEqPredicate(c.sup, c.sub).into();
(constraint, origin.to_constraint_category(), c.visible_for_leak_check)
QueryRegionConstraint {
constraint,
category: origin.to_constraint_category(),
visible_for_leak_check: c.visible_for_leak_check,
}
}
})
.chain(outlives_obligations.into_iter().map(
|TypeOutlivesConstraint { sub_region, sup_type, origin }| {
(
ty::OutlivesPredicate(sup_type.into(), sub_region).into(),
origin.to_constraint_category(),
QueryRegionConstraint {
constraint: ty::OutlivesPredicate(sup_type.into(), sub_region).into(),
category: origin.to_constraint_category(),
// We don't do leak checks for type outlives
ty::VisibleForLeakCheck::Unreachable,
)
visible_for_leak_check: ty::VisibleForLeakCheck::Unreachable,
}
},
))
.collect();
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_middle/src/infer/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,12 @@ impl<'tcx, R> QueryResponse<'tcx, R> {
}
}

// FIXME: Convert this into a struct
pub type QueryRegionConstraint<'tcx> =
(ty::RegionConstraint<'tcx>, ConstraintCategory<'tcx>, ty::VisibleForLeakCheck);
#[derive(Debug, StableHash, Hash, Eq, PartialEq, TypeVisitable, Clone, TypeFoldable, Copy)]
pub struct QueryRegionConstraint<'tcx> {
pub constraint: ty::RegionConstraint<'tcx>,
pub category: ConstraintCategory<'tcx>,
pub visible_for_leak_check: ty::VisibleForLeakCheck,
}

#[derive(Default)]
pub struct CanonicalParamEnvCache<'tcx> {
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_trait_selection/src/solve/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::{
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues,
QueryRegionConstraint,
};
use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
use rustc_infer::traits::solve::{FetchEligibleAssocItemResponse, Goal};
Expand Down Expand Up @@ -262,7 +263,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<

let mut seen = FxHashMap::default();
let mut constraints = vec![];
for (outlives, _, vis) in region_constraints.constraints {
for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in
region_constraints.constraints
{
match seen.entry(outlives) {
Entry::Occupied(occupied) => {
let idx = occupied.get();
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_infer::infer::InferOk;
use rustc_infer::infer::canonical::QueryRegionConstraint;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
use rustc_macros::extension;
Expand Down Expand Up @@ -83,7 +84,7 @@ fn implied_outlives_bounds<'a, 'tcx>(
// outlives bound required proving some higher-ranked coroutine obl.
let QueryRegionConstraints { constraints, assumptions: _ } = constraints;
let cause = ObligationCause::misc(span, body_id);
for &(constraint, _, vis) in &constraints {
for &QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in &constraints {
match constraint {
ty::RegionConstraint::Outlives(predicate) => {
infcx.register_outlives_constraint(predicate, vis, &cause)
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_traits/src/coroutine_witnesses.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::infer::canonical::QueryRegionConstraint;
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
use rustc_infer::traits::{Obligation, ObligationCause};
Expand Down Expand Up @@ -80,7 +81,7 @@ fn compute_assumptions<'tcx>(
tcx.mk_outlives_from_iter(
constraints
.into_iter()
.flat_map(|(constraint, _, _)| constraint.iter_outlives())
.flat_map(|QueryRegionConstraint { constraint, .. }| constraint.iter_outlives())
// FIXME(higher_ranked_auto): We probably should deeply resolve these before
// filtering out infers which only correspond to unconstrained infer regions
// which we can sometimes get.
Expand Down
5 changes: 5 additions & 0 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,11 @@ impl<'test> TestCx<'test> {
if self.config.mode == TestMode::CodegenUnits {
compiler.args(&["-Z", "human_readable_cgu_names"]);
}

if self.config.mode == TestMode::DebugInfo && cfg!(target_os = "windows") {
// Prevent debugger processes from creating new console windows.
compiler.args(&["-Z", r#"crate-attr=windows_subsystem="windows""#]);
}
}

if self.config.optimize_tests && compiler_kind == CompilerKind::Rustc {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Regression test for #157189.
//!
//! When a method call fails to resolve, the "trait which provides `<method>` is
//! implemented but not in scope" diagnostic probes all traits for a method of the
//! same name. Here `.borrow()` matches `std::borrow::Borrow::borrow`, and `Borrow`
//! has a generic parameter (`Borrowed`) besides `Self`. Building the trait
//! reference for the diagnostic used to pass only the receiver type as the single
//! argument, which mismatched the trait's generics and ICEd in
//! `debug_assert_args_compatible`. It should just report the error.

trait Foo {
extern "C" fn borrow(&self);
}

struct Bar;

fn main() {
let foo: Box<dyn Fn(bool) -> usize> = Box::new(Bar);
//~^ ERROR expected a `Fn(bool)` closure, found `Bar`
foo.borrow();
//~^ ERROR no method named `borrow` found
foo.take()
//~^ ERROR `Box<dyn Fn(bool) -> usize>` is not an iterator
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
error[E0599]: no method named `borrow` found for struct `Box<dyn Fn(bool) -> usize>` in the current scope
--> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:20:9
|
LL | foo.borrow();
| ^^^^^^
|
--> $SRC_DIR/core/src/borrow.rs:LL:COL
|
= note: the method is available for `Box<dyn Fn(bool) -> usize>` here
|
= help: items from traits can only be used if the trait is in scope
help: use parentheses to call this trait object
|
LL | foo(/* bool */).borrow();
| ++++++++++++
help: trait `Borrow` which provides `borrow` is implemented but not in scope; perhaps you want to import it
|
LL + use std::borrow::Borrow;
|
help: there is a method `borrow_mut` with a similar name
|
LL | foo.borrow_mut();
| ++++

error[E0277]: expected a `Fn(bool)` closure, found `Bar`
--> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:18:43
|
LL | let foo: Box<dyn Fn(bool) -> usize> = Box::new(Bar);
| ^^^^^^^^^^^^^ expected an `Fn(bool)` closure, found `Bar`
|
help: the trait `Fn(bool)` is not implemented for `Bar`
--> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:15:1
|
LL | struct Bar;
| ^^^^^^^^^^
= note: required for the cast from `Box<Bar>` to `Box<dyn Fn(bool) -> usize>`

error[E0599]: `Box<dyn Fn(bool) -> usize>` is not an iterator
--> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:22:9
|
LL | foo.take()
| ^^^^
| |
| this is an associated function, not a method
| `Box<dyn Fn(bool) -> usize>` is not an iterator
|
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
= note: the candidate is defined in an impl for the type `Box<T, A>`
= note: the following trait bounds were not satisfied:
`dyn Fn(bool) -> usize: Iterator`
which is required by `Box<dyn Fn(bool) -> usize>: Iterator`
`Box<dyn Fn(bool) -> usize>: Iterator`
which is required by `&mut Box<dyn Fn(bool) -> usize>: Iterator`
`dyn Fn(bool) -> usize: Iterator`
which is required by `&mut dyn Fn(bool) -> usize: Iterator`
help: use associated function syntax instead
|
LL - foo.take()
LL + Box::<dyn Fn(bool) -> usize>::take(foo)
|

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
Loading