Skip to content

Commit 0ec16f3

Browse files
Subtype FRU fields first in type_changing_struct_update
1 parent e652caa commit 0ec16f3

File tree

4 files changed

+91
-71
lines changed

4 files changed

+91
-71
lines changed

compiler/rustc_typeck/src/check/expr.rs

+56-54
Original file line numberDiff line numberDiff line change
@@ -1561,71 +1561,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15611561
// FIXME: We are currently creating two branches here in order to maintain
15621562
// consistency. But they should be merged as much as possible.
15631563
let fru_tys = if self.tcx.features().type_changing_struct_update {
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.
1564+
if adt.is_struct() {
1565+
// Make an ADT with fresh inference substitutions.
1566+
let fresh_substs = self.fresh_substs_for_item(base_expr.span, adt.did());
1567+
// We do subtyping on the FRU fields first, so we can
1568+
// learn exactly what types we expect the base expr
1569+
// needs constrained to be compatible with the struct
1570+
// type we expect from the expectation value.
1571+
let fru_tys = variant
1572+
.fields
1573+
.iter()
1574+
.map(|f| {
1575+
let fru_ty = self.normalize_associated_types_in(
1576+
expr_span,
1577+
self.field_ty(base_expr.span, f, fresh_substs),
1578+
);
1579+
let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id);
1580+
if let Some(_) = remaining_fields.remove(&ident) {
1581+
let target_ty = self.field_ty(base_expr.span, f, substs);
1582+
let cause = self.misc(base_expr.span);
1583+
match self.at(&cause, self.param_env).sup(target_ty, fru_ty) {
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+
}
1598+
}
1599+
self.resolve_vars_if_possible(fru_ty)
1600+
})
1601+
.collect();
1602+
// The use of fresh substs that we have subtyped against
1603+
// our existing ADT type allows us to guide inference along
1604+
// so that, e.g.
15671605
// ```
1568-
// let x = MyStruct<'a, B, const C: usize> {
1569-
// f: 1,
1570-
// ..Default::default()
1606+
// MyStruct<'a, F1, F2, const C: usize> {
1607+
// f: F1,
1608+
// // Other fields that reference `'a`, `F2`, and `C`
1609+
// }
1610+
//
1611+
// let x = MyStruct {
1612+
// f: 1usize,
1613+
// ..other_struct
15711614
// };
15721615
// ```
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());
1616+
// will have the `other_struct` expression constrained to
1617+
// `MyStruct<'a, _, F2, C>`, as opposed to just `_`...
1618+
// This is important to allow coercions to happen in
1619+
// `other_struct` itself. See `coerce-in-base-expr.rs`.
15781620
let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs);
15791621
let base_ty = self.check_expr_has_type_or_error(
15801622
base_expr,
1581-
fresh_base_ty,
1623+
self.resolve_vars_if_possible(fresh_base_ty),
15821624
|_| {
15831625
error_happened = true;
15841626
},
15851627
);
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)
1609-
}
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()
1628+
if !error_happened && !base_ty.references_error() {
1629+
fru_tys
16251630
} 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`");
1628-
}
16291631
return;
16301632
}
16311633
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// check-pass
2+
3+
#![feature(type_changing_struct_update)]
4+
#![allow(incomplete_features)]
5+
6+
use std::any::Any;
7+
8+
struct Foo<A, B: ?Sized, C: ?Sized> {
9+
a: A,
10+
b: Box<B>,
11+
c: Box<C>,
12+
}
13+
14+
struct B;
15+
struct C;
16+
17+
fn main() {
18+
let y = Foo::<usize, dyn Any, dyn Any> {
19+
a: 0,
20+
b: Box::new(B),
21+
..Foo {
22+
a: 0,
23+
b: Box::new(B),
24+
// C needs to be told to coerce to `Box<dyn Any>`
25+
c: Box::new(C),
26+
}
27+
};
28+
}

src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs

-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ fn fail_update() {
5050
let m3 = Machine::<i32, i32> {
5151
..m1
5252
//~^ ERROR mismatched types [E0308]
53-
//~| ERROR mismatched types [E0308]
5453
};
5554
}
5655

src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr

+7-16
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,20 @@ error[E0308]: mismatched types
22
--> $DIR/type-generic-update.rs:46:11
33
|
44
LL | ..m1
5-
| ^^ field type mismatch: Machine.state
5+
| ^^ expected `i32`, found `f64`
66
|
7-
= note: expected type `i32`
8-
found type `f64`
7+
= note: expected struct `Machine<'_, i32, _>`
8+
found struct `Machine<'_, f64, _>`
99

1010
error[E0308]: mismatched types
1111
--> $DIR/type-generic-update.rs:51:11
1212
|
1313
LL | ..m1
14-
| ^^ field type mismatch: Machine.state
14+
| ^^ expected `i32`, found `f64`
1515
|
16-
= note: expected type `i32`
17-
found type `f64`
16+
= note: expected struct `Machine<'_, i32, i32>`
17+
found struct `Machine<'_, f64, f64>`
1818

19-
error[E0308]: mismatched types
20-
--> $DIR/type-generic-update.rs:51:11
21-
|
22-
LL | ..m1
23-
| ^^ field type mismatch: Machine.message
24-
|
25-
= note: expected type `i32`
26-
found type `f64`
27-
28-
error: aborting due to 3 previous errors
19+
error: aborting due to 2 previous errors
2920

3021
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)