Skip to content

Commit fc83350

Browse files
committed
Auto merge of rust-lang#129317 - compiler-errors:expectation-subtyping, r=<try>
Use equality when relating formal and expected type in arg checking rust-lang#129059 uncovered an interesting issue in argument checking. When we check arguments, we create three sets of types: * Formals * Expected * Actuals The **actuals** are the types of the argument expressions themselves. The **formals** are the types from the signature that we're checking. The **expected** types are the formal types, but passed through `expected_inputs_for_expected_outputs`: https://github.com/rust-lang/rust/blob/a971212545766fdfe0dd68e5d968133f79944a19/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs#L691-L725 This method attempts to constrain the formal inputs by relating the [expectation](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/expectation/enum.Expectation.html) of the call expression and the formal output. When we check an argument, we get the expression's actual type, and then we first attempt to coerce it to the expected type: https://github.com/rust-lang/rust/blob/a971212545766fdfe0dd68e5d968133f79944a19/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs#L280-L293 Then we subtype the expected type and the formal type: https://github.com/rust-lang/rust/blob/a971212545766fdfe0dd68e5d968133f79944a19/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs#L299-L305 However, since we are now recording the right coercion target (since rust-lang#129059), we now end up recording the expected type to the typeck results, rather than the actual. Since that expected type was [fudged](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.fudge_inference_if_ok), it has fresh variables. And since the expected type is only subtyped against the formal type, if that expected type has a bivariant parameter, it will likely remain unconstrained since `Covariant * Bivariant = Bivariant` according to [xform](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.Variance.html#method.xform). This leads to an unconstrained type variable in writeback. AFAICT, there's no reason for us to be using subtyping here, though. The expected output is already related to the expectation by subtyping: https://github.com/rust-lang/rust/blob/a971212545766fdfe0dd68e5d968133f79944a19/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs#L713 So the formals don't need "another" indirection of subtyping in the argument checking... So I've changed it to use equality here. We could alternatively fix this by requiring WF for all the expected types to constrain their bivariant parameters, but this seems a bit overkill. Fixes rust-lang#129286
2 parents 8910346 + c286326 commit fc83350

File tree

4 files changed

+78
-71
lines changed

4 files changed

+78
-71
lines changed

compiler/rustc_hir_typeck/src/callee.rs

+4-17
Original file line numberDiff line numberDiff line change
@@ -502,18 +502,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
502502
let fn_sig = self.instantiate_binder_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig);
503503
let fn_sig = self.normalize(call_expr.span, fn_sig);
504504

505-
// Call the generic checker.
506-
let expected_arg_tys = self.expected_inputs_for_expected_output(
507-
call_expr.span,
508-
expected,
509-
fn_sig.output(),
510-
fn_sig.inputs(),
511-
);
512505
self.check_argument_types(
513506
call_expr.span,
514507
call_expr,
515508
fn_sig.inputs(),
516-
expected_arg_tys,
509+
fn_sig.output(),
510+
expected,
517511
arg_exprs,
518512
fn_sig.c_variadic,
519513
TupleArgumentsFlag::DontTupleArguments,
@@ -865,19 +859,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
865859
// don't know the full details yet (`Fn` vs `FnMut` etc), but we
866860
// do know the types expected for each argument and the return
867861
// type.
868-
869-
let expected_arg_tys = self.expected_inputs_for_expected_output(
870-
call_expr.span,
871-
expected,
872-
fn_sig.output(),
873-
fn_sig.inputs(),
874-
);
875-
876862
self.check_argument_types(
877863
call_expr.span,
878864
call_expr,
879865
fn_sig.inputs(),
880-
expected_arg_tys,
866+
fn_sig.output(),
867+
expected,
881868
arg_exprs,
882869
fn_sig.c_variadic,
883870
TupleArgumentsFlag::TupleArguments,

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

-36
Original file line numberDiff line numberDiff line change
@@ -688,42 +688,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
688688
vec![ty_error; len]
689689
}
690690

691-
/// Unifies the output type with the expected type early, for more coercions
692-
/// and forward type information on the input expressions.
693-
#[instrument(skip(self, call_span), level = "debug")]
694-
pub(crate) fn expected_inputs_for_expected_output(
695-
&self,
696-
call_span: Span,
697-
expected_ret: Expectation<'tcx>,
698-
formal_ret: Ty<'tcx>,
699-
formal_args: &[Ty<'tcx>],
700-
) -> Option<Vec<Ty<'tcx>>> {
701-
let formal_ret = self.resolve_vars_with_obligations(formal_ret);
702-
let ret_ty = expected_ret.only_has_type(self)?;
703-
704-
let expect_args = self
705-
.fudge_inference_if_ok(|| {
706-
let ocx = ObligationCtxt::new(self);
707-
708-
// Attempt to apply a subtyping relationship between the formal
709-
// return type (likely containing type variables if the function
710-
// is polymorphic) and the expected return type.
711-
// No argument expectations are produced if unification fails.
712-
let origin = self.misc(call_span);
713-
ocx.sup(&origin, self.param_env, ret_ty, formal_ret)?;
714-
if !ocx.select_where_possible().is_empty() {
715-
return Err(TypeError::Mismatch);
716-
}
717-
718-
// Record all the argument types, with the args
719-
// produced from the above subtyping unification.
720-
Ok(Some(formal_args.iter().map(|&ty| self.resolve_vars_if_possible(ty)).collect()))
721-
})
722-
.unwrap_or_default();
723-
debug!(?formal_args, ?formal_ret, ?expect_args, ?expected_ret);
724-
expect_args
725-
}
726-
727691
pub(crate) fn resolve_lang_item_path(
728692
&self,
729693
lang_item: hir::LangItem,

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+55-18
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
1717
use rustc_index::IndexVec;
1818
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TypeTrace};
1919
use rustc_middle::ty::adjustment::AllowTwoPhase;
20+
use rustc_middle::ty::error::TypeError;
2021
use rustc_middle::ty::visit::TypeVisitableExt;
2122
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
2223
use rustc_middle::{bug, span_bug};
@@ -25,7 +26,7 @@ use rustc_span::symbol::{kw, Ident};
2526
use rustc_span::{sym, Span, DUMMY_SP};
2627
use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt};
2728
use rustc_trait_selection::infer::InferCtxtExt;
28-
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
29+
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext};
2930
use {rustc_ast as ast, rustc_hir as hir};
3031

3132
use crate::coercion::CoerceMany;
@@ -123,6 +124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
123124
};
124125
if let Err(guar) = has_error {
125126
let err_inputs = self.err_args(args_no_rcvr.len(), guar);
127+
let err_output = Ty::new_error(self.tcx, guar);
126128

127129
let err_inputs = match tuple_arguments {
128130
DontTupleArguments => err_inputs,
@@ -133,28 +135,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
133135
sp,
134136
expr,
135137
&err_inputs,
136-
None,
138+
err_output,
139+
NoExpectation,
137140
args_no_rcvr,
138141
false,
139142
tuple_arguments,
140143
method.ok().map(|method| method.def_id),
141144
);
142-
return Ty::new_error(self.tcx, guar);
145+
return err_output;
143146
}
144147

145148
let method = method.unwrap();
146-
// HACK(eddyb) ignore self in the definition (see above).
147-
let expected_input_tys = self.expected_inputs_for_expected_output(
148-
sp,
149-
expected,
150-
method.sig.output(),
151-
&method.sig.inputs()[1..],
152-
);
153149
self.check_argument_types(
154150
sp,
155151
expr,
156152
&method.sig.inputs()[1..],
157-
expected_input_tys,
153+
method.sig.output(),
154+
expected,
158155
args_no_rcvr,
159156
method.sig.c_variadic,
160157
tuple_arguments,
@@ -174,8 +171,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
174171
call_expr: &'tcx hir::Expr<'tcx>,
175172
// Types (as defined in the *signature* of the target function)
176173
formal_input_tys: &[Ty<'tcx>],
177-
// More specific expected types, after unifying with caller output types
178-
expected_input_tys: Option<Vec<Ty<'tcx>>>,
174+
formal_output: Ty<'tcx>,
175+
// Expected output from the parent expression or statement
176+
expectation: Expectation<'tcx>,
179177
// The expressions for each provided argument
180178
provided_args: &'tcx [hir::Expr<'tcx>],
181179
// Whether the function is variadic, for example when imported from C
@@ -209,6 +207,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
209207
);
210208
}
211209

210+
// First, let's unify the formal method signature with the expectation eagerly.
211+
// We use this to guide coercion inference; it's output is "fudged" which means
212+
// any remaining type variables are assigned to new, unrelated variables. This
213+
// is because the inference guidance here is only speculative.
214+
//
215+
// We only do this if the formals have non-region infer vars, since this is only
216+
// meant to guide inference.
217+
let expected_input_tys: Option<Vec<_>> = if formal_input_tys.has_non_region_infer() {
218+
expectation
219+
.only_has_type(self)
220+
.and_then(|expected_output| {
221+
self.fudge_inference_if_ok(|| {
222+
let ocx = ObligationCtxt::new(self);
223+
224+
// Attempt to apply a subtyping relationship between the formal
225+
// return type (likely containing type variables if the function
226+
// is polymorphic) and the expected return type.
227+
// No argument expectations are produced if unification fails.
228+
let origin = self.misc(call_span);
229+
ocx.sup(&origin, self.param_env, expected_output, formal_output)?;
230+
if !ocx.select_where_possible().is_empty() {
231+
return Err(TypeError::Mismatch);
232+
}
233+
234+
// Record all the argument types, with the args
235+
// produced from the above subtyping unification.
236+
Ok(Some(
237+
formal_input_tys
238+
.iter()
239+
.map(|&ty| self.resolve_vars_if_possible(ty))
240+
.collect(),
241+
))
242+
})
243+
.ok()
244+
})
245+
.unwrap_or_default()
246+
} else {
247+
None
248+
};
249+
212250
let mut err_code = E0061;
213251

214252
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
@@ -291,21 +329,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
291329

292330
let coerce_error =
293331
self.coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None).err();
294-
295332
if coerce_error.is_some() {
296333
return Compatibility::Incompatible(coerce_error);
297334
}
298335

299-
// 3. Check if the formal type is a supertype of the checked one
300-
// and register any such obligations for future type checks
301-
let supertype_error = self.at(&self.misc(provided_arg.span), self.param_env).sup(
336+
// 3. Check if the formal type is actually equal to the checked one
337+
// and register any such obligations for future type checks.
338+
let formal_ty_error = self.at(&self.misc(provided_arg.span), self.param_env).eq(
302339
DefineOpaqueTypes::Yes,
303340
formal_input_ty,
304341
coerced_ty,
305342
);
306343

307344
// If neither check failed, the types are compatible
308-
match supertype_error {
345+
match formal_ty_error {
309346
Ok(InferOk { obligations, value: () }) => {
310347
self.register_predicates(obligations);
311348
Compatibility::Compatible
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ check-pass
2+
3+
trait Trait {
4+
type Item;
5+
}
6+
7+
struct Struct<A: Trait<Item = B>, B> {
8+
pub field: A,
9+
}
10+
11+
fn identity<T>(x: T) -> T {
12+
x
13+
}
14+
15+
fn test<A: Trait<Item = B>, B>(x: &Struct<A, B>) {
16+
let x: &Struct<_, _> = identity(x);
17+
}
18+
19+
fn main() {}

0 commit comments

Comments
 (0)