From 83ef3042de889db07b819fc6b2802cd0ace7e21c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 04:01:30 -0500 Subject: [PATCH] Modify `type_known_to_meet_builtin_bound` so that it doesn't suppress overflow, which should always result in an error. NB. Some of the hunks in this commit rely on a later commit which adds `tcx` into `param_env` and modifies `ParameterEnvironment` to implement `Typer`. --- src/librustc/lint/builtin.rs | 10 +- src/librustc/middle/check_match.rs | 4 +- src/librustc/middle/check_rvalues.rs | 2 +- src/librustc/middle/expr_use_visitor.rs | 25 ++--- src/librustc/middle/intrinsicck.rs | 12 ++- src/librustc/middle/traits/error_reporting.rs | 8 +- src/librustc/middle/traits/mod.rs | 91 ++++++++++++++++--- src/librustc/middle/ty.rs | 50 +++++----- src/librustc_trans/trans/_match.rs | 5 +- src/librustc_trans/trans/common.rs | 4 + src/librustc_trans/trans/datum.rs | 5 +- src/librustc_typeck/check/mod.rs | 8 +- src/librustc_typeck/coherence/mod.rs | 2 +- 13 files changed, 149 insertions(+), 77 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index c314fba91d59e..53249c724627e 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1612,15 +1612,11 @@ impl LintPass for MissingCopyImplementations { } _ => return, }; - let parameter_environment = ty::empty_parameter_environment(); - if !ty::type_moves_by_default(cx.tcx, - ty, - ¶meter_environment) { + let parameter_environment = ty::empty_parameter_environment(cx.tcx); + if !ty::type_moves_by_default(¶meter_environment, item.span, ty) { return } - if ty::can_type_implement_copy(cx.tcx, - ty, - ¶meter_environment).is_ok() { + if ty::can_type_implement_copy(¶meter_environment, item.span, ty).is_ok() { cx.span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, "type could implement `Copy`; consider adding `impl \ diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index d16224ec5b8ac..7952c99dd79b6 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -1032,9 +1032,7 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, match p.node { ast::PatIdent(ast::BindByValue(_), _, ref sub) => { let pat_ty = ty::node_id_to_type(tcx, p.id); - if ty::type_moves_by_default(tcx, - pat_ty, - &cx.param_env) { + if ty::type_moves_by_default(&cx.param_env, pat.span, pat_ty) { check_move(p, sub.as_ref().map(|p| &**p)); } } diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc/middle/check_rvalues.rs index 3b4ea5234f43f..1a7a642f82afc 100644 --- a/src/librustc/middle/check_rvalues.rs +++ b/src/librustc/middle/check_rvalues.rs @@ -60,7 +60,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for RvalueContextDelegate<'a, 'tcx> { cmt: mc::cmt<'tcx>, _: euv::ConsumeMode) { debug!("consume; cmt: {}; type: {}", *cmt, ty_to_string(self.tcx, cmt.ty)); - if !ty::type_is_sized(self.tcx, cmt.ty, self.param_env) { + if !ty::type_is_sized(self.param_env, span, cmt.ty) { span_err!(self.tcx.sess, span, E0161, "cannot move a value of type {0}: the size of {0} cannot be statically determined", ty_to_string(self.tcx, cmt.ty)); diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 7dbf797e7392c..b2d866cf5861d 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -367,10 +367,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { consume_id: ast::NodeId, consume_span: Span, cmt: mc::cmt<'tcx>) { - let mode = copy_or_move(self.tcx(), - cmt.ty, - self.param_env, - DirectRefMove); + let mode = copy_or_move(self.typer, &cmt, DirectRefMove); self.delegate.consume(consume_id, consume_span, cmt, mode); } @@ -1020,10 +1017,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { ast::PatIdent(ast::BindByRef(_), _, _) => mode.lub(BorrowingMatch), ast::PatIdent(ast::BindByValue(_), _, _) => { - match copy_or_move(tcx, - cmt_pat.ty, - self.param_env, - PatBindingMove) { + match copy_or_move(self.typer, &cmt_pat, PatBindingMove) { Copy => mode.lub(CopyingMatch), Move(_) => mode.lub(MovingMatch), } @@ -1085,10 +1079,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { r, bk, RefBinding); } ast::PatIdent(ast::BindByValue(_), _, _) => { - let mode = copy_or_move(typer.tcx(), - cmt_pat.ty, - param_env, - PatBindingMove); + let mode = copy_or_move(typer, &cmt_pat, PatBindingMove); debug!("walk_pat binding consuming pat"); delegate.consume_pat(pat, cmt_pat, mode); } @@ -1303,12 +1294,12 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { } } -fn copy_or_move<'tcx>(tcx: &ty::ctxt<'tcx>, - ty: Ty<'tcx>, - param_env: &ParameterEnvironment<'tcx>, +fn copy_or_move<'tcx>(typer: &mc::Typer<'tcx>, + cmt: &mc::cmt<'tcx>, move_reason: MoveReason) - -> ConsumeMode { - if ty::type_moves_by_default(tcx, ty, param_env) { + -> ConsumeMode +{ + if typer.type_moves_by_default(cmt.span, cmt.ty) { Move(move_reason) } else { Copy diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index df06b3b7789c9..89ffcbf37a938 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -170,6 +170,7 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { let mut substs = param_env.free_substs.clone(); self.with_each_combination( + span, param_env, param_env.free_substs.types.iter_enumerated(), &mut substs, @@ -187,7 +188,8 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { } fn with_each_combination(&self, - param_env: &ty::ParameterEnvironment<'tcx>, + span: Span, + param_env: &ty::ParameterEnvironment<'a,'tcx>, mut types_in_scope: EnumeratedItems>, substs: &mut Substs<'tcx>, callback: &mut FnMut(&Substs<'tcx>)) @@ -210,15 +212,17 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { debug!("with_each_combination: space={}, index={}, param_ty={}", space, index, param_ty.repr(self.tcx)); - if !ty::type_is_sized(self.tcx, param_ty, param_env) { + if !ty::type_is_sized(param_env, span, param_ty) { debug!("with_each_combination: param_ty is not known to be sized"); substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty; - self.with_each_combination(param_env, types_in_scope.clone(), substs, callback); + self.with_each_combination(span, param_env, types_in_scope.clone(), + substs, callback); } substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty; - self.with_each_combination(param_env, types_in_scope, substs, callback); + self.with_each_combination(span, param_env, types_in_scope, + substs, callback); } } } diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index a0413701abcad..05ea2f9a7d258 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -77,7 +77,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, "overflow evaluating the requirement `{}`", predicate.user_string(infcx.tcx)).as_slice()); - suggest_new_overflow_limit(infcx, obligation.cause.span); + suggest_new_overflow_limit(infcx.tcx, obligation.cause.span); note_obligation_cause(infcx, obligation); } @@ -332,10 +332,10 @@ fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, } } -pub fn suggest_new_overflow_limit(infcx: &InferCtxt, span: Span) { - let current_limit = infcx.tcx.sess.recursion_limit.get(); +pub fn suggest_new_overflow_limit(tcx: &ty::ctxt, span: Span) { + let current_limit = tcx.sess.recursion_limit.get(); let suggested_limit = current_limit * 2; - infcx.tcx.sess.span_note( + tcx.sess.span_note( span, format!( "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index c83898bcd8ade..1a1d52a047c84 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -15,6 +15,7 @@ pub use self::FulfillmentErrorCode::*; pub use self::Vtable::*; pub use self::ObligationCauseCode::*; +use middle::mem_categorization::Typer; use middle::subst; use middle::ty::{mod, Ty}; use middle::infer::InferCtxt; @@ -22,9 +23,10 @@ use std::slice::Iter; use std::rc::Rc; use syntax::ast; use syntax::codemap::{Span, DUMMY_SP}; -use util::ppaux::Repr; +use util::ppaux::{Repr, UserString}; pub use self::error_reporting::report_fulfillment_errors; +pub use self::error_reporting::suggest_new_overflow_limit; pub use self::coherence::orphan_check; pub use self::coherence::OrphanCheckErr; pub use self::fulfill::{FulfillmentContext, RegionObligation}; @@ -288,11 +290,12 @@ pub fn predicates_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>, /// `bound` or is not known to meet bound (note that this is /// conservative towards *no impl*, which is the opposite of the /// `evaluate` methods). -pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>, - ty: Ty<'tcx>, - bound: ty::BuiltinBound) - -> bool +pub fn evaluate_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, + typer: &ty::UnboxedClosureTyper<'tcx>, + ty: Ty<'tcx>, + bound: ty::BuiltinBound, + span: Span) + -> SelectionResult<'tcx, ()> { debug!("type_known_to_meet_builtin_bound(ty={}, bound={})", ty.repr(infcx.tcx), @@ -300,17 +303,49 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, let mut fulfill_cx = FulfillmentContext::new(); - // We can use dummy values here because we won't report any errors - // that result nor will we pay any mind to region obligations that arise - // (there shouldn't really be any anyhow). - let cause = ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID); + // We can use a dummy node-id here because we won't pay any mind + // to region obligations that arise (there shouldn't really be any + // anyhow). + let cause = ObligationCause::misc(span, ast::DUMMY_NODE_ID); fulfill_cx.register_builtin_bound(infcx, ty, bound, cause); // Note: we only assume something is `Copy` if we can // *definitively* show that it implements `Copy`. Otherwise, // assume it is move; linear is always ok. - let result = fulfill_cx.select_all_or_error(infcx, param_env, infcx.tcx).is_ok(); + let result = match fulfill_cx.select_all_or_error(infcx, typer) { + Ok(()) => Ok(Some(())), // Success, we know it implements Copy. + Err(errors) => { + // Check if overflow occurred anywhere and propagate that. + if errors.iter().any( + |err| match err.code { CodeSelectionError(Overflow) => true, _ => false }) + { + return Err(Overflow); + } + + // Otherwise, if there were any hard errors, propagate an + // arbitrary one of those. If no hard errors at all, + // report ambiguity. + let sel_error = + errors.iter() + .filter_map(|err| { + match err.code { + CodeAmbiguity => None, + CodeSelectionError(ref e) => Some(e.clone()), + CodeProjectionError(_) => { + infcx.tcx.sess.span_bug( + span, + "projection error while selecting?") + } + } + }) + .next(); + match sel_error { + None => { Ok(None) } + Some(e) => { Err(e) } + } + } + }; debug!("type_known_to_meet_builtin_bound: ty={} bound={} result={}", ty.repr(infcx.tcx), @@ -320,6 +355,40 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, result } +pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, + typer: &ty::UnboxedClosureTyper<'tcx>, + ty: Ty<'tcx>, + bound: ty::BuiltinBound, + span: Span) + -> bool +{ + match evaluate_builtin_bound(infcx, typer, ty, bound, span) { + Ok(Some(())) => { + // definitely impl'd + true + } + Ok(None) => { + // ambiguous: if coherence check was successful, shouldn't + // happen, but we might have reported an error and been + // soldering on, so just treat this like not implemented + false + } + Err(Overflow) => { + infcx.tcx.sess.span_err( + span, + format!("overflow evaluating whether `{}` is `{}`", + ty.user_string(infcx.tcx), + bound.user_string(infcx.tcx))[]); + suggest_new_overflow_limit(infcx.tcx, span); + false + } + Err(_) => { + // other errors: not implemented. + false + } + } +} + impl<'tcx,O> Obligation<'tcx,O> { pub fn new(cause: ObligationCause<'tcx>, trait_ref: O) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 079972eff6791..fd32e798689ed 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3548,11 +3548,12 @@ fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>, } } - let infcx = infer::new_infer_ctxt(cx); - let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, param_env, ty, bound); + let infcx = infer::new_infer_ctxt(param_env.tcx); + + let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, param_env, ty, bound, span); debug!("type_impls_bound({}, {}) = {}", - ty_to_string(cx, ty), + ty.repr(param_env.tcx), bound, is_impld); @@ -3564,20 +3565,22 @@ fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>, is_impld } -pub fn type_moves_by_default<'tcx>(cx: &ctxt<'tcx>, - ty: Ty<'tcx>, - param_env: &ParameterEnvironment<'tcx>) - -> bool +pub fn type_moves_by_default<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>, + span: Span, + ty: Ty<'tcx>) + -> bool { - !type_impls_bound(cx, &cx.type_impls_copy_cache, param_env, ty, ty::BoundCopy) + let tcx = param_env.tcx; + !type_impls_bound(param_env, &tcx.type_impls_copy_cache, ty, ty::BoundCopy, span) } -pub fn type_is_sized<'tcx>(cx: &ctxt<'tcx>, - ty: Ty<'tcx>, - param_env: &ParameterEnvironment<'tcx>) - -> bool +pub fn type_is_sized<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>, + span: Span, + ty: Ty<'tcx>) + -> bool { - type_impls_bound(cx, &cx.type_impls_sized_cache, param_env, ty, ty::BoundSized) + let tcx = param_env.tcx; + type_impls_bound(param_env, &tcx.type_impls_sized_cache, ty, ty::BoundSized, span) } pub fn is_ffi_safe<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool { @@ -6562,6 +6565,10 @@ impl<'tcx> mc::Typer<'tcx> for ty::ctxt<'tcx> { -> ast::CaptureClause { self.capture_modes.borrow()[closure_expr_id].clone() } + + fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { + type_moves_by_default(self, span, ty) + } } impl<'tcx> UnboxedClosureTyper<'tcx> for ty::ctxt<'tcx> { @@ -6944,15 +6951,18 @@ pub enum CopyImplementationError { TypeIsStructural, } -pub fn can_type_implement_copy<'tcx>(tcx: &ctxt<'tcx>, - self_type: Ty<'tcx>, - param_env: &ParameterEnvironment<'tcx>) - -> Result<(),CopyImplementationError> { +pub fn can_type_implement_copy<'a,'tcx>(param_env: &ParameterEnvironment<'a, 'tcx>, + span: Span, + self_type: Ty<'tcx>) + -> Result<(),CopyImplementationError> +{ + let tcx = param_env.tcx; + match self_type.sty { ty::ty_struct(struct_did, substs) => { let fields = ty::struct_fields(tcx, struct_did, substs); for field in fields.iter() { - if type_moves_by_default(tcx, field.mt.ty, param_env) { + if type_moves_by_default(param_env, span, field.mt.ty) { return Err(FieldDoesNotImplementCopy(field.name)) } } @@ -6963,9 +6973,7 @@ pub fn can_type_implement_copy<'tcx>(tcx: &ctxt<'tcx>, for variant_arg_type in variant.args.iter() { let substd_arg_type = variant_arg_type.subst(tcx, substs); - if type_moves_by_default(tcx, - substd_arg_type, - param_env) { + if type_moves_by_default(param_env, span, substd_arg_type) { return Err(VariantDoesNotImplementCopy(variant.name)) } } diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index fc68d1d3258e1..0b1dfcb378ba0 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -1327,9 +1327,8 @@ fn create_bindings_map<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pat: &ast::Pat, let trmode; match bm { ast::BindByValue(_) - if !ty::type_moves_by_default(tcx, - variable_ty, - ¶m_env) || reassigned => { + if !ty::type_moves_by_default(¶m_env, span, variable_ty) || reassigned => + { llmatch = alloca_no_lifetime(bcx, llvariable_ty.ptr_to(), "__llmatch"); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index aa88224088063..660b7aeeb001b 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -625,6 +625,10 @@ impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> { -> ast::CaptureClause { self.tcx().capture_modes.borrow()[closure_expr_id].clone() } + + fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { + self.param_env().type_moves_by_default(span, ty) + } } impl<'blk, 'tcx> ty::UnboxedClosureTyper<'tcx> for BlockS<'blk, 'tcx> { diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs index 83bf06383a89c..0afe3a8ef8d4e 100644 --- a/src/librustc_trans/trans/datum.rs +++ b/src/librustc_trans/trans/datum.rs @@ -543,8 +543,9 @@ impl<'tcx, K: KindOps + fmt::Show> Datum<'tcx, K> { * affine values (since they must never be duplicated). */ - let param_env = ty::empty_parameter_environment(); - assert!(!ty::type_moves_by_default(bcx.tcx(), self.ty, ¶m_env)); + assert!(!ty::type_moves_by_default(&ty::empty_parameter_environment(bcx.tcx()), + DUMMY_SP, + self.ty)); self.shallow_copy_raw(bcx, dst) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 189798a1390c2..37ec24ae23b46 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1462,7 +1462,7 @@ fn check_cast(fcx: &FnCtxt, return } - if !fcx.type_is_known_to_be_sized(t_1) { + if !fcx.type_is_known_to_be_sized(t_1, cast_expr.span) { let tstr = fcx.infcx().ty_to_string(t_1); fcx.type_error_message(span, |actual| { format!("cast to unsized type: `{}` as `{}`", actual, tstr) @@ -1981,13 +1981,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn type_is_known_to_be_sized(&self, - ty: Ty<'tcx>) + ty: Ty<'tcx>, + span: Span) -> bool { traits::type_known_to_meet_builtin_bound(self.infcx(), self.param_env(), ty, - ty::BoundSized) + ty::BoundSized, + span) } pub fn register_builtin_bound(&self, diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 79e1efa618f53..bb308198330f3 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -482,7 +482,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { debug!("check_implementations_of_copy: self_type={} (free)", self_type.repr(tcx)); - match ty::can_type_implement_copy(tcx, self_type, ¶m_env) { + match ty::can_type_implement_copy(¶m_env, span, self_type) { Ok(()) => {} Err(ty::FieldDoesNotImplementCopy(name)) => { tcx.sess