implement type-changing-struct-update
put the test dir in test/ui/rfcs
This commit is contained in:
parent
41d8c94d45
commit
7bde18a0f3
10 changed files with 275 additions and 29 deletions
|
|
@ -42,6 +42,7 @@ pub enum TypeError<'tcx> {
|
|||
TupleSize(ExpectedFound<usize>),
|
||||
FixedArraySize(ExpectedFound<u64>),
|
||||
ArgCount,
|
||||
FieldMisMatch(Symbol, Symbol),
|
||||
|
||||
RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
|
||||
RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
|
||||
|
|
@ -134,6 +135,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
|
|||
pluralize!(values.found)
|
||||
),
|
||||
ArgCount => write!(f, "incorrect number of function parameters"),
|
||||
FieldMisMatch(adt, field) => write!(f, "field type mismatch: {}.{}", adt, field),
|
||||
RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"),
|
||||
RegionsInsufficientlyPolymorphic(br, _) => write!(
|
||||
f,
|
||||
|
|
@ -224,6 +226,7 @@ impl<'tcx> TypeError<'tcx> {
|
|||
| ArgumentMutability(_)
|
||||
| TupleSize(_)
|
||||
| ArgCount
|
||||
| FieldMisMatch(..)
|
||||
| RegionsDoesNotOutlive(..)
|
||||
| RegionsInsufficientlyPolymorphic(..)
|
||||
| RegionsOverlyPolymorphic(..)
|
||||
|
|
|
|||
|
|
@ -602,6 +602,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
|
|||
TupleSize(x) => TupleSize(x),
|
||||
FixedArraySize(x) => FixedArraySize(x),
|
||||
ArgCount => ArgCount,
|
||||
FieldMisMatch(x, y) => FieldMisMatch(x, y),
|
||||
RegionsDoesNotOutlive(a, b) => {
|
||||
return tcx.lift((a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use crate::type_error_struct;
|
|||
|
||||
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
|
||||
|
|
@ -33,8 +33,10 @@ use rustc_hir::def_id::DefId;
|
|||
use rustc_hir::{ExprKind, QPath};
|
||||
use rustc_infer::infer;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::InferOk;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
|
||||
use rustc_middle::ty::error::TypeError::{FieldMisMatch, Mismatch};
|
||||
use rustc_middle::ty::subst::SubstsRef;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::TypeFoldable;
|
||||
|
|
@ -1262,7 +1264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() });
|
||||
}
|
||||
|
||||
let error_happened = self.check_expr_struct_fields(
|
||||
let (error_happened, mut remaining_fields) = self.check_expr_struct_fields(
|
||||
adt_ty,
|
||||
expected,
|
||||
expr.hir_id,
|
||||
|
|
@ -1277,32 +1279,92 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// the fields with the base_expr. This could cause us to hit errors later
|
||||
// when certain fields are assumed to exist that in fact do not.
|
||||
if !error_happened {
|
||||
self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
|
||||
match adt_ty.kind() {
|
||||
ty::Adt(adt, substs) if adt.is_struct() => {
|
||||
let fru_field_types = adt
|
||||
.non_enum_variant()
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| {
|
||||
self.normalize_associated_types_in(
|
||||
expr.span,
|
||||
f.ty(self.tcx, substs),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
// FIXME: We are currently creating two branches here in order to maintain
|
||||
// consistency. But they should be merged as much as possible.
|
||||
if self.tcx.features().type_changing_struct_update {
|
||||
let base_ty = self.check_expr(base_expr);
|
||||
match (adt_ty.kind(), base_ty.kind()) {
|
||||
(ty::Adt(adt, substs), ty::Adt(base_adt, base_subs)) if adt == base_adt => {
|
||||
if !adt.is_struct() {
|
||||
self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct {
|
||||
span: base_expr.span,
|
||||
});
|
||||
};
|
||||
let fru_field_types = 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 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: Needs better diagnostics here
|
||||
Err(_) => self
|
||||
.report_mismatched_types(
|
||||
&cause,
|
||||
target_ty,
|
||||
fru_ty,
|
||||
FieldMisMatch(variant.ident.name, ident.name),
|
||||
)
|
||||
.emit(),
|
||||
}
|
||||
}
|
||||
fru_ty
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.fru_field_types_mut()
|
||||
.insert(expr.hir_id, fru_field_types);
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.fru_field_types_mut()
|
||||
.insert(expr.hir_id, fru_field_types);
|
||||
}
|
||||
_ => {
|
||||
self.report_mismatched_types(
|
||||
&self.misc(base_expr.span),
|
||||
adt_ty,
|
||||
base_ty,
|
||||
Mismatch,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
|
||||
} else {
|
||||
self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
|
||||
match adt_ty.kind() {
|
||||
ty::Adt(adt, substs) if adt.is_struct() => {
|
||||
let fru_field_types = adt
|
||||
.non_enum_variant()
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| {
|
||||
self.normalize_associated_types_in(
|
||||
expr.span,
|
||||
f.ty(self.tcx, substs),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.fru_field_types_mut()
|
||||
.insert(expr.hir_id, fru_field_types);
|
||||
}
|
||||
_ => {
|
||||
self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct {
|
||||
span: base_expr.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
|
||||
|
|
@ -1319,7 +1381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
ast_fields: &'tcx [hir::ExprField<'tcx>],
|
||||
check_completeness: bool,
|
||||
expr_span: Span,
|
||||
) -> bool {
|
||||
) -> (bool, FxHashSet<Ident>) {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let adt_ty_hint = self
|
||||
|
|
@ -1402,11 +1464,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if inaccessible_remaining_fields {
|
||||
self.report_inaccessible_fields(adt_ty, span);
|
||||
} else {
|
||||
self.report_missing_fields(adt_ty, span, remaining_fields);
|
||||
self.report_missing_fields(adt_ty, span, remaining_fields.clone());
|
||||
}
|
||||
}
|
||||
|
||||
error_happened
|
||||
(error_happened, remaining_fields.iter().map(|(ident, _)| ident.clone()).collect())
|
||||
}
|
||||
|
||||
fn check_struct_fields_on_error(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
# `type_changing_struct_update`
|
||||
|
||||
The tracking issue for this feature is: [#86555]
|
||||
|
||||
[#86555]: https://github.com/rust-lang/rust/issues/86555
|
||||
|
||||
------------------------
|
||||
|
||||
This implements [RFC2528]. When turned on, you can create instances of the same struct
|
||||
that have different generic type or lifetime parameters.
|
||||
|
||||
[RFC2528]: https://github.com/rust-lang/rfcs/blob/master/text/2528-type-changing-struct-update-syntax.md
|
||||
|
||||
```rust
|
||||
#![allow(unused_variables, dead_code)]
|
||||
#![feature(type_changing_struct_update)]
|
||||
|
||||
fn main () {
|
||||
struct Foo<T, U> {
|
||||
field1: T,
|
||||
field2: U,
|
||||
}
|
||||
|
||||
let base: Foo<String, i32> = Foo {
|
||||
field1: String::from("hello"),
|
||||
field2: 1234,
|
||||
};
|
||||
let updated: Foo<f64, i32> = Foo {
|
||||
field1: 3.14,
|
||||
..base
|
||||
};
|
||||
}
|
||||
```
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
// gate-test-type_changing_struct_update
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Machine<S> {
|
||||
state: S,
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/feature-gate-type_changing_struct_update.rs:20:11
|
||||
--> $DIR/feature-gate.rs:22:11
|
||||
|
|
||||
LL | ..m1
|
||||
| ^^ expected struct `State2`, found struct `State1`
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#![feature(type_changing_struct_update)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Machine<'a, S> {
|
||||
state: S,
|
||||
lt_str: &'a str,
|
||||
common_field: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct State1;
|
||||
#[derive(Clone)]
|
||||
struct State2;
|
||||
|
||||
fn update_to_state2() {
|
||||
let s = String::from("hello");
|
||||
let m1: Machine<State1> = Machine {
|
||||
state: State1,
|
||||
lt_str: &s,
|
||||
//~^ ERROR `s` does not live long enough [E0597]
|
||||
// FIXME: The error here actually comes from line 34. The
|
||||
// span of the error message should be corrected to line 34
|
||||
common_field: 2,
|
||||
};
|
||||
// update lifetime
|
||||
let m3: Machine<'static, State1> = Machine {
|
||||
lt_str: "hello, too",
|
||||
..m1.clone()
|
||||
};
|
||||
// update lifetime and type
|
||||
let m4: Machine<'static, State2> = Machine {
|
||||
state: State2,
|
||||
lt_str: "hello, again",
|
||||
..m1.clone()
|
||||
};
|
||||
// updating to `static should fail.
|
||||
let m2: Machine<'static, State1> = Machine {
|
||||
..m1
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
error[E0597]: `s` does not live long enough
|
||||
--> $DIR/lifetime-update.rs:20:17
|
||||
|
|
||||
LL | lt_str: &s,
|
||||
| ^^ borrowed value does not live long enough
|
||||
...
|
||||
LL | let m2: Machine<'static, State1> = Machine {
|
||||
| ------------------------ type annotation requires that `s` is borrowed for `'static`
|
||||
...
|
||||
LL | }
|
||||
| - `s` dropped here while still borrowed
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0597`.
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
#![feature(type_changing_struct_update)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct Machine<'a, S, M> {
|
||||
state: S,
|
||||
message: M,
|
||||
lt_str: &'a str,
|
||||
common_field: i32,
|
||||
}
|
||||
|
||||
struct State1;
|
||||
struct State2;
|
||||
|
||||
struct Message1;
|
||||
struct Message2;
|
||||
|
||||
fn update() {
|
||||
let m1: Machine<State1, Message1> = Machine {
|
||||
state: State1,
|
||||
message: Message1,
|
||||
lt_str: "hello",
|
||||
common_field: 2,
|
||||
};
|
||||
// single type update
|
||||
let m2: Machine<State2, Message1> = Machine {
|
||||
state: State2,
|
||||
..m1
|
||||
};
|
||||
// multiple type update
|
||||
let m3: Machine<State2, Message2> = Machine {
|
||||
state: State2,
|
||||
message: Message2,
|
||||
..m1
|
||||
};
|
||||
}
|
||||
|
||||
fn fail_update() {
|
||||
let m1: Machine<f64, f64> = Machine {
|
||||
state: 3.2,
|
||||
message: 6.4,
|
||||
lt_str: "hello",
|
||||
common_field: 2,
|
||||
};
|
||||
// single type update fail
|
||||
let m2: Machine<i32, f64> = Machine {
|
||||
..m1
|
||||
//~^ ERROR mismatched types [E0308]
|
||||
};
|
||||
// multiple type update fail
|
||||
let m3 = Machine::<i32, i32> {
|
||||
..m1
|
||||
//~^ ERROR mismatched types [E0308]
|
||||
//~| ERROR mismatched types [E0308]
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/type-generic-update.rs:46:11
|
||||
|
|
||||
LL | ..m1
|
||||
| ^^ field type mismatch: Machine.state
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found type `f64`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/type-generic-update.rs:51:11
|
||||
|
|
||||
LL | ..m1
|
||||
| ^^ field type mismatch: Machine.state
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found type `f64`
|
||||
|
||||
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`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue