Skip to content

Commit 7bde18a

Browse files
committed
implement type-changing-struct-update
put the test dir in test/ui/rfcs
1 parent 41d8c94 commit 7bde18a

File tree

10 files changed

+276
-30
lines changed

10 files changed

+276
-30
lines changed

compiler/rustc_middle/src/ty/error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub enum TypeError<'tcx> {
4242
TupleSize(ExpectedFound<usize>),
4343
FixedArraySize(ExpectedFound<u64>),
4444
ArgCount,
45+
FieldMisMatch(Symbol, Symbol),
4546

4647
RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
4748
RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
@@ -134,6 +135,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
134135
pluralize!(values.found)
135136
),
136137
ArgCount => write!(f, "incorrect number of function parameters"),
138+
FieldMisMatch(adt, field) => write!(f, "field type mismatch: {}.{}", adt, field),
137139
RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"),
138140
RegionsInsufficientlyPolymorphic(br, _) => write!(
139141
f,
@@ -224,6 +226,7 @@ impl<'tcx> TypeError<'tcx> {
224226
| ArgumentMutability(_)
225227
| TupleSize(_)
226228
| ArgCount
229+
| FieldMisMatch(..)
227230
| RegionsDoesNotOutlive(..)
228231
| RegionsInsufficientlyPolymorphic(..)
229232
| RegionsOverlyPolymorphic(..)

compiler/rustc_middle/src/ty/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
602602
TupleSize(x) => TupleSize(x),
603603
FixedArraySize(x) => FixedArraySize(x),
604604
ArgCount => ArgCount,
605+
FieldMisMatch(x, y) => FieldMisMatch(x, y),
605606
RegionsDoesNotOutlive(a, b) => {
606607
return tcx.lift((a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b));
607608
}

compiler/rustc_typeck/src/check/expr.rs

+91-29
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::type_error_struct;
2323

2424
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
2525
use rustc_ast as ast;
26-
use rustc_data_structures::fx::FxHashMap;
26+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2727
use rustc_data_structures::stack::ensure_sufficient_stack;
2828
use rustc_errors::ErrorReported;
2929
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
@@ -33,8 +33,10 @@ use rustc_hir::def_id::DefId;
3333
use rustc_hir::{ExprKind, QPath};
3434
use rustc_infer::infer;
3535
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
36+
use rustc_infer::infer::InferOk;
3637
use rustc_middle::ty;
3738
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
39+
use rustc_middle::ty::error::TypeError::{FieldMisMatch, Mismatch};
3840
use rustc_middle::ty::subst::SubstsRef;
3941
use rustc_middle::ty::Ty;
4042
use rustc_middle::ty::TypeFoldable;
@@ -1262,7 +1264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12621264
.emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() });
12631265
}
12641266

1265-
let error_happened = self.check_expr_struct_fields(
1267+
let (error_happened, mut remaining_fields) = self.check_expr_struct_fields(
12661268
adt_ty,
12671269
expected,
12681270
expr.hir_id,
@@ -1277,32 +1279,92 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12771279
// the fields with the base_expr. This could cause us to hit errors later
12781280
// when certain fields are assumed to exist that in fact do not.
12791281
if !error_happened {
1280-
self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
1281-
match adt_ty.kind() {
1282-
ty::Adt(adt, substs) if adt.is_struct() => {
1283-
let fru_field_types = adt
1284-
.non_enum_variant()
1285-
.fields
1286-
.iter()
1287-
.map(|f| {
1288-
self.normalize_associated_types_in(
1289-
expr.span,
1290-
f.ty(self.tcx, substs),
1291-
)
1292-
})
1293-
.collect();
1294-
1295-
self.typeck_results
1296-
.borrow_mut()
1297-
.fru_field_types_mut()
1298-
.insert(expr.hir_id, fru_field_types);
1282+
// FIXME: We are currently creating two branches here in order to maintain
1283+
// consistency. But they should be merged as much as possible.
1284+
if self.tcx.features().type_changing_struct_update {
1285+
let base_ty = self.check_expr(base_expr);
1286+
match (adt_ty.kind(), base_ty.kind()) {
1287+
(ty::Adt(adt, substs), ty::Adt(base_adt, base_subs)) if adt == base_adt => {
1288+
if !adt.is_struct() {
1289+
self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct {
1290+
span: base_expr.span,
1291+
});
1292+
};
1293+
let fru_field_types = variant
1294+
.fields
1295+
.iter()
1296+
.map(|f| {
1297+
let fru_ty = self.normalize_associated_types_in(
1298+
expr.span,
1299+
self.field_ty(base_expr.span, f, base_subs),
1300+
);
1301+
let ident = self.tcx.adjust_ident(f.ident, variant.def_id);
1302+
if remaining_fields.remove(&ident) {
1303+
let target_ty = self.field_ty(base_expr.span, f, substs);
1304+
let cause = self.misc(base_expr.span);
1305+
match self.at(&cause, self.param_env).sup(target_ty, fru_ty)
1306+
{
1307+
Ok(InferOk { obligations, value: () }) => {
1308+
self.register_predicates(obligations)
1309+
}
1310+
// FIXME: Needs better diagnostics here
1311+
Err(_) => self
1312+
.report_mismatched_types(
1313+
&cause,
1314+
target_ty,
1315+
fru_ty,
1316+
FieldMisMatch(variant.ident.name, ident.name),
1317+
)
1318+
.emit(),
1319+
}
1320+
}
1321+
fru_ty
1322+
})
1323+
.collect();
1324+
1325+
self.typeck_results
1326+
.borrow_mut()
1327+
.fru_field_types_mut()
1328+
.insert(expr.hir_id, fru_field_types);
1329+
}
1330+
_ => {
1331+
self.report_mismatched_types(
1332+
&self.misc(base_expr.span),
1333+
adt_ty,
1334+
base_ty,
1335+
Mismatch,
1336+
)
1337+
.emit();
1338+
}
12991339
}
1300-
_ => {
1301-
self.tcx
1302-
.sess
1303-
.emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
1340+
} else {
1341+
self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
1342+
match adt_ty.kind() {
1343+
ty::Adt(adt, substs) if adt.is_struct() => {
1344+
let fru_field_types = adt
1345+
.non_enum_variant()
1346+
.fields
1347+
.iter()
1348+
.map(|f| {
1349+
self.normalize_associated_types_in(
1350+
expr.span,
1351+
f.ty(self.tcx, substs),
1352+
)
1353+
})
1354+
.collect();
1355+
1356+
self.typeck_results
1357+
.borrow_mut()
1358+
.fru_field_types_mut()
1359+
.insert(expr.hir_id, fru_field_types);
1360+
}
1361+
_ => {
1362+
self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct {
1363+
span: base_expr.span,
1364+
});
1365+
}
13041366
}
1305-
}
1367+
};
13061368
}
13071369
}
13081370
self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
@@ -1319,7 +1381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13191381
ast_fields: &'tcx [hir::ExprField<'tcx>],
13201382
check_completeness: bool,
13211383
expr_span: Span,
1322-
) -> bool {
1384+
) -> (bool, FxHashSet<Ident>) {
13231385
let tcx = self.tcx;
13241386

13251387
let adt_ty_hint = self
@@ -1402,11 +1464,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14021464
if inaccessible_remaining_fields {
14031465
self.report_inaccessible_fields(adt_ty, span);
14041466
} else {
1405-
self.report_missing_fields(adt_ty, span, remaining_fields);
1467+
self.report_missing_fields(adt_ty, span, remaining_fields.clone());
14061468
}
14071469
}
14081470

1409-
error_happened
1471+
(error_happened, remaining_fields.iter().map(|(ident, _)| ident.clone()).collect())
14101472
}
14111473

14121474
fn check_struct_fields_on_error(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# `type_changing_struct_update`
2+
3+
The tracking issue for this feature is: [#86555]
4+
5+
[#86555]: https://github.com/rust-lang/rust/issues/86555
6+
7+
------------------------
8+
9+
This implements [RFC2528]. When turned on, you can create instances of the same struct
10+
that have different generic type or lifetime parameters.
11+
12+
[RFC2528]: https://github.com/rust-lang/rfcs/blob/master/text/2528-type-changing-struct-update-syntax.md
13+
14+
```rust
15+
#![allow(unused_variables, dead_code)]
16+
#![feature(type_changing_struct_update)]
17+
18+
fn main () {
19+
struct Foo<T, U> {
20+
field1: T,
21+
field2: U,
22+
}
23+
24+
let base: Foo<String, i32> = Foo {
25+
field1: String::from("hello"),
26+
field2: 1234,
27+
};
28+
let updated: Foo<f64, i32> = Foo {
29+
field1: 3.14,
30+
..base
31+
};
32+
}
33+
```

src/test/ui/feature-gates/feature-gate-type_changing_struct_update.rs renamed to src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// gate-test-type_changing_struct_update
2+
13
#[derive(Debug)]
24
struct Machine<S> {
35
state: S,

src/test/ui/feature-gates/feature-gate-type_changing_struct_update.stderr renamed to src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0308]: mismatched types
2-
--> $DIR/feature-gate-type_changing_struct_update.rs:20:11
2+
--> $DIR/feature-gate.rs:22:11
33
|
44
LL | ..m1
55
| ^^ expected struct `State2`, found struct `State1`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#![feature(type_changing_struct_update)]
2+
#![allow(incomplete_features)]
3+
4+
#[derive(Clone)]
5+
struct Machine<'a, S> {
6+
state: S,
7+
lt_str: &'a str,
8+
common_field: i32,
9+
}
10+
11+
#[derive(Clone)]
12+
struct State1;
13+
#[derive(Clone)]
14+
struct State2;
15+
16+
fn update_to_state2() {
17+
let s = String::from("hello");
18+
let m1: Machine<State1> = Machine {
19+
state: State1,
20+
lt_str: &s,
21+
//~^ ERROR `s` does not live long enough [E0597]
22+
// FIXME: The error here actually comes from line 34. The
23+
// span of the error message should be corrected to line 34
24+
common_field: 2,
25+
};
26+
// update lifetime
27+
let m3: Machine<'static, State1> = Machine {
28+
lt_str: "hello, too",
29+
..m1.clone()
30+
};
31+
// update lifetime and type
32+
let m4: Machine<'static, State2> = Machine {
33+
state: State2,
34+
lt_str: "hello, again",
35+
..m1.clone()
36+
};
37+
// updating to `static should fail.
38+
let m2: Machine<'static, State1> = Machine {
39+
..m1
40+
};
41+
}
42+
43+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0597]: `s` does not live long enough
2+
--> $DIR/lifetime-update.rs:20:17
3+
|
4+
LL | lt_str: &s,
5+
| ^^ borrowed value does not live long enough
6+
...
7+
LL | let m2: Machine<'static, State1> = Machine {
8+
| ------------------------ type annotation requires that `s` is borrowed for `'static`
9+
...
10+
LL | }
11+
| - `s` dropped here while still borrowed
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0597`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#![feature(type_changing_struct_update)]
2+
#![allow(incomplete_features)]
3+
4+
struct Machine<'a, S, M> {
5+
state: S,
6+
message: M,
7+
lt_str: &'a str,
8+
common_field: i32,
9+
}
10+
11+
struct State1;
12+
struct State2;
13+
14+
struct Message1;
15+
struct Message2;
16+
17+
fn update() {
18+
let m1: Machine<State1, Message1> = Machine {
19+
state: State1,
20+
message: Message1,
21+
lt_str: "hello",
22+
common_field: 2,
23+
};
24+
// single type update
25+
let m2: Machine<State2, Message1> = Machine {
26+
state: State2,
27+
..m1
28+
};
29+
// multiple type update
30+
let m3: Machine<State2, Message2> = Machine {
31+
state: State2,
32+
message: Message2,
33+
..m1
34+
};
35+
}
36+
37+
fn fail_update() {
38+
let m1: Machine<f64, f64> = Machine {
39+
state: 3.2,
40+
message: 6.4,
41+
lt_str: "hello",
42+
common_field: 2,
43+
};
44+
// single type update fail
45+
let m2: Machine<i32, f64> = Machine {
46+
..m1
47+
//~^ ERROR mismatched types [E0308]
48+
};
49+
// multiple type update fail
50+
let m3 = Machine::<i32, i32> {
51+
..m1
52+
//~^ ERROR mismatched types [E0308]
53+
//~| ERROR mismatched types [E0308]
54+
};
55+
}
56+
57+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/type-generic-update.rs:46:11
3+
|
4+
LL | ..m1
5+
| ^^ field type mismatch: Machine.state
6+
|
7+
= note: expected type `i32`
8+
found type `f64`
9+
10+
error[E0308]: mismatched types
11+
--> $DIR/type-generic-update.rs:51:11
12+
|
13+
LL | ..m1
14+
| ^^ field type mismatch: Machine.state
15+
|
16+
= note: expected type `i32`
17+
found type `f64`
18+
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
29+
30+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)