Skip to content

Commit e652caa

Browse files
committed
Auto merge of #97705 - compiler-errors:guide-inference, r=lcnr
Fix inference issues with unconstrained base expr in `type_changing_struct_update` Use fresh infer vars to guide inference along in `type_changing_struct_update`. Fixes #96878
2 parents 99930ac + 5052911 commit e652caa

File tree

2 files changed

+116
-67
lines changed
  • compiler/rustc_typeck/src/check
  • src/test/ui/rfcs/rfc-2528-type-changing-struct-update

2 files changed

+116
-67
lines changed

compiler/rustc_typeck/src/check/expr.rs

+85-67
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
4141
use rustc_infer::infer::InferOk;
4242
use rustc_middle::middle::stability;
4343
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
44-
use rustc_middle::ty::error::ExpectedFound;
45-
use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts};
44+
use rustc_middle::ty::error::TypeError::FieldMisMatch;
4645
use rustc_middle::ty::subst::SubstsRef;
4746
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeFoldable};
4847
use rustc_session::parse::feature_err;
@@ -65,7 +64,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6564
&self,
6665
expr: &'tcx hir::Expr<'tcx>,
6766
expected: Ty<'tcx>,
68-
extend_err: impl Fn(&mut Diagnostic),
67+
extend_err: impl FnMut(&mut Diagnostic),
6968
) -> Ty<'tcx> {
7069
self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err)
7170
}
@@ -74,7 +73,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7473
&self,
7574
expr: &'tcx hir::Expr<'tcx>,
7675
expected: Expectation<'tcx>,
77-
extend_err: impl Fn(&mut Diagnostic),
76+
mut extend_err: impl FnMut(&mut Diagnostic),
7877
) -> Ty<'tcx> {
7978
let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool);
8079
let mut ty = self.check_expr_with_expectation(expr, expected);
@@ -1480,10 +1479,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14801479
// re-link the regions that EIfEO can erase.
14811480
self.demand_eqtype(span, adt_ty_hint, adt_ty);
14821481

1483-
let (substs, adt_kind, kind_name) = match adt_ty.kind() {
1484-
ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()),
1485-
_ => span_bug!(span, "non-ADT passed to check_expr_struct_fields"),
1482+
let ty::Adt(adt, substs) = adt_ty.kind() else {
1483+
span_bug!(span, "non-ADT passed to check_expr_struct_fields");
14861484
};
1485+
let adt_kind = adt.adt_kind();
14871486

14881487
let mut remaining_fields = variant
14891488
.fields
@@ -1521,7 +1520,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15211520
});
15221521
} else {
15231522
self.report_unknown_field(
1524-
adt_ty, variant, field, ast_fields, kind_name, expr_span,
1523+
adt_ty,
1524+
variant,
1525+
field,
1526+
ast_fields,
1527+
adt.variant_descr(),
1528+
expr_span,
15251529
);
15261530
}
15271531

@@ -1534,7 +1538,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15341538
}
15351539

15361540
// Make sure the programmer specified correct number of fields.
1537-
if kind_name == "union" {
1541+
if adt_kind == AdtKind::Union {
15381542
if ast_fields.len() != 1 {
15391543
struct_span_err!(
15401544
tcx.sess,
@@ -1557,67 +1561,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15571561
// FIXME: We are currently creating two branches here in order to maintain
15581562
// consistency. But they should be merged as much as possible.
15591563
let fru_tys = if self.tcx.features().type_changing_struct_update {
1560-
let base_ty = self.check_expr(base_expr);
1561-
match adt_ty.kind() {
1562-
ty::Adt(adt, substs) if adt.is_struct() => {
1563-
match base_ty.kind() {
1564-
ty::Adt(base_adt, base_subs) if adt == base_adt => {
1565-
variant
1566-
.fields
1567-
.iter()
1568-
.map(|f| {
1569-
let fru_ty = self.normalize_associated_types_in(
1570-
expr_span,
1571-
self.field_ty(base_expr.span, f, base_subs),
1572-
);
1573-
let ident = self
1574-
.tcx
1575-
.adjust_ident(f.ident(self.tcx), variant.def_id);
1576-
if let Some(_) = remaining_fields.remove(&ident) {
1577-
let target_ty =
1578-
self.field_ty(base_expr.span, f, substs);
1579-
let cause = self.misc(base_expr.span);
1580-
match self
1581-
.at(&cause, self.param_env)
1582-
.sup(target_ty, fru_ty)
1583-
{
1584-
Ok(InferOk { obligations, value: () }) => {
1585-
self.register_predicates(obligations)
1586-
}
1587-
// FIXME: Need better diagnostics for `FieldMisMatch` error
1588-
Err(_) => {
1589-
self.report_mismatched_types(
1590-
&cause,
1591-
target_ty,
1592-
fru_ty,
1593-
FieldMisMatch(variant.name, ident.name),
1594-
)
1595-
.emit();
1596-
}
1597-
}
1564+
if let ty::Adt(adt, substs) = adt_ty.kind() && adt.is_struct() {
1565+
// Make an ADT with fresh inference substitutions. This
1566+
// will allow us to guide inference along so that, e.g.
1567+
// ```
1568+
// let x = MyStruct<'a, B, const C: usize> {
1569+
// f: 1,
1570+
// ..Default::default()
1571+
// };
1572+
// ```
1573+
// will have the default base expression constrained to
1574+
// `MyStruct<'_, _, _>`, as opposed to just `_`... This
1575+
// will allow us to then do a subtyping relation on all
1576+
// of the `remaining_fields` below, per the RFC.
1577+
let fresh_substs = self.fresh_substs_for_item(base_expr.span, adt.did());
1578+
let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs);
1579+
let base_ty = self.check_expr_has_type_or_error(
1580+
base_expr,
1581+
fresh_base_ty,
1582+
|_| {
1583+
error_happened = true;
1584+
},
1585+
);
1586+
let base_ty = self.shallow_resolve(base_ty);
1587+
if let ty::Adt(base_adt, base_substs) = base_ty.kind() && adt == base_adt {
1588+
variant
1589+
.fields
1590+
.iter()
1591+
.map(|f| {
1592+
let fru_ty = self.normalize_associated_types_in(
1593+
expr_span,
1594+
self.field_ty(base_expr.span, f, base_substs),
1595+
);
1596+
let ident = self
1597+
.tcx
1598+
.adjust_ident(f.ident(self.tcx), variant.def_id);
1599+
if let Some(_) = remaining_fields.remove(&ident) {
1600+
let target_ty =
1601+
self.field_ty(base_expr.span, f, substs);
1602+
let cause = self.misc(base_expr.span);
1603+
match self
1604+
.at(&cause, self.param_env)
1605+
.sup(target_ty, fru_ty)
1606+
{
1607+
Ok(InferOk { obligations, value: () }) => {
1608+
self.register_predicates(obligations)
15981609
}
1599-
fru_ty
1600-
})
1601-
.collect()
1602-
}
1603-
_ => {
1604-
self.report_mismatched_types(
1605-
&self.misc(base_expr.span),
1606-
adt_ty,
1607-
base_ty,
1608-
Sorts(ExpectedFound::new(true, adt_ty, base_ty)),
1609-
)
1610-
.emit();
1611-
return;
1612-
}
1610+
// FIXME: Need better diagnostics for `FieldMisMatch` error
1611+
Err(_) => {
1612+
self.report_mismatched_types(
1613+
&cause,
1614+
target_ty,
1615+
fru_ty,
1616+
FieldMisMatch(variant.name, ident.name),
1617+
)
1618+
.emit();
1619+
}
1620+
}
1621+
}
1622+
self.resolve_vars_if_possible(fru_ty)
1623+
})
1624+
.collect()
1625+
} else {
1626+
if !error_happened && !base_ty.references_error() {
1627+
span_bug!(base_expr.span, "expected an error to have been reported in `check_expr_has_type_or_error`");
16131628
}
1614-
}
1615-
_ => {
1616-
self.tcx
1617-
.sess
1618-
.emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
16191629
return;
16201630
}
1631+
} else {
1632+
// Check the base_expr, regardless of a bad expected adt_ty, so we can get
1633+
// type errors on that expression, too.
1634+
self.check_expr(base_expr);
1635+
self.tcx
1636+
.sess
1637+
.emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
1638+
return;
16211639
}
16221640
} else {
16231641
self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {
@@ -1653,7 +1671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16531671
}
16541672
};
16551673
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
1656-
} else if kind_name != "union" && !remaining_fields.is_empty() {
1674+
} else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
16571675
let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
16581676
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
16591677
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// check-pass
2+
3+
#![feature(type_changing_struct_update)]
4+
#![allow(incomplete_features)]
5+
6+
use std::borrow::Cow;
7+
use std::marker::PhantomData;
8+
9+
#[derive(Default)]
10+
struct NonGeneric {
11+
field1: usize,
12+
}
13+
14+
#[derive(Default)]
15+
struct Generic<T, U> {
16+
field1: T,
17+
field2: U,
18+
}
19+
20+
#[derive(Default)]
21+
struct MoreGeneric<'a, const N: usize> {
22+
// If only `for<const N: usize> [u32; N]: Default`...
23+
field1: PhantomData<[u32; N]>,
24+
field2: Cow<'a, str>,
25+
}
26+
27+
fn main() {
28+
let default1 = NonGeneric { ..Default::default() };
29+
let default2: Generic<i32, f32> = Generic { ..Default::default() };
30+
let default3: MoreGeneric<'static, 12> = MoreGeneric { ..Default::default() };
31+
}

0 commit comments

Comments
 (0)