Skip to content
Open
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
4 changes: 4 additions & 0 deletions compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rustc_hir::def::{self, CtorKind, Namespace, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, HirId, LangItem};
use rustc_hir_analysis::autoderef::Autoderef;
use rustc_hir_analysis::hir_ty_lowering::IsMethodCall;
use rustc_infer::infer::BoundRegionConversionTime;
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode};
use rustc_middle::ty::adjustment::{
Expand Down Expand Up @@ -573,6 +574,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_argument_types(
call_expr.span,
call_expr,
IsMethodCall::No,
fn_sig.inputs(),
fn_sig.output(),
expected,
Expand Down Expand Up @@ -883,6 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_argument_types(
call_expr.span,
call_expr,
IsMethodCall::No,
fn_sig.inputs(),
fn_sig.output(),
expected,
Expand Down Expand Up @@ -962,6 +965,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_argument_types(
call_expr.span,
call_expr,
IsMethodCall::Yes,
&method.sig.inputs()[1..],
method.sig.output(),
expected,
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal};
use rustc_hir_analysis::NoVariantNamed;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _;
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer as _, IsMethodCall};
use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin};
use rustc_infer::traits::query::NoSolution;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
Expand Down Expand Up @@ -1494,6 +1494,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_argument_types(
segment.ident.span,
expr,
IsMethodCall::Yes,
&method.sig.inputs()[1..],
method.sig.output(),
expected,
Expand All @@ -1516,6 +1517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_argument_types(
segment.ident.span,
expr,
IsMethodCall::Yes,
&err_inputs,
err_output,
NoExpectation,
Expand Down
110 changes: 106 additions & 4 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath, is_range_literal};
use rustc_hir_analysis::check::potentially_plural_count;
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants};
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, IsMethodCall, PermitVariants};
use rustc_index::IndexVec;
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TypeTrace};
use rustc_middle::ty::adjustment::AllowTwoPhase;
Expand Down Expand Up @@ -195,6 +195,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
call_span: Span,
// Expression of the call site
call_expr: &'tcx hir::Expr<'tcx>,
is_method: IsMethodCall,
// Types (as defined in the *signature* of the target function)
formal_input_tys: &[Ty<'tcx>],
formal_output: Ty<'tcx>,
Expand All @@ -206,7 +207,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
c_variadic: bool,
// Whether the arguments have been bundled in a tuple (ex: closures)
tuple_arguments: TupleArgumentsFlag,
// The DefId for the function being called, for better error messages
// The DefId for the function being called, for callee bound checks and
// better error messages
fn_def_id: Option<DefId>,
) {
let tcx = self.tcx;
Expand Down Expand Up @@ -305,12 +307,104 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
.ok()
})
.unwrap_or_default();
.unwrap_or_default()
.and_then(|expected_input_tys: Vec<Ty<'_>>| {
// Check the well-formedness of expected input tys, as using ill-formed
// expectation may cause type inference errors.
let well_formed = self.probe(|_| {
let ocx = ObligationCtxt::new(self);
for &ty in &expected_input_tys {
ocx.register_obligation(traits::Obligation::new(
self.tcx,
self.misc(call_span),
self.param_env,
ty::ClauseKind::WellFormed(ty.into()),
));
}
ocx.try_evaluate_obligations().is_empty()
});
well_formed.then_some(expected_input_tys)
});

let mut err_code = E0061;

let mut expected_meets_callee_bounds = Vec::with_capacity(formal_input_tys.len());
expected_meets_callee_bounds.resize(formal_input_tys.len(), true);

// Check whether the expected inputs satisfy the callee's where bounds.
// This is skipped for the old solver: attempting trait solving there can
// trigger an overflow, which is a fatal error in the old solver but is
// treated as mere ambiguity by the next solver.
if self.next_trait_solver()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, checking function's bounds against the expected input tys regresses tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.rs in the old solver.

impl<'a> Stream for &'a mut Repeat {
type Item = &'a u64;
fn next(self) -> Option<Self::Item> {
Some(&self.0)
}
}
pub struct Map<S, F> {
stream: S,
func: F,
}
impl<'a, A, F, T> Stream for &'a mut Map<A, F>
where
&'a mut A: Stream,
F: FnMut(<&'a mut A as Stream>::Item) -> T,
{
type Item = T;
fn next(self) -> Option<T> {
match self.stream.next() {
Some(item) => Some((self.func)(item)),
None => None,
}
}
}
pub struct Filter<S, F> {
stream: S,
func: F,
}
impl<'a, A, F, T> Stream for &'a mut Filter<A, F>
where
for<'b> &'b mut A: Stream<Item = T>, // <---- BAD
F: FnMut(&T) -> bool,
{
type Item = <&'a mut A as Stream>::Item;
fn next(self) -> Option<Self::Item> {
while let Some(item) = self.stream.next() {
if (self.func)(&item) {
return Some(item);
}
}
None
}
}
pub trait StreamExt
where
for<'b> &'b mut Self: Stream,
{
fn mapx<F>(self, func: F) -> Map<Self, F>

Evaluating the bounds for<'b> &'b mut Self: Stream on method StreamExt::mapx causes overflow while trying to consider the candidate &'_ mut Map<Map<Map<...>, _>, _>, _> and this ends up with a fatal error 😢

&& let Some(expected_input_tys) = &expected_input_tys
&& let Some(fn_def_id) = fn_def_id
// We don't need to check bounds for closures as they are already in the current
// body's param env.
&& self.tcx.def_kind(fn_def_id) != DefKind::Closure
{
self.probe(|_| {
let new_args = self.fresh_args_for_item(call_span, fn_def_id);
let fn_sig = self.tcx.fn_sig(fn_def_id).instantiate(self.tcx, new_args);
let fn_sig = self.instantiate_binder_with_fresh_vars(
call_span,
BoundRegionConversionTime::FnCall,
fn_sig,
);
let ocx = ObligationCtxt::new(self);
let origin = self.misc(call_span);
let fn_sig = ocx.normalize(&origin, self.param_env, fn_sig);

let bound_input_tys = fn_sig.inputs();
let bound_input_tys = if is_method == IsMethodCall::Yes {
&bound_input_tys[1..]
} else {
&bound_input_tys[..]
};

let fn_bounds = self.tcx.predicates_of(fn_def_id).instantiate(self.tcx, new_args);
let fn_bounds = traits::predicates_for_generics(
|_, sp| self.misc(sp),
self.param_env,
fn_bounds,
);
ocx.register_obligations(fn_bounds);

// perf: We reuse this by cloning rather than repeating the above lines
let preds = ocx.into_pending_obligations();

assert_eq!(bound_input_tys.len(), formal_input_tys.len(),);
for idx in 0..formal_input_tys.len() {
let meets_bounds = self
.probe(|_| {
let ocx = ObligationCtxt::new(self);
ocx.register_obligations(preds.clone());
ocx.eq(
&origin,
self.param_env,
bound_input_tys[idx],
expected_input_tys[idx],
)?;
if ocx.try_evaluate_obligations().is_empty() {
Ok(())
} else {
Err(TypeError::Mismatch)
}
})
.is_ok();
expected_meets_callee_bounds[idx] = meets_bounds;
}
});
}

// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
// Since the expected inputs are unpacked from a single tuple,
// each input meets the callee bounds if and only if the tuple does.
expected_meets_callee_bounds.resize(
provided_args.len(),
expected_meets_callee_bounds.first().copied().unwrap_or(true),
);

let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]);
match tuple_type.kind() {
// We expected a tuple and got a tuple
Expand Down Expand Up @@ -373,7 +467,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We're on the happy path here, so we'll do a more involved check and write back types
// To check compatibility, we'll do 3 things:
// 1. Unify the provided argument with the expected type
let expectation = Expectation::rvalue_hint(self, expected_input_ty);
let expectation = if expected_meets_callee_bounds[idx] {
Expectation::rvalue_hint(self, expected_input_ty)
} else {
// If the expected input does not satisfy the callee's bounds, we must
// weaken the expectation; otherwise, coercing to a type that violates
// those bounds would result in a type mismatch.
// See https://github.com/rust-lang/rust/issues/149379.
Expectation::ExpectRvalueLikeUnsized(expected_input_ty)
};

let checked_ty = self.check_expr_with_expectation(provided_arg, expectation);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time
--> $DIR/expectated-input-not-satisfying-fn-bounds-issue-149881.rs:16:24
|
LL | <[_]>::into_vec(id(Box::new([0, 1, 2])));
| -- ^^^^^^^^^^^^^^^^^^^ 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 `[{integer}]`
note: required by an implicit `Sized` bound in `id`
--> $DIR/expectated-input-not-satisfying-fn-bounds-issue-149881.rs:11:7
|
LL | fn id<T>(x: Box<T>) -> Box<T> {
| ^ required by the implicit `Sized` requirement on this type parameter in `id`
help: consider relaxing the implicit `Sized` restriction
|
LL | fn id<T: ?Sized>(x: Box<T>) -> Box<T> {
| ++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@[next] check-pass

// FIXME(#149379): This should pass, but fails due to fudged expactation
// types which are potentially not well-formed or for whom the function
// where-bounds don't actually hold. And this results in weird bugs when
// later treating these expectations as if they were actually correct..

fn id<T>(x: Box<T>) -> Box<T> {
x
}

fn main() {
<[_]>::into_vec(id(Box::new([0, 1, 2])));
//[current]~^ ERROR: the size for values of type `[{integer}]` cannot be known at compilation time
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0277]: `dyn Trait + Send` cannot be unpinned
--> $DIR/expectated-input-not-satisfying-fn-bounds-issue-89299.rs:21:27
|
LL | let _x = Foo(Pin::new(&mut a));
| -------- ^^^^^^ the trait `Unpin` is not implemented for `dyn Trait + Send`
| |
| required by a bound introduced by this call
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
note: required by a bound in `Pin::<Ptr>::new`
--> $SRC_DIR/core/src/pin.rs:LL:COL

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@[next] check-pass

// FIXME(#149379): This should pass, but fails due to fudged expactation
// types which are potentially not well-formed or for whom the function
// where-bounds don't actually hold. And this results in weird bugs when
// later treating these expectations as if they were actually correct..

use std::pin::Pin;

trait Trait {}

impl Trait for i32 {}

struct Foo<'a>(Pin<&'a mut (dyn Trait + Send)>);

fn main() {
let mut a = 1;
let _x = Foo(Pin::new(&mut a));
//[current]~^ ERROR: `dyn Trait + Send` cannot be unpinned
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ check-pass

// FIXME(#149379): This should pass, but fails due to fudged expactation
// types which are potentially not well-formed or for whom the function
// where-bounds don't actually hold. And this results in weird bugs when
// later treating these expectations as if they were actually correct..
// A regression test for https://github.com/rust-lang/rust/issues/149379.

fn foo<T>(x: (T, ())) -> Box<T> {
Box::new(x.0)
Expand All @@ -14,6 +12,4 @@ fn foo<T>(x: (T, ())) -> Box<T> {
fn main() {
// Uses expectation as its struct tail is sized, resulting in `(dyn Send, ())`
let _: Box<dyn Send> = foo(((), ()));
//~^ ERROR mismatched types
//~| ERROR the size for values of type `dyn Send` cannot be known at compilation time
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0277]: the size for values of type `dyn Send` cannot be known at compilation time
--> $DIR/fn-ret-trait-object-propagated-to-inputs-issue-149379-2.rs:15:38
--> $DIR/fn-ret-trait-object-propagated-to-inputs-issue-149379-2.rs:16:38
|
LL | let _: Box<dyn Send> = sized_box(Box::new(1));
| --------- ^^^^^^^^^^^ doesn't have a size known at compile-time
Expand All @@ -8,7 +8,7 @@ LL | let _: Box<dyn Send> = sized_box(Box::new(1));
|
= help: the trait `Sized` is not implemented for `dyn Send`
note: required by an implicit `Sized` bound in `sized_box`
--> $DIR/fn-ret-trait-object-propagated-to-inputs-issue-149379-2.rs:10:14
--> $DIR/fn-ret-trait-object-propagated-to-inputs-issue-149379-2.rs:11:14
|
LL | fn sized_box<T>(x: Box<T>) -> Box<T> {
| ^ required by the implicit `Sized` requirement on this type parameter in `sized_box`
Expand Down
Loading
Loading