diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 7110a1ba81d8f..08cf6d3a59ec5 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -74,10 +74,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) { - if let Some(mut err) = self.demand_coerce_diag(expr, checked_ty, expected) { + pub fn demand_coerce(&self, + expr: &hir::Expr, + checked_ty: Ty<'tcx>, + expected: Ty<'tcx>) + -> Ty<'tcx> { + let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected); + if let Some(mut err) = err { err.emit(); } + ty } // Checks that the type of `expr` can be coerced to `expected`. @@ -88,61 +94,64 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn demand_coerce_diag(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, - expected: Ty<'tcx>) -> Option> { + expected: Ty<'tcx>) + -> (Ty<'tcx>, Option>) { let expected = self.resolve_type_vars_with_obligations(expected); - if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { - let cause = self.misc(expr.span); - let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); - let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); + let e = match self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { + Ok(ty) => return (ty, None), + Err(e) => e + }; - // If the expected type is an enum with any variants whose sole - // field is of the found type, suggest such variants. See Issue - // #42764. - if let ty::TyAdt(expected_adt, substs) = expected.sty { - let mut compatible_variants = vec![]; - for variant in &expected_adt.variants { - if variant.fields.len() == 1 { - let sole_field = &variant.fields[0]; - let sole_field_ty = sole_field.ty(self.tcx, substs); - if self.can_coerce(expr_ty, sole_field_ty) { - let mut variant_path = self.tcx.item_path_str(variant.did); - variant_path = variant_path.trim_left_matches("std::prelude::v1::") - .to_string(); - compatible_variants.push(variant_path); - } + let cause = self.misc(expr.span); + let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); + let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); + + // If the expected type is an enum with any variants whose sole + // field is of the found type, suggest such variants. See Issue + // #42764. + if let ty::TyAdt(expected_adt, substs) = expected.sty { + let mut compatible_variants = vec![]; + for variant in &expected_adt.variants { + if variant.fields.len() == 1 { + let sole_field = &variant.fields[0]; + let sole_field_ty = sole_field.ty(self.tcx, substs); + if self.can_coerce(expr_ty, sole_field_ty) { + let mut variant_path = self.tcx.item_path_str(variant.did); + variant_path = variant_path.trim_left_matches("std::prelude::v1::") + .to_string(); + compatible_variants.push(variant_path); } } - if !compatible_variants.is_empty() { - let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr)); - let suggestions = compatible_variants.iter() - .map(|v| format!("{}({})", v, expr_text)).collect::>(); - err.span_suggestions(expr.span, - "try using a variant of the expected type", - suggestions); - } } + if !compatible_variants.is_empty() { + let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr)); + let suggestions = compatible_variants.iter() + .map(|v| format!("{}({})", v, expr_text)).collect::>(); + err.span_suggestions(expr.span, + "try using a variant of the expected type", + suggestions); + } + } - if let Some(suggestion) = self.check_ref(expr, - checked_ty, - expected) { - err.help(&suggestion); - } else { - let mode = probe::Mode::MethodCall; - let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID); - if suggestions.len() > 0 { - err.help(&format!("here are some functions which \ - might fulfill your needs:\n{}", - self.get_best_match(&suggestions).join("\n"))); - } + if let Some(suggestion) = self.check_ref(expr, + checked_ty, + expected) { + err.help(&suggestion); + } else { + let mode = probe::Mode::MethodCall; + let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, + mode, + expected, + checked_ty, + ast::DUMMY_NODE_ID); + if suggestions.len() > 0 { + err.help(&format!("here are some functions which \ + might fulfill your needs:\n{}", + self.get_best_match(&suggestions).join("\n"))); } - return Some(err); } - None + (expected, Some(err)) } fn format_method_suggestion(&self, method: &AssociatedItem) -> String { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index c82cafd3a62ed..26f7a7a378477 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2755,9 +2755,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn check_expr_coercable_to_type(&self, expr: &'gcx hir::Expr, expected: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.check_expr_with_hint(expr, expected); - self.demand_coerce(expr, ty, expected); - ty + self.check_expr_coercable_to_type_with_lvalue_pref(expr, expected, NoPreference) + } + + fn check_expr_coercable_to_type_with_lvalue_pref(&self, + expr: &'gcx hir::Expr, + expected: Ty<'tcx>, + lvalue_pref: LvaluePreference) + -> Ty<'tcx> { + let ty = self.check_expr_with_expectation_and_lvalue_pref( + expr, + ExpectHasType(expected), + lvalue_pref); + self.demand_coerce(expr, ty, expected) } fn check_expr_with_hint(&self, expr: &'gcx hir::Expr, diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index a3dd81fdddee3..2d45f797ecb4d 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -12,7 +12,7 @@ use super::FnCtxt; use super::method::MethodCallee; -use rustc::ty::{self, Ty, TypeFoldable, PreferMutLvalue, TypeVariants}; +use rustc::ty::{self, Ty, TypeFoldable, NoPreference, PreferMutLvalue, TypeVariants}; use rustc::ty::TypeVariants::{TyStr, TyRef}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::infer::type_variable::TypeVariableOrigin; @@ -29,12 +29,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { lhs_expr: &'gcx hir::Expr, rhs_expr: &'gcx hir::Expr) -> Ty<'tcx> { - let lhs_ty = self.check_expr_with_lvalue_pref(lhs_expr, PreferMutLvalue); - - let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty); - let (rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::Yes); - let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty); + let (lhs_ty, rhs_ty, return_ty) = + self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::Yes); let ty = if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { @@ -73,27 +69,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { lhs_expr, rhs_expr); - let lhs_ty = self.check_expr(lhs_expr); - let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty); - match BinOpCategory::from(op) { BinOpCategory::Shortcircuit => { // && and || are a simple case. + self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool); let lhs_diverges = self.diverges.get(); - self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty); - self.check_expr_coercable_to_type(rhs_expr, tcx.mk_bool()); + self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool); // Depending on the LHS' value, the RHS can never execute. self.diverges.set(lhs_diverges); - tcx.mk_bool() + tcx.types.bool } _ => { // Otherwise, we always treat operators as if they are // overloaded. This is the way to be most flexible w/r/t // types that get inferred. - let (rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs_expr, lhs_ty, + let (lhs_ty, rhs_ty, return_ty) = + self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::No); // Supply type inference hints if relevant. Probably these @@ -108,7 +101,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // deduce that the result type should be `u32`, even // though we don't know yet what type 2 has and hence // can't pin this down to a specific impl. - let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty); if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) @@ -164,17 +156,30 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn check_overloaded_binop(&self, expr: &'gcx hir::Expr, lhs_expr: &'gcx hir::Expr, - lhs_ty: Ty<'tcx>, rhs_expr: &'gcx hir::Expr, op: hir::BinOp, is_assign: IsAssign) - -> (Ty<'tcx>, Ty<'tcx>) + -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { - debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})", + debug!("check_overloaded_binop(expr.id={}, op={:?}, is_assign={:?})", expr.id, - lhs_ty, + op, is_assign); + let lhs_pref = match is_assign { + IsAssign::Yes => PreferMutLvalue, + IsAssign::No => NoPreference + }; + // Find a suitable supertype of the LHS expression's type, by coercing to + // a type variable, to pass as the `Self` to the trait, avoiding invariant + // trait matching creating lifetime constraints that are too strict. + // E.g. adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result + // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. + let lhs_ty = self.check_expr_coercable_to_type_with_lvalue_pref(lhs_expr, + self.next_ty_var(TypeVariableOrigin::MiscVariable(lhs_expr.span)), + lhs_pref); + let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty); + // NB: As we have not yet type-checked the RHS, we don't have the // type at hand. Make a variable to represent it. The whole reason // for this indirection is so that, below, we can check the expr @@ -187,6 +192,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // see `NB` above let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var); + let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty); let return_ty = match result { Ok(method) => { @@ -296,7 +302,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - (rhs_ty_var, return_ty) + (lhs_ty, rhs_ty, return_ty) } fn check_str_addition(&self, diff --git a/src/test/compile-fail/issue-41394.rs b/src/test/compile-fail/issue-41394.rs index 1fb3b7c4ee120..89f11edaec862 100644 --- a/src/test/compile-fail/issue-41394.rs +++ b/src/test/compile-fail/issue-41394.rs @@ -10,7 +10,7 @@ enum Foo { A = "" + 1 - //~^ ERROR binary operation `+` cannot be applied to type `&'static str` + //~^ ERROR binary operation `+` cannot be applied to type `&str` } enum Bar { diff --git a/src/test/run-fail/binop-fail-3.rs b/src/test/run-fail/binop-fail-3.rs index 5be9cd4a9bc34..efbc49fbece92 100644 --- a/src/test/run-fail/binop-fail-3.rs +++ b/src/test/run-fail/binop-fail-3.rs @@ -12,6 +12,8 @@ fn foo() -> ! { panic!("quux"); } + +#[allow(resolve_trait_on_defaulted_unit)] fn main() { foo() == foo(); // these types wind up being defaulted to () } diff --git a/src/test/run-pass/issue-32008.rs b/src/test/run-pass/issue-32008.rs new file mode 100644 index 0000000000000..cb489acf1d919 --- /dev/null +++ b/src/test/run-pass/issue-32008.rs @@ -0,0 +1,35 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that binary operators allow subtyping on both the LHS and RHS, +// and as such do not introduce unnecesarily strict lifetime constraints. + +use std::ops::Add; + +struct Foo; + +impl<'a> Add<&'a Foo> for &'a Foo { + type Output = (); + fn add(self, rhs: &'a Foo) {} +} + +fn try_to_add(input: &Foo) { + let local = Foo; + + // Manual reborrow worked even with invariant trait search. + &*input + &local; + + // Direct use of the reference on the LHS requires additional + // subtyping before searching (invariantly) for `LHS: Add`. + input + &local; +} + +fn main() { +} diff --git a/src/test/run-pass/issue-45425.rs b/src/test/run-pass/issue-45425.rs new file mode 100644 index 0000000000000..06ffa6b3dea93 --- /dev/null +++ b/src/test/run-pass/issue-45425.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::Add; + +fn ref_add(a: &T, b: &T) -> T +where + for<'x> &'x T: Add<&'x T, Output = T>, +{ + a + b +} + +fn main() {} diff --git a/src/test/ui/span/issue-39018.stderr b/src/test/ui/span/issue-39018.stderr index d87fc122d8ee2..2782753f6c8e9 100644 --- a/src/test/ui/span/issue-39018.stderr +++ b/src/test/ui/span/issue-39018.stderr @@ -1,4 +1,4 @@ -error[E0369]: binary operation `+` cannot be applied to type `&'static str` +error[E0369]: binary operation `+` cannot be applied to type `&str` --> $DIR/issue-39018.rs:12:13 | 12 | let x = "Hello " + "World!";