Rollup merge of #40398 - eddyb:struct-hint, r=nikomatsakis
Propagate expected type hints through struct literals. Partial fix for #31260 to maximize backwards-compatibility, i.e. the hint is provided but not coerced to. The added test works because `{...; x}` with a hint of `T` coerces `x` to `T`, and the reasoning why that is slightly different has to do with DSTs: `&Struct { tail: [x] }: &Struct<[T]>` has a hint of `[T]` for `[x]`, but the inferred type should be `[T; 1]` to succeed later, so `[x]` shouldn't be *forced* to be `[T]`. *However*, implementing that complete behavior in a backwards-compatible way may be non-trivial, and has not yet been fully investigated, while this PR fixes #40355 and can be backported. r? @nikomatsakis
This commit is contained in:
commit
6480a00222
|
@ -254,7 +254,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
// Call the generic checker.
|
||||
let expected_arg_tys =
|
||||
self.expected_types_for_fn_args(call_expr.span,
|
||||
self.expected_inputs_for_expected_output(call_expr.span,
|
||||
expected,
|
||||
fn_sig.output(),
|
||||
fn_sig.inputs());
|
||||
|
@ -280,7 +280,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
// do know the types expected for each argument and the return
|
||||
// type.
|
||||
|
||||
let expected_arg_tys = self.expected_types_for_fn_args(call_expr.span,
|
||||
let expected_arg_tys = self.expected_inputs_for_expected_output(call_expr.span,
|
||||
expected,
|
||||
fn_sig.output().clone(),
|
||||
fn_sig.inputs());
|
||||
|
|
|
@ -2292,7 +2292,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
match method_fn_ty.sty {
|
||||
ty::TyFnDef(def_id, .., ref fty) => {
|
||||
// HACK(eddyb) ignore self in the definition (see above).
|
||||
let expected_arg_tys = self.expected_types_for_fn_args(
|
||||
let expected_arg_tys = self.expected_inputs_for_expected_output(
|
||||
sp,
|
||||
expected,
|
||||
fty.0.output(),
|
||||
|
@ -2645,9 +2645,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
TypeAndSubsts { substs: substs, ty: substd_ty }
|
||||
}
|
||||
|
||||
/// Unifies the return type with the expected type early, for more coercions
|
||||
/// and forward type information on the argument expressions.
|
||||
fn expected_types_for_fn_args(&self,
|
||||
/// Unifies the output type with the expected type early, for more coercions
|
||||
/// and forward type information on the input expressions.
|
||||
fn expected_inputs_for_expected_output(&self,
|
||||
call_span: Span,
|
||||
expected_ret: Expectation<'tcx>,
|
||||
formal_ret: Ty<'tcx>,
|
||||
|
@ -2675,7 +2675,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
}).collect())
|
||||
}).ok()
|
||||
}).unwrap_or(vec![]);
|
||||
debug!("expected_types_for_fn_args(formal={:?} -> {:?}, expected={:?} -> {:?})",
|
||||
debug!("expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
|
||||
formal_args, formal_ret,
|
||||
expected_args, expected_ret);
|
||||
expected_args
|
||||
|
@ -3032,14 +3032,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
fn check_expr_struct_fields(&self,
|
||||
adt_ty: Ty<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
expr_id: ast::NodeId,
|
||||
span: Span,
|
||||
variant: &'tcx ty::VariantDef,
|
||||
ast_fields: &'gcx [hir::Field],
|
||||
check_completeness: bool) {
|
||||
let tcx = self.tcx;
|
||||
let (substs, adt_kind, kind_name) = match adt_ty.sty {
|
||||
ty::TyAdt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()),
|
||||
|
||||
let adt_ty_hint =
|
||||
self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty])
|
||||
.get(0).cloned().unwrap_or(adt_ty);
|
||||
|
||||
let (substs, hint_substs, adt_kind, kind_name) = match (&adt_ty.sty, &adt_ty_hint.sty) {
|
||||
(&ty::TyAdt(adt, substs), &ty::TyAdt(_, hint_substs)) => {
|
||||
(substs, hint_substs, adt.adt_kind(), adt.variant_descr())
|
||||
}
|
||||
_ => span_bug!(span, "non-ADT passed to check_expr_struct_fields")
|
||||
};
|
||||
|
||||
|
@ -3054,10 +3062,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
// Typecheck each field.
|
||||
for field in ast_fields {
|
||||
let expected_field_type;
|
||||
let final_field_type;
|
||||
let field_type_hint;
|
||||
|
||||
if let Some(v_field) = remaining_fields.remove(&field.name.node) {
|
||||
expected_field_type = self.field_ty(field.span, v_field, substs);
|
||||
final_field_type = self.field_ty(field.span, v_field, substs);
|
||||
field_type_hint = self.field_ty(field.span, v_field, hint_substs);
|
||||
|
||||
seen_fields.insert(field.name.node, field.span);
|
||||
|
||||
|
@ -3069,7 +3079,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
} else {
|
||||
error_happened = true;
|
||||
expected_field_type = tcx.types.err;
|
||||
final_field_type = tcx.types.err;
|
||||
field_type_hint = tcx.types.err;
|
||||
if let Some(_) = variant.find_field_named(field.name.node) {
|
||||
let mut err = struct_span_err!(self.tcx.sess,
|
||||
field.name.span,
|
||||
|
@ -3091,7 +3102,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
// Make sure to give a type to the field even if there's
|
||||
// an error, so we can continue typechecking
|
||||
self.check_expr_coercable_to_type(&field.expr, expected_field_type);
|
||||
let ty = self.check_expr_with_hint(&field.expr, field_type_hint);
|
||||
self.demand_coerce(&field.expr, ty, final_field_type);
|
||||
}
|
||||
|
||||
// Make sure the programmer specified correct number of fields.
|
||||
|
@ -3201,6 +3213,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
fn check_expr_struct(&self,
|
||||
expr: &hir::Expr,
|
||||
expected: Expectation<'tcx>,
|
||||
qpath: &hir::QPath,
|
||||
fields: &'gcx [hir::Field],
|
||||
base_expr: &'gcx Option<P<hir::Expr>>) -> Ty<'tcx>
|
||||
|
@ -3219,7 +3232,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
hir::QPath::TypeRelative(ref qself, _) => qself.span
|
||||
};
|
||||
|
||||
self.check_expr_struct_fields(struct_ty, expr.id, path_span, variant, fields,
|
||||
self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span, variant, fields,
|
||||
base_expr.is_none());
|
||||
if let &Some(ref base_expr) = base_expr {
|
||||
self.check_expr_has_type(base_expr, struct_ty);
|
||||
|
@ -3764,7 +3777,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
hir::ExprStruct(ref qpath, ref fields, ref base_expr) => {
|
||||
self.check_expr_struct(expr, qpath, fields, base_expr)
|
||||
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
|
||||
}
|
||||
hir::ExprField(ref base, ref field) => {
|
||||
self.check_field(expr, lvalue_pref, &base, field)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub struct Struct<K: 'static> {
|
||||
pub field: K,
|
||||
}
|
||||
|
||||
// Partial fix for #31260, doesn't work without {...}.
|
||||
static STRUCT: Struct<&'static [u8]> = Struct {
|
||||
field: {&[1]}
|
||||
};
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue