Skip to content

Commit

Permalink
Modify type_known_to_meet_builtin_bound so that it doesn't suppress…
Browse files Browse the repository at this point in the history
… 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`.
  • Loading branch information
nikomatsakis committed Jan 3, 2015
1 parent 429d9cc commit 83ef304
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 77 deletions.
10 changes: 3 additions & 7 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
&parameter_environment) {
let parameter_environment = ty::empty_parameter_environment(cx.tcx);
if !ty::type_moves_by_default(&parameter_environment, item.span, ty) {
return
}
if ty::can_type_implement_copy(cx.tcx,
ty,
&parameter_environment).is_ok() {
if ty::can_type_implement_copy(&parameter_environment, item.span, ty).is_ok() {
cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
item.span,
"type could implement `Copy`; consider adding `impl \
Expand Down
4 changes: 1 addition & 3 deletions src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/check_rvalues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
25 changes: 8 additions & 17 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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),
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand Down
12 changes: 8 additions & 4 deletions src/librustc/middle/intrinsicck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<Ty<'tcx>>,
substs: &mut Substs<'tcx>,
callback: &mut FnMut(&Substs<'tcx>))
Expand All @@ -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);
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/librustc/middle/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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",
Expand Down
91 changes: 80 additions & 11 deletions src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@ 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;
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};
Expand Down Expand Up @@ -288,29 +290,62 @@ 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),
bound);

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),
Expand All @@ -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)
Expand Down
50 changes: 29 additions & 21 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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 {
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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))
}
}
Expand All @@ -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))
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/librustc_trans/trans/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
&param_env) || reassigned => {
if !ty::type_moves_by_default(&param_env, span, variable_ty) || reassigned =>
{
llmatch = alloca_no_lifetime(bcx,
llvariable_ty.ptr_to(),
"__llmatch");
Expand Down
Loading

0 comments on commit 83ef304

Please sign in to comment.