Skip to content

Commit bcc851d

Browse files
committed
Fix typecks on some spurious fudged function input expectation
1 parent 0ac9e59 commit bcc851d

16 files changed

Lines changed: 195 additions & 263 deletions

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_hir::def::{self, CtorKind, Namespace, Res};
77
use rustc_hir::def_id::DefId;
88
use rustc_hir::{self as hir, HirId, LangItem};
99
use rustc_hir_analysis::autoderef::Autoderef;
10+
use rustc_hir_analysis::hir_ty_lowering::IsMethodCall;
1011
use rustc_infer::infer::BoundRegionConversionTime;
1112
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode};
1213
use rustc_middle::ty::adjustment::{
@@ -573,6 +574,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
573574
self.check_argument_types(
574575
call_expr.span,
575576
call_expr,
577+
IsMethodCall::No,
576578
fn_sig.inputs(),
577579
fn_sig.output(),
578580
expected,
@@ -883,6 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
883885
self.check_argument_types(
884886
call_expr.span,
885887
call_expr,
888+
IsMethodCall::No,
886889
fn_sig.inputs(),
887890
fn_sig.output(),
888891
expected,
@@ -962,6 +965,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
962965
self.check_argument_types(
963966
call_expr.span,
964967
call_expr,
968+
IsMethodCall::Yes,
965969
&method.sig.inputs()[1..],
966970
method.sig.output(),
967971
expected,

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_hir::def_id::DefId;
2121
use rustc_hir::lang_items::LangItem;
2222
use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal};
2323
use rustc_hir_analysis::NoVariantNamed;
24-
use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _};
24+
use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _, IsMethodCall};
2525
use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin};
2626
use rustc_infer::traits::query::NoSolution;
2727
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
@@ -1494,6 +1494,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14941494
self.check_argument_types(
14951495
segment.ident.span,
14961496
expr,
1497+
IsMethodCall::Yes,
14971498
&method.sig.inputs()[1..],
14981499
method.sig.output(),
14991500
expected,
@@ -1516,6 +1517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15161517
self.check_argument_types(
15171518
segment.ident.span,
15181519
expr,
1520+
IsMethodCall::Yes,
15191521
&err_inputs,
15201522
err_output,
15211523
NoExpectation,

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_hir::def_id::DefId;
1010
use rustc_hir::intravisit::Visitor;
1111
use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath, is_range_literal};
1212
use rustc_hir_analysis::check::potentially_plural_count;
13-
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants};
13+
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, IsMethodCall, PermitVariants};
1414
use rustc_index::IndexVec;
1515
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TypeTrace};
1616
use rustc_middle::ty::adjustment::AllowTwoPhase;
@@ -195,6 +195,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
195195
call_span: Span,
196196
// Expression of the call site
197197
call_expr: &'tcx hir::Expr<'tcx>,
198+
is_method: IsMethodCall,
198199
// Types (as defined in the *signature* of the target function)
199200
formal_input_tys: &[Ty<'tcx>],
200201
formal_output: Ty<'tcx>,
@@ -206,7 +207,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
206207
c_variadic: bool,
207208
// Whether the arguments have been bundled in a tuple (ex: closures)
208209
tuple_arguments: TupleArgumentsFlag,
209-
// The DefId for the function being called, for better error messages
210+
// The DefId for the function being called, for callee bound checks and
211+
// better error messages
210212
fn_def_id: Option<DefId>,
211213
) {
212214
let tcx = self.tcx;
@@ -290,6 +292,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
290292
formal_input_tys
291293
};
292294

295+
for &ty in formal_input_tys {
296+
ocx.register_obligation(traits::Obligation::new(
297+
self.tcx,
298+
self.misc(call_span),
299+
self.param_env,
300+
ty::ClauseKind::WellFormed(ty.into()),
301+
));
302+
}
303+
293304
if !ocx.try_evaluate_obligations().is_empty() {
294305
return Err(TypeError::Mismatch);
295306
}
@@ -309,8 +320,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
309320

310321
let mut err_code = E0061;
311322

323+
let mut expected_meets_callee_bounds = Vec::with_capacity(formal_input_tys.len());
324+
expected_meets_callee_bounds.resize(formal_input_tys.len(), true);
325+
326+
// Check whether the expected inputs satisfy the callee's where bounds.
327+
// This is skipped for the old solver: attempting trait solving there can
328+
// trigger an overflow, which is a fatal error in the old solver but is
329+
// treated as mere ambiguity by the next solver.
330+
if self.next_trait_solver()
331+
&& let Some(expected_input_tys) = &expected_input_tys
332+
&& let Some(fn_def_id) = fn_def_id
333+
// We don't need to check bounds for closures as they are already in the current
334+
// body's param env.
335+
&& self.tcx.def_kind(fn_def_id) != DefKind::Closure
336+
{
337+
self.probe(|_| {
338+
let new_args = self.fresh_args_for_item(call_span, fn_def_id);
339+
let fn_sig = self.tcx.fn_sig(fn_def_id).instantiate(self.tcx, new_args);
340+
let fn_sig = self.instantiate_binder_with_fresh_vars(
341+
call_span,
342+
BoundRegionConversionTime::FnCall,
343+
fn_sig,
344+
);
345+
let ocx = ObligationCtxt::new(self);
346+
let origin = self.misc(call_span);
347+
let fn_sig = ocx.normalize(&origin, self.param_env, fn_sig);
348+
349+
let bound_input_tys = fn_sig.inputs();
350+
let bound_input_tys = if is_method == IsMethodCall::Yes {
351+
&bound_input_tys[1..]
352+
} else {
353+
&bound_input_tys[..]
354+
};
355+
356+
let fn_bounds = self.tcx.predicates_of(fn_def_id).instantiate(self.tcx, new_args);
357+
let fn_bounds = traits::predicates_for_generics(
358+
|_, sp| self.misc(sp),
359+
self.param_env,
360+
fn_bounds,
361+
);
362+
ocx.register_obligations(fn_bounds);
363+
364+
// perf: We reuse this by cloning rather than repeating the above lines
365+
let preds = ocx.into_pending_obligations();
366+
367+
assert_eq!(bound_input_tys.len(), formal_input_tys.len(),);
368+
for idx in 0..formal_input_tys.len() {
369+
let meets_bounds = self
370+
.probe(|_| {
371+
let ocx = ObligationCtxt::new(self);
372+
ocx.register_obligations(preds.clone());
373+
ocx.eq(
374+
&origin,
375+
self.param_env,
376+
bound_input_tys[idx],
377+
expected_input_tys[idx],
378+
)?;
379+
if ocx.try_evaluate_obligations().is_empty() {
380+
Ok(())
381+
} else {
382+
Err(TypeError::Mismatch)
383+
}
384+
})
385+
.is_ok();
386+
expected_meets_callee_bounds[idx] = meets_bounds;
387+
}
388+
});
389+
}
390+
312391
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
313392
let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
393+
// Since the expected inputs are unpacked from a single tuple,
394+
// each input meets the callee bounds if and only if the tuple does.
395+
expected_meets_callee_bounds.resize(
396+
provided_args.len(),
397+
expected_meets_callee_bounds.first().copied().unwrap_or(true),
398+
);
399+
314400
let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]);
315401
match tuple_type.kind() {
316402
// We expected a tuple and got a tuple
@@ -373,7 +459,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
373459
// We're on the happy path here, so we'll do a more involved check and write back types
374460
// To check compatibility, we'll do 3 things:
375461
// 1. Unify the provided argument with the expected type
376-
let expectation = Expectation::rvalue_hint(self, expected_input_ty);
462+
let expectation = if expected_meets_callee_bounds[idx] {
463+
Expectation::rvalue_hint(self, expected_input_ty)
464+
} else {
465+
// If the expected input does not satisfy the callee's bounds, we must
466+
// weaken the expectation; otherwise, coercing to a type that violates
467+
// those bounds would result in a type mismatch.
468+
// See https://github.com/rust-lang/rust/issues/149379.
469+
Expectation::ExpectRvalueLikeUnsized(expected_input_ty)
470+
};
377471

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time
2+
--> $DIR/expectated-input-not-satisfying-fn-bounds-issue-149881.rs:16:24
3+
|
4+
LL | <[_]>::into_vec(id(Box::new([0, 1, 2])));
5+
| -- ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the trait `Sized` is not implemented for `[{integer}]`
10+
note: required by an implicit `Sized` bound in `id`
11+
--> $DIR/expectated-input-not-satisfying-fn-bounds-issue-149881.rs:11:7
12+
|
13+
LL | fn id<T>(x: Box<T>) -> Box<T> {
14+
| ^ required by the implicit `Sized` requirement on this type parameter in `id`
15+
help: consider relaxing the implicit `Sized` restriction
16+
|
17+
LL | fn id<T: ?Sized>(x: Box<T>) -> Box<T> {
18+
| ++++++++
19+
20+
error: aborting due to 1 previous error
21+
22+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
//@[next] check-pass
5+
6+
// FIXME(#149379): This should pass, but fails due to fudged expactation
7+
// types which are potentially not well-formed or for whom the function
8+
// where-bounds don't actually hold. And this results in weird bugs when
9+
// later treating these expectations as if they were actually correct..
10+
11+
fn id<T>(x: Box<T>) -> Box<T> {
12+
x
13+
}
14+
15+
fn main() {
16+
<[_]>::into_vec(id(Box::new([0, 1, 2])));
17+
//[current]~^ ERROR: the size for values of type `[{integer}]` cannot be known at compilation time
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0277]: `dyn Trait + Send` cannot be unpinned
2+
--> $DIR/expectated-input-not-satisfying-fn-bounds-issue-89299.rs:21:27
3+
|
4+
LL | let _x = Foo(Pin::new(&mut a));
5+
| -------- ^^^^^^ the trait `Unpin` is not implemented for `dyn Trait + Send`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= note: consider using the `pin!` macro
10+
consider using `Box::pin` if you need to access the pinned value outside of the current scope
11+
note: required by a bound in `Pin::<Ptr>::new`
12+
--> $SRC_DIR/core/src/pin.rs:LL:COL
13+
14+
error: aborting due to 1 previous error
15+
16+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
//@[next] check-pass
5+
6+
// FIXME(#149379): This should pass, but fails due to fudged expactation
7+
// types which are potentially not well-formed or for whom the function
8+
// where-bounds don't actually hold. And this results in weird bugs when
9+
// later treating these expectations as if they were actually correct..
10+
11+
use std::pin::Pin;
12+
13+
trait Trait {}
14+
15+
impl Trait for i32 {}
16+
17+
struct Foo<'a>(Pin<&'a mut (dyn Trait + Send)>);
18+
19+
fn main() {
20+
let mut a = 1;
21+
let _x = Foo(Pin::new(&mut a));
22+
//[current]~^ ERROR: `dyn Trait + Send` cannot be unpinned
23+
}

tests/ui/coercion/fudge-inference/fn-ret-trait-object-propagated-to-inputs-issue-149379-1.current.stderr

Lines changed: 0 additions & 32 deletions
This file was deleted.

tests/ui/coercion/fudge-inference/fn-ret-trait-object-propagated-to-inputs-issue-149379-1.next.stderr

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
//@ revisions: current next
22
//@ ignore-compare-mode-next-solver (explicit revisions)
33
//@[next] compile-flags: -Znext-solver
4+
//@ check-pass
45

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

108
fn foo<T>(x: (T, ())) -> Box<T> {
119
Box::new(x.0)
@@ -14,6 +12,4 @@ fn foo<T>(x: (T, ())) -> Box<T> {
1412
fn main() {
1513
// Uses expectation as its struct tail is sized, resulting in `(dyn Send, ())`
1614
let _: Box<dyn Send> = foo(((), ()));
17-
//~^ ERROR mismatched types
18-
//~| ERROR the size for values of type `dyn Send` cannot be known at compilation time
1915
}

0 commit comments

Comments
 (0)