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`.
This commit is contained in:
parent
429d9cce1b
commit
83ef3042de
13 changed files with 149 additions and 77 deletions
|
@ -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 \
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Ty<'tcx>>,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue