8000 implement rfc-2528 type_changing-struct-update by SparrowLii · Pull Request #90035 · rust-lang/rust · GitHub
[go: up one dir, main page]

Skip to content

implement rfc-2528 type_changing-struct-update #90035

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Add feature trigger and enable is_struct check
  • Loading branch information
SparrowLii committed Nov 5, 2021
commit 926892ddc0b75b50f5d0a3483d829e501aa8e895
188 changes: 85 additions & 103 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
use rustc_infer::infer::InferOk;
use rustc_middle::ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
use rustc_middle::ty::error::TypeError::FieldMisMatch;
use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts};
use rustc_middle::ty::relate::expected_found_bool;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::Ty;
use rustc_middle::ty::TypeFoldable;
Expand Down Expand Up @@ -1375,124 +1376,105 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

if let Some(base_expr) = base_expr {
let expected = if self.tcx.features().type_changing_struct_update {
NoExpectation
} else {
ExpectHasType(adt_ty)
};
let mut ty = self.check_expr_with_expectation(base_expr, expected);

let expected_ty = expected.to_option(&self).unwrap_or(adt_ty);
// While we don't allow *arbitrary* coercions here, we *do* allow
// coercions from ! to `expected`.
if ty.is_never() {
assert!(
!self.typeck_results.borrow().adjustments().contains_key(base_expr.hir_id),
"expression with never type wound up being adjusted"
);
let adj_ty = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::AdjustmentType,
span: base_expr.span,
});
self.apply_adjustments(
base_expr,
vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }],
);
ty = adj_ty;
}
let cause = self.misc(base_expr.span);
let mut fru_tys = None;
let mut err = None;
let is_struct;

if let ty::Adt(adt, substs) = expected_ty.kind() {
match ty.kind() {
ty::Adt(base_adt, base_subs) if adt == base_adt => {
if self.tcx.features().type_changing_struct_update {
let tys = variant
.fields
.iter()
.map(|f| {
let fru_ty = self.normalize_associated_types_in(
expr_span,
self.field_ty(base_expr.span, f, base_subs),
);
let ident = self.tcx.adjust_ident(f.ident, variant.def_id);
if let Some(_) = remaining_fields.remove(&ident) {
let target_ty = self.field_ty(base_expr.span, f, substs);
match self.at(&cause, self.param_env).sup(target_ty, fru_ty)
{
Ok(InferOk { obligations, value: () }) => {
self.register_predicates(obligations)
}
// FIXME: Need better diagnostics for `FieldMisMatch` error
Err(_) => {
if err.is_none() {
err = Some(self.report_mismatched_types(
// FIXME: We are currently creating two branches here in order to maintain
// consistency. But they should be merged as much as possible.
let fru_tys = if self.tcx.features().type_changing_struct_update {
let base_ty = self.check_expr(base_expr);
match adt_ty.kind() {
ty::Adt(adt, substs) if adt.is_struct() => {
match base_ty.kind() {
ty::Adt(base_adt, base_subs) if adt == base_adt => {
variant
.fields
.iter()
.map(|f| {
let fru_ty = self.normalize_associated_types_in(
expr_span,
self.field_ty(base_expr.span, f, base_subs),
);
let ident = self.tcx.adjust_ident(f.ident, variant.def_id);
if let Some(_) = remaining_fields.remove(&ident) {
let target_ty =
self.field_ty(base_expr.span, f, substs);
let cause = self.misc(base_expr.span);
match self
.at(&cause, self.param_env)
.sup(target_ty, fru_ty)
{
Ok(InferOk { obligations, value: () }) => {
self.register_predicates(obligations)
}
// FIXME: Need better diagnostics for `FieldMisMatch` error
Err(_) => self
.report_mismatched_types(
&cause,
target_ty,
fru_ty,
FieldMisMatch(
variant.ident.name,
ident.name,
),
))
}
)
.emit(),
}
}
}
fru_ty
})
.collect();
fru_tys = Some(tys);
} else {
err = self.demand_suptype_diag(base_expr.span, expected_ty, ty);
if err.is_some() && self.tcx.sess.is_nightly_build() {
feature_err(
&self.tcx.sess.parse_sess,
sym::type_changing_struct_update,
base_expr.span,
"type changing struct updating is experimental",
)
.emit();
fru_ty
})
.collect()
}
_ => {
return self
.report_mismatched_types(
&self.misc(base_expr.span),
adt_ty,
base_ty,
Sorts(expected_found_bool(true, adt_ty, base_ty)),
)
.emit();
}
}
}
_ => {
err = self.demand_suptype_diag(base_expr.span, expected_ty, ty);
return self
.tcx
.sess
.emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
}
}
is_struct = adt.is_struct();
if is_struct && fru_tys.is_none() {
fru_tys = Some(
variant
.fields
.iter()
.map(|f| {
self.normalize_associated_types_in(
expr_span,
f.ty(self.tcx, substs),
)
})
.collect(),
)
}
} else {
err = self.demand_suptype_diag(base_expr.span, expected_ty, ty);
is_struct = false;
}
if let Some(mut err) = err {
let expr = base_expr.peel_drop_temps();
self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None);
err.emit();
}
if let Some(fru_tys) = fru_tys {
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
}
if !is_struct {
let e = FunctionalRecordUpdateOnNonStruct { span: base_expr.span };
self.tcx.sess.emit_err(e);
}
self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {
let base_ty = self.check_expr(base_expr);
let same_adt = match (adt_ty.kind(), base_ty.kind()) {
(ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true,
_ => false,
};
if self.tcx.sess.is_nightly_build() && same_adt {
feature_err(
&self.tcx.sess.parse_sess,
sym::type_changing_struct_update,
base_expr.span,
"type changing struct updating is experimental",
)
.emit();
}
});
match adt_ty.kind() {
ty::Adt(adt, substs) if adt.is_struct() => variant
.fields
.iter()
.map(|f| {
self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs))
})
.collect(),
_ => {
return self
.tcx
.sess
.emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
}
}
};
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
} else if kind_name != "union" && !remaining_fields.is_empty() {
let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ fn fail_update() {
let m3 = Machine::<i32, i32> {
..m1
//~^ ERROR mismatched types [E0308]
//~| ERROR mismatched types [E0308]
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ LL | ..m1
= note: expected type `i32`
found type `f64`

error: aborting due to 2 previous errors
error[E0308]: mismatched types
--> $DIR/type-generic-update.rs:51:11
|
LL | ..m1
| ^^ field type mismatch: Machine.message
|
= note: expected type `i32`
found type `f64`

error: aborting due to 3 previous errors

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