Skip to content

Commit b4cbff9

Browse files
committed
Auto merge of #129317 - compiler-errors:expectation-subtyping, r=<try>
Use equality when relating formal and expected type in arg checking #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 #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 #129286
2 parents 8910346 + f6f5d72 commit b4cbff9

File tree

5 files changed

+98
-80
lines changed

5 files changed

+98
-80
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/expr.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -1668,15 +1668,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16681668
) {
16691669
let tcx = self.tcx;
16701670

1671-
let expected_inputs =
1672-
self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]);
1673-
let adt_ty_hint = if let Some(expected_inputs) = expected_inputs {
1674-
expected_inputs.get(0).cloned().unwrap_or(adt_ty)
1671+
let adt_ty = self.resolve_vars_with_obligations(adt_ty);
1672+
let adt_ty_hint = if adt_ty.has_non_region_infer() {
1673+
expected.only_has_type(self).and_then(|expected| {
1674+
self.fudge_inference_if_ok(|| {
1675+
let ocx = ObligationCtxt::new(self);
1676+
ocx.sup(&self.misc(span), self.param_env, expected, adt_ty)?;
1677+
if !ocx.select_where_possible().is_empty() {
1678+
return Err(TypeError::Mismatch);
1679+
}
1680+
Ok(self.resolve_vars_if_possible(adt_ty))
1681+
})
1682+
.ok()
1683+
})
16751684
} else {
1676-
adt_ty
1685+
None
16771686
};
1678-
// re-link the regions that EIfEO can erase.
1679-
self.demand_eqtype(span, adt_ty_hint, adt_ty);
1687+
if let Some(adt_ty_hint) = adt_ty_hint {
1688+
// re-link the variables that the fudging above can create.
1689+
self.demand_eqtype(span, adt_ty_hint, adt_ty);
1690+
}
16801691

16811692
let ty::Adt(adt, args) = adt_ty.kind() else {
16821693
span_bug!(span, "non-ADT passed to check_expr_struct_fields");

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+1-38
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryRespons
2020
use rustc_infer::infer::{DefineOpaqueTypes, InferResult};
2121
use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM;
2222
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
23-
use rustc_middle::ty::error::TypeError;
2423
use rustc_middle::ty::fold::TypeFoldable;
2524
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
2625
use rustc_middle::ty::{
@@ -36,7 +35,7 @@ use rustc_span::Span;
3635
use rustc_target::abi::FieldIdx;
3736
use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
3837
use rustc_trait_selection::traits::{
39-
self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt,
38+
self, NormalizeExt, ObligationCauseCode, StructurallyNormalizeExt,
4039
};
4140

4241
use crate::callee::{self, DeferredCallResolution};
@@ -688,42 +687,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
688687
vec![ty_error; len]
689688
}
690689

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-
727690
pub(crate) fn resolve_lang_item_path(
728691
&self,
729692
lang_item: hir::LangItem,

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+56-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,47 @@ 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 formal_output = self.resolve_vars_with_obligations(formal_output);
218+
let expected_input_tys: Option<Vec<_>> = if formal_input_tys.has_non_region_infer() {
219+
expectation
220+
.only_has_type(self)
221+
.and_then(|expected_output| {
222+
self.fudge_inference_if_ok(|| {
223+
let ocx = ObligationCtxt::new(self);
224+
225+
// Attempt to apply a subtyping relationship between the formal
226+
// return type (likely containing type variables if the function
227+
// is polymorphic) and the expected return type.
228+
// No argument expectations are produced if unification fails.
229+
let origin = self.misc(call_span);
230+
ocx.sup(&origin, self.param_env, expected_output, formal_output)?;
231+
if !ocx.select_where_possible().is_empty() {
232+
return Err(TypeError::Mismatch);
233+
}
234+
235+
// Record all the argument types, with the args
236+
// produced from the above subtyping unification.
237+
Ok(Some(
238+
formal_input_tys
239+
.iter()
240+
.map(|&ty| self.resolve_vars_if_possible(ty))
241+
.collect(),
242+
))
243+
})
244+
.ok()
245+
})
246+
.unwrap_or_default()
247+
} else {
248+
None
249+
};
250+
212251
let mut err_code = E0061;
213252

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

292331
let coerce_error =
293332
self.coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None).err();
294-
295333
if coerce_error.is_some() {
296334
return Compatibility::Incompatible(coerce_error);
297335
}
298336

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(
337+
// 3. Check if the formal type is actually equal to the checked one
338+
// and register any such obligations for future type checks.
339+
let formal_ty_error = self.at(&self.misc(provided_arg.span), self.param_env).eq(
302340
DefineOpaqueTypes::Yes,
303341
formal_input_ty,
304342
coerced_ty,
305343
);
306344

307345
// If neither check failed, the types are compatible
308-
match supertype_error {
346+
match formal_ty_error {
309347
Ok(InferOk { obligations, value: () }) => {
310348
self.register_predicates(obligations);
311349
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)