Skip to content
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

Propagate expected type hints through struct literals. #40398

Merged
merged 2 commits into from
Mar 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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());
Expand Down
49 changes: 31 additions & 18 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2321,7 +2321,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(),
Expand Down Expand Up @@ -2674,14 +2674,14 @@ 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,
call_span: Span,
expected_ret: Expectation<'tcx>,
formal_ret: Ty<'tcx>,
formal_args: &[Ty<'tcx>])
-> Vec<Ty<'tcx>> {
/// 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>,
formal_args: &[Ty<'tcx>])
-> Vec<Ty<'tcx>> {
let expected_args = expected_ret.only_has_type(self).and_then(|ret_ty| {
self.fudge_regions_if_ok(&RegionVariableOrigin::Coercion(call_span), || {
// Attempt to apply a subtyping relationship between the formal
Expand All @@ -2704,7 +2704,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
Expand Down Expand Up @@ -3061,14 +3061,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")
};

Expand All @@ -3083,10 +3091,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);

Expand All @@ -3098,7 +3108,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,
Expand All @@ -3120,7 +3131,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.
Expand Down Expand Up @@ -3230,6 +3242,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>
Expand All @@ -3248,7 +3261,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);
Expand Down Expand Up @@ -3793,7 +3806,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)
Expand Down
20 changes: 20 additions & 0 deletions src/test/run-pass/issue-31260.rs
Original file line number Diff line number Diff line change
@@ -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() {}