Skip to content

Commit 1ab2616

Browse files
committed
Add feature trigger and correct is_struct check
1 parent f3679bc commit 1ab2616

File tree

5 files changed

+129
-81
lines changed

5 files changed

+129
-81
lines changed

compiler/rustc_typeck/src/check/expr.rs

+112-65
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
3636
use rustc_infer::infer::InferOk;
3737
use rustc_middle::ty;
3838
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
39-
use rustc_middle::ty::error::TypeError::{FieldMisMatch, Mismatch};
39+
use rustc_middle::ty::error::TypeError::FieldMisMatch;
4040
use rustc_middle::ty::subst::SubstsRef;
4141
use rustc_middle::ty::Ty;
4242
use rustc_middle::ty::TypeFoldable;
4343
use rustc_middle::ty::{AdtKind, Visibility};
44+
use rustc_session::parse::feature_err;
4445
use rustc_span::edition::LATEST_STABLE_EDITION;
4546
use rustc_span::hygiene::DesugaringKind;
4647
use rustc_span::lev_distance::find_best_match_for_name;
@@ -1374,78 +1375,124 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13741375
}
13751376

13761377
if let Some(base_expr) = base_expr {
1377-
// FIXME: We are currently creating two branches here in order to maintain
1378-
// consistency. But they should be merged as much as possible.
1379-
let fru_tys = if self.tcx.features().type_changing_struct_update {
1380-
let base_ty = self.check_expr(base_expr);
1381-
match (adt_ty.kind(), base_ty.kind()) {
1382-
(ty::Adt(adt, substs), ty::Adt(base_adt, base_subs)) if adt == base_adt => {
1383-
if !adt.is_struct() {
1384-
self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct {
1385-
span: base_expr.span,
1386-
});
1387-
};
1378+
let expected = if self.tcx.features().type_changing_struct_update {
1379+
NoExpectation
1380+
} else {
1381+
ExpectHasType(adt_ty)
1382+
};
1383+
let mut ty = self.check_expr_with_expectation(base_expr, expected);
1384+
1385+
let expected_ty = expected.to_option(&self).unwrap_or(adt_ty);
1386+
// While we don't allow *arbitrary* coercions here, we *do* allow
1387+
// coercions from ! to `expected`.
1388+
if ty.is_never() {
1389+
assert!(
1390+
!self.typeck_results.borrow().adjustments().contains_key(base_expr.hir_id),
1391+
"expression with never type wound up being adjusted"
1392+
);
1393+
let adj_ty = self.next_ty_var(TypeVariableOrigin {
1394+
kind: TypeVariableOriginKind::AdjustmentType,
1395+
span: base_expr.span,
1396+
});
1397+
self.apply_adjustments(
1398+
base_expr,
1399+
vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }],
1400+
);
1401+
ty = adj_ty;
1402+
}
1403+
let cause = self.misc(base_expr.span);
1404+
let mut fru_tys = None;
1405+
let mut err = None;
1406+
let is_struct;
1407+
1408+
if let ty::Adt(adt, substs) = expected_ty.kind() {
1409+
match ty.kind() {
1410+
ty::Adt(base_adt, base_subs) if adt == base_adt => {
1411+
if self.tcx.features().type_changing_struct_update {
1412+
let tys = variant
1413+
.fields
1414+
.iter()
1415+
.map(|f| {
1416+
let fru_ty = self.normalize_associated_types_in(
1417+
expr_span,
1418+
self.field_ty(base_expr.span, f, base_subs),
1419+
);
1420+
let ident = self.tcx.adjust_ident(f.ident, variant.def_id);
1421+
if let Some(_) = remaining_fields.remove(&ident) {
1422+
let target_ty = self.field_ty(base_expr.span, f, substs);
1423+
match self.at(&cause, self.param_env).sup(target_ty, fru_ty)
1424+
{
1425+
Ok(InferOk { obligations, value: () }) => {
1426+
self.register_predicates(obligations)
1427+
}
1428+
// FIXME: Need better diagnostics for `FieldMisMatch` error
1429+
Err(_) => {
1430+
if err.is_none() {
1431+
err = Some(self.report_mismatched_types(
1432+
&cause,
1433+
target_ty,
1434+
fru_ty,
1435+
FieldMisMatch(
1436+
variant.ident.name,
1437+
ident.name,
1438+
),
1439+
))
1440+
}
1441+
}
1442+
}
1443+
}
1444+
fru_ty
1445+
})
1446+
.collect();
1447+
fru_tys = Some(tys);
1448+
} else {
1449+
err = self.demand_suptype_diag(base_expr.span, expected_ty, ty);
1450+
if err.is_some() && self.tcx.sess.is_nightly_build() {
1451+
feature_err(
1452+
&self.tcx.sess.parse_sess,
1453+
sym::type_changing_struct_update,
1454+
base_expr.span,
1455+
"type changing struct updating is experimental",
1456+
)
1457+
.emit();
1458+
}
1459+
}
1460+
}
1461+
_ => {
1462+
err = self.demand_suptype_diag(base_expr.span, expected_ty, ty);
1463+
}
1464+
}
1465+
is_struct = adt.is_struct();
1466+
if is_struct && fru_tys.is_none() {
1467+
fru_tys = Some(
13881468
variant
13891469
.fields
13901470
.iter()
13911471
.map(|f| {
1392-
let fru_ty = self.normalize_associated_types_in(
1472+
self.normalize_associated_types_in(
13931473
expr_span,
1394-
self.field_ty(base_expr.span, f, base_subs),
1395-
);
1396-
let ident = self.tcx.adjust_ident(f.ident, variant.def_id);
1397-
if let Some(_) = remaining_fields.remove(&ident) {
1398-
let target_ty = self.field_ty(base_expr.span, f, substs);
1399-
let cause = self.misc(base_expr.span);
1400-
match self.at(&cause, self.param_env).sup(target_ty, fru_ty) {
1401-
Ok(InferOk { obligations, value: () }) => {
1402-
self.register_predicates(obligations)
1403-
}
1404-
// FIXME: Need better diagnostics for `FieldMisMatch` error
1405-
Err(_) => self
1406-
.report_mismatched_types(
1407-
&cause,
1408-
target_ty,
1409-
fru_ty,
1410-
FieldMisMatch(variant.ident.name, ident.name),
1411-
)
1412-
.emit(),
1413-
}
1414-
}
1415-
fru_ty
1474+
f.ty(self.tcx, substs),
1475+
)
14161476
})
1417-
.collect()
1418-
}
1419-
_ => {
1420-
return self
1421-
.report_mismatched_types(
1422-
&self.misc(base_expr.span),
1423-
adt_ty,
1424-
base_ty,
1425-
Mismatch,
1426-
)
1427-
.emit();
1428-
}
1477+
.collect(),
1478+
)
14291479
}
14301480
} else {
1431-
self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
1432-
match adt_ty.kind() {
1433-
ty::Adt(adt, substs) if adt.is_struct() => variant
1434-
.fields
1435-
.iter()
1436-
.map(|f| {
1437-
self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs))
1438-
})
1439-
.collect(),
1440-
_ => {
1441-
return self
1442-
.tcx
1443-
.sess
1444-
.emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
1445-
}
1446-
}
1447-
};
1448-
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
1481+
err = self.demand_suptype_diag(base_expr.span, expected_ty, ty);
1482+
is_struct = false;
1483+
}
1484+
if let Some(mut err) = err {
1485+
let expr = base_expr.peel_drop_temps();
1486+
self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None);
1487+
err.emit();
1488+
}
1489+
if let Some(fru_tys) = fru_tys {
1490+
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
1491+
}
1492+
if !is_struct {
1493+
let e = FunctionalRecordUpdateOnNonStruct { span: base_expr.span };
1494+
self.tcx.sess.emit_err(e);
1495+
}
14491496
} else if kind_name != "union" && !remaining_fields.is_empty() {
14501497
let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
14511498
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ fn update_to_state2() {
1717
common_field1: "hello",
1818
common_field2: 2,
1919
};
20-
// FIXME: this should trigger feature gate
2120
let m2: Machine<State2> = Machine {
2221
state: State2,
23-
..m1 //~ ERROR mismatched types
22+
..m1
23+
//~^ ERROR type changing struct updating is experimental [E0658]
24+
//~| ERROR mismatched types [E0308]
2425
};
2526
assert_eq!(State2, m2.state);
2627
}
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1+
error[E0658]: type changing struct updating is experimental
2+
--> $DIR/feature-gate.rs:22:11
3+
|
4+
LL | ..m1
5+
| ^^
6+
|
7+
= note: see issue #86555 <https://github.com/rust-lang/rust/issues/86555> for more information
8+
= help: add `#![feature(type_changing_struct_update)]` to the crate attributes to enable
9+
110
error[E0308]: mismatched types
2-
--> $DIR/feature-gate.rs:23:11
11+
--> $DIR/feature-gate.rs:22:11
312
|
413
LL | ..m1
514
| ^^ expected struct `State2`, found struct `State1`
615
|
716
= note: expected struct `Machine<State2>`
817
found struct `Machine<State1>`
918

10-
error: aborting due to previous error
19+
error: aborting due to 2 previous errors
1120

12-
For more information about this error, try `rustc --explain E0308`.
21+
Some errors have detailed explanations: E0308, E0658.
22+
For more information about an error, try `rustc --explain E0308`.

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

+1-10
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,6 @@ LL | ..m1
1616
= note: expected type `i32`
1717
found type `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)