From 3906aef5c6df5a6d71d0a6b0c9c816b9160f9de1 Mon Sep 17 00:00:00 2001 From: James Miller Date: Sun, 1 May 2016 17:56:07 +1200 Subject: [PATCH] Handle coercion casts properly when building the MIR Coercion casts (`expr as T` where the type of `expr` can be coerced to `T`) are essentially no-ops, as the actual work is done by a coercion. Previously a check for type equality was used to avoid emitting the redundant cast in the MIR, but this failed for coercion casts of function items that had lifetime parameters. The MIR trans code doesn't handle `FnPtr -> FnPtr` casts and produced an error. Also fixes a bug with type ascription expressions not having any adjustments applied. Fixes #33295 --- src/librustc_mir/build/expr/as_rvalue.rs | 9 +- src/librustc_mir/hair/cx/expr.rs | 791 ++++++++++--------- src/test/run-pass/mir_ascription_coercion.rs | 20 + src/test/run-pass/mir_coercion_casts.rs | 22 + 4 files changed, 448 insertions(+), 394 deletions(-) create mode 100644 src/test/run-pass/mir_ascription_coercion.rs create mode 100644 src/test/run-pass/mir_coercion_casts.rs diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 8992381135ea..88757c6873c5 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -87,12 +87,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { } ExprKind::Cast { source } => { let source = this.hir.mirror(source); - if source.ty == expr.ty { - this.expr_as_rvalue(block, source) - } else { - let source = unpack!(block = this.as_operand(block, source)); - block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty)) - } + + let source = unpack!(block = this.as_operand(block, source)); + block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty)) } ExprKind::ReifyFnPointer { source } => { let source = unpack!(block = this.as_operand(block, source)); diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 7dab8c4c5fb2..6d527f77800f 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -21,6 +21,7 @@ use rustc_const_eval as const_eval; use rustc::middle::region::CodeExtent; use rustc::hir::pat_util; use rustc::ty::{self, VariantDef, Ty}; +use rustc::ty::cast::CastKind as TyCastKind; use rustc::mir::repr::*; use rustc::hir; use syntax::ptr::P; @@ -29,398 +30,12 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { type Output = Expr<'tcx>; fn make_mirror<'a>(self, cx: &mut Cx<'a, 'tcx>) -> Expr<'tcx> { - debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span); - - let expr_ty = cx.tcx.expr_ty(self); // note: no adjustments (yet)! let temp_lifetime = cx.tcx.region_maps.temporary_scope(self.id); let expr_extent = cx.tcx.region_maps.node_extent(self.id); - let kind = match self.node { - // Here comes the interesting stuff: - hir::ExprMethodCall(_, _, ref args) => { - // Rewrite a.b(c) into UFCS form like Trait::b(a, c) - let expr = method_callee(cx, self, ty::MethodCall::expr(self.id)); - let args = args.iter() - .map(|e| e.to_ref()) - .collect(); - ExprKind::Call { - ty: expr.ty, - fun: expr.to_ref(), - args: args, - } - } - - hir::ExprCall(ref fun, ref args) => { - if cx.tcx.is_method_call(self.id) { - // The callee is something implementing Fn, FnMut, or FnOnce. - // Find the actual method implementation being called and - // build the appropriate UFCS call expression with the - // callee-object as self parameter. - - // rewrite f(u, v) into FnOnce::call_once(f, (u, v)) - - let method = method_callee(cx, self, ty::MethodCall::expr(self.id)); - - let sig = match method.ty.sty { - ty::TyFnDef(_, _, fn_ty) => &fn_ty.sig, - _ => span_bug!(self.span, "type of method is not an fn") - }; - - let sig = cx.tcx.no_late_bound_regions(sig).unwrap_or_else(|| { - span_bug!(self.span, "method call has late-bound regions") - }); - - assert_eq!(sig.inputs.len(), 2); - - let tupled_args = Expr { - ty: sig.inputs[1], - temp_lifetime: temp_lifetime, - span: self.span, - kind: ExprKind::Tuple { - fields: args.iter().map(ToRef::to_ref).collect() - } - }; - - ExprKind::Call { - ty: method.ty, - fun: method.to_ref(), - args: vec![fun.to_ref(), tupled_args.to_ref()] - } - } else { - let adt_data = if let hir::ExprPath(..) = fun.node { - // Tuple-like ADTs are represented as ExprCall. We convert them here. - expr_ty.ty_adt_def().and_then(|adt_def|{ - match cx.tcx.def_map.borrow()[&fun.id].full_def() { - Def::Variant(_, variant_id) => { - Some((adt_def, adt_def.variant_index_with_id(variant_id))) - }, - Def::Struct(..) => { - Some((adt_def, 0)) - }, - _ => None - } - }) - } else { None }; - if let Some((adt_def, index)) = adt_data { - let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(fun.id).substs); - let field_refs = args.iter().enumerate().map(|(idx, e)| FieldExprRef { - name: Field::new(idx), - expr: e.to_ref() - }).collect(); - ExprKind::Adt { - adt_def: adt_def, - substs: substs, - variant_index: index, - fields: field_refs, - base: None - } - } else { - ExprKind::Call { - ty: cx.tcx.node_id_to_type(fun.id), - fun: fun.to_ref(), - args: args.to_ref(), - } - } - } - } - - hir::ExprAddrOf(mutbl, ref expr) => { - let region = match expr_ty.sty { - ty::TyRef(r, _) => r, - _ => span_bug!(expr.span, "type of & not region"), - }; - ExprKind::Borrow { - region: *region, - borrow_kind: to_borrow_kind(mutbl), - arg: expr.to_ref(), - } - } - - hir::ExprBlock(ref blk) => { - ExprKind::Block { body: &blk } - } - - hir::ExprAssign(ref lhs, ref rhs) => { - ExprKind::Assign { - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), - } - } - - hir::ExprAssignOp(op, ref lhs, ref rhs) => { - if cx.tcx.is_method_call(self.id) { - let pass_args = if op.node.is_by_value() { - PassArgs::ByValue - } else { - PassArgs::ByRef - }; - overloaded_operator(cx, self, ty::MethodCall::expr(self.id), - pass_args, lhs.to_ref(), vec![rhs]) - } else { - ExprKind::AssignOp { - op: bin_op(op.node), - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), - } - } - } - - hir::ExprLit(..) => ExprKind::Literal { - literal: cx.const_eval_literal(self) - }, - - hir::ExprBinary(op, ref lhs, ref rhs) => { - if cx.tcx.is_method_call(self.id) { - let pass_args = if op.node.is_by_value() { - PassArgs::ByValue - } else { - PassArgs::ByRef - }; - overloaded_operator(cx, self, ty::MethodCall::expr(self.id), - pass_args, lhs.to_ref(), vec![rhs]) - } else { - // FIXME overflow - match op.node { - hir::BinOp_::BiAnd => { - ExprKind::LogicalOp { - op: LogicalOp::And, - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), - } - } - hir::BinOp_::BiOr => { - ExprKind::LogicalOp { - op: LogicalOp::Or, - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), - } - } - _ => { - let op = bin_op(op.node); - ExprKind::Binary { - op: op, - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), - } - } - } - } - } - - hir::ExprIndex(ref lhs, ref index) => { - if cx.tcx.is_method_call(self.id) { - overloaded_lvalue(cx, self, ty::MethodCall::expr(self.id), - PassArgs::ByValue, lhs.to_ref(), vec![index]) - } else { - ExprKind::Index { - lhs: lhs.to_ref(), - index: index.to_ref(), - } - } - } - - hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => { - if cx.tcx.is_method_call(self.id) { - overloaded_lvalue(cx, self, ty::MethodCall::expr(self.id), - PassArgs::ByValue, arg.to_ref(), vec![]) - } else { - ExprKind::Deref { arg: arg.to_ref() } - } - } - - hir::ExprUnary(hir::UnOp::UnNot, ref arg) => { - if cx.tcx.is_method_call(self.id) { - overloaded_operator(cx, self, ty::MethodCall::expr(self.id), - PassArgs::ByValue, arg.to_ref(), vec![]) - } else { - ExprKind::Unary { - op: UnOp::Not, - arg: arg.to_ref(), - } - } - } - - hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => { - if cx.tcx.is_method_call(self.id) { - overloaded_operator(cx, self, ty::MethodCall::expr(self.id), - PassArgs::ByValue, arg.to_ref(), vec![]) - } else { - // FIXME runtime-overflow - if let hir::ExprLit(_) = arg.node { - ExprKind::Literal { - literal: cx.const_eval_literal(self), - } - } else { - ExprKind::Unary { - op: UnOp::Neg, - arg: arg.to_ref(), - } - } - } - } - - hir::ExprStruct(_, ref fields, ref base) => { - match expr_ty.sty { - ty::TyStruct(adt, substs) => { - let field_refs = field_refs(&adt.variants[0], fields); - ExprKind::Adt { - adt_def: adt, - variant_index: 0, - substs: substs, - fields: field_refs, - base: base.as_ref().map(|base| { - FruInfo { - base: base.to_ref(), - field_types: cx.tcx.tables - .borrow() - .fru_field_types[&self.id] - .clone() - } - }) - } - } - ty::TyEnum(adt, substs) => { - match cx.tcx.def_map.borrow()[&self.id].full_def() { - Def::Variant(enum_id, variant_id) => { - debug_assert!(adt.did == enum_id); - assert!(base.is_none()); - - let index = adt.variant_index_with_id(variant_id); - let field_refs = field_refs(&adt.variants[index], fields); - ExprKind::Adt { - adt_def: adt, - variant_index: index, - substs: substs, - fields: field_refs, - base: None - } - } - ref def => { - span_bug!( - self.span, - "unexpected def: {:?}", - def); - } - } - } - _ => { - span_bug!( - self.span, - "unexpected type for struct literal: {:?}", - expr_ty); - } - } - } - - hir::ExprClosure(..) => { - let closure_ty = cx.tcx.expr_ty(self); - let (def_id, substs) = match closure_ty.sty { - ty::TyClosure(def_id, ref substs) => (def_id, substs), - _ => { - span_bug!(self.span, - "closure expr w/o closure type: {:?}", - closure_ty); - } - }; - let upvars = cx.tcx.with_freevars(self.id, |freevars| { - freevars.iter() - .enumerate() - .map(|(i, fv)| capture_freevar(cx, self, fv, substs.upvar_tys[i])) - .collect() - }); - ExprKind::Closure { - closure_id: def_id, - substs: &substs, - upvars: upvars, - } - } - - hir::ExprPath(..) => { - convert_path_expr(cx, self) - } - - hir::ExprInlineAsm(ref asm, ref outputs, ref inputs) => { - ExprKind::InlineAsm { - asm: asm, - outputs: outputs.to_ref(), - inputs: inputs.to_ref() - } - } - - // Now comes the rote stuff: - - hir::ExprRepeat(ref v, ref c) => ExprKind::Repeat { - value: v.to_ref(), - count: TypedConstVal { - ty: cx.tcx.expr_ty(c), - span: c.span, - value: match const_eval::eval_const_expr(cx.tcx, c) { - ConstVal::Integral(ConstInt::Usize(u)) => u, - other => bug!("constant evaluation of repeat count yielded {:?}", other), - }, - } - }, - hir::ExprRet(ref v) => - ExprKind::Return { value: v.to_ref() }, - hir::ExprBreak(label) => - ExprKind::Break { label: label.map(|_| loop_label(cx, self)) }, - hir::ExprAgain(label) => - ExprKind::Continue { label: label.map(|_| loop_label(cx, self)) }, - hir::ExprMatch(ref discr, ref arms, _) => - ExprKind::Match { discriminant: discr.to_ref(), - arms: arms.iter().map(|a| convert_arm(cx, a)).collect() }, - hir::ExprIf(ref cond, ref then, ref otherwise) => - ExprKind::If { condition: cond.to_ref(), - then: block::to_expr_ref(cx, then), - otherwise: otherwise.to_ref() }, - hir::ExprWhile(ref cond, ref body, _) => - ExprKind::Loop { condition: Some(cond.to_ref()), - body: block::to_expr_ref(cx, body) }, - hir::ExprLoop(ref body, _) => - ExprKind::Loop { condition: None, - body: block::to_expr_ref(cx, body) }, - hir::ExprField(ref source, name) => { - let index = match cx.tcx.expr_ty_adjusted(source).sty { - ty::TyStruct(adt_def, _) => - adt_def.variants[0].index_of_field_named(name.node), - ref ty => - span_bug!( - self.span, - "field of non-struct: {:?}", - ty), - }; - let index = index.unwrap_or_else(|| { - span_bug!( - self.span, - "no index found for field `{}`", - name.node) - }); - ExprKind::Field { lhs: source.to_ref(), name: Field::new(index) } - } - hir::ExprTupField(ref source, index) => - ExprKind::Field { lhs: source.to_ref(), - name: Field::new(index.node as usize) }, - hir::ExprCast(ref source, _) => - ExprKind::Cast { source: source.to_ref() }, - hir::ExprType(ref source, _) => - return source.make_mirror(cx), - hir::ExprBox(ref value) => - ExprKind::Box { - value: value.to_ref(), - value_extents: cx.tcx.region_maps.node_extent(value.id) - }, - hir::ExprVec(ref fields) => - ExprKind::Vec { fields: fields.to_ref() }, - hir::ExprTup(ref fields) => - ExprKind::Tuple { fields: fields.to_ref() }, - }; + debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span); - let mut expr = Expr { - temp_lifetime: temp_lifetime, - ty: expr_ty, - span: self.span, - kind: kind, - }; + let mut expr = make_mirror_unadjusted(cx, self); debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}", expr, cx.tcx.tables.borrow().adjustments.get(&self.id)); @@ -587,6 +202,406 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { } } +fn make_mirror_unadjusted<'a, 'tcx>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) -> Expr<'tcx> { + let expr_ty = cx.tcx.expr_ty(expr); + let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id); + + let kind = match expr.node { + // Here comes the interesting stuff: + hir::ExprMethodCall(_, _, ref args) => { + // Rewrite a.b(c) into UFCS form like Trait::b(a, c) + let expr = method_callee(cx, expr, ty::MethodCall::expr(expr.id)); + let args = args.iter() + .map(|e| e.to_ref()) + .collect(); + ExprKind::Call { + ty: expr.ty, + fun: expr.to_ref(), + args: args, + } + } + + hir::ExprCall(ref fun, ref args) => { + if cx.tcx.is_method_call(expr.id) { + // The callee is something implementing Fn, FnMut, or FnOnce. + // Find the actual method implementation being called and + // build the appropriate UFCS call expression with the + // callee-object as expr parameter. + + // rewrite f(u, v) into FnOnce::call_once(f, (u, v)) + + let method = method_callee(cx, expr, ty::MethodCall::expr(expr.id)); + + let sig = match method.ty.sty { + ty::TyFnDef(_, _, fn_ty) => &fn_ty.sig, + _ => span_bug!(expr.span, "type of method is not an fn") + }; + + let sig = cx.tcx.no_late_bound_regions(sig).unwrap_or_else(|| { + span_bug!(expr.span, "method call has late-bound regions") + }); + + assert_eq!(sig.inputs.len(), 2); + + let tupled_args = Expr { + ty: sig.inputs[1], + temp_lifetime: temp_lifetime, + span: expr.span, + kind: ExprKind::Tuple { + fields: args.iter().map(ToRef::to_ref).collect() + } + }; + + ExprKind::Call { + ty: method.ty, + fun: method.to_ref(), + args: vec![fun.to_ref(), tupled_args.to_ref()] + } + } else { + let adt_data = if let hir::ExprPath(..) = fun.node { + // Tuple-like ADTs are represented as ExprCall. We convert them here. + expr_ty.ty_adt_def().and_then(|adt_def|{ + match cx.tcx.def_map.borrow()[&fun.id].full_def() { + Def::Variant(_, variant_id) => { + Some((adt_def, adt_def.variant_index_with_id(variant_id))) + }, + Def::Struct(..) => { + Some((adt_def, 0)) + }, + _ => None + } + }) + } else { None }; + if let Some((adt_def, index)) = adt_data { + let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(fun.id).substs); + let field_refs = args.iter().enumerate().map(|(idx, e)| FieldExprRef { + name: Field::new(idx), + expr: e.to_ref() + }).collect(); + ExprKind::Adt { + adt_def: adt_def, + substs: substs, + variant_index: index, + fields: field_refs, + base: None + } + } else { + ExprKind::Call { + ty: cx.tcx.node_id_to_type(fun.id), + fun: fun.to_ref(), + args: args.to_ref(), + } + } + } + } + + hir::ExprAddrOf(mutbl, ref expr) => { + let region = match expr_ty.sty { + ty::TyRef(r, _) => r, + _ => span_bug!(expr.span, "type of & not region"), + }; + ExprKind::Borrow { + region: *region, + borrow_kind: to_borrow_kind(mutbl), + arg: expr.to_ref(), + } + } + + hir::ExprBlock(ref blk) => { + ExprKind::Block { body: &blk } + } + + hir::ExprAssign(ref lhs, ref rhs) => { + ExprKind::Assign { + lhs: lhs.to_ref(), + rhs: rhs.to_ref(), + } + } + + hir::ExprAssignOp(op, ref lhs, ref rhs) => { + if cx.tcx.is_method_call(expr.id) { + let pass_args = if op.node.is_by_value() { + PassArgs::ByValue + } else { + PassArgs::ByRef + }; + overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), + pass_args, lhs.to_ref(), vec![rhs]) + } else { + ExprKind::AssignOp { + op: bin_op(op.node), + lhs: lhs.to_ref(), + rhs: rhs.to_ref(), + } + } + } + + hir::ExprLit(..) => ExprKind::Literal { + literal: cx.const_eval_literal(expr) + }, + + hir::ExprBinary(op, ref lhs, ref rhs) => { + if cx.tcx.is_method_call(expr.id) { + let pass_args = if op.node.is_by_value() { + PassArgs::ByValue + } else { + PassArgs::ByRef + }; + overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), + pass_args, lhs.to_ref(), vec![rhs]) + } else { + // FIXME overflow + match op.node { + hir::BinOp_::BiAnd => { + ExprKind::LogicalOp { + op: LogicalOp::And, + lhs: lhs.to_ref(), + rhs: rhs.to_ref(), + } + } + hir::BinOp_::BiOr => { + ExprKind::LogicalOp { + op: LogicalOp::Or, + lhs: lhs.to_ref(), + rhs: rhs.to_ref(), + } + } + _ => { + let op = bin_op(op.node); + ExprKind::Binary { + op: op, + lhs: lhs.to_ref(), + rhs: rhs.to_ref(), + } + } + } + } + } + + hir::ExprIndex(ref lhs, ref index) => { + if cx.tcx.is_method_call(expr.id) { + overloaded_lvalue(cx, expr, ty::MethodCall::expr(expr.id), + PassArgs::ByValue, lhs.to_ref(), vec![index]) + } else { + ExprKind::Index { + lhs: lhs.to_ref(), + index: index.to_ref(), + } + } + } + + hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => { + if cx.tcx.is_method_call(expr.id) { + overloaded_lvalue(cx, expr, ty::MethodCall::expr(expr.id), + PassArgs::ByValue, arg.to_ref(), vec![]) + } else { + ExprKind::Deref { arg: arg.to_ref() } + } + } + + hir::ExprUnary(hir::UnOp::UnNot, ref arg) => { + if cx.tcx.is_method_call(expr.id) { + overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), + PassArgs::ByValue, arg.to_ref(), vec![]) + } else { + ExprKind::Unary { + op: UnOp::Not, + arg: arg.to_ref(), + } + } + } + + hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => { + if cx.tcx.is_method_call(expr.id) { + overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), + PassArgs::ByValue, arg.to_ref(), vec![]) + } else { + // FIXME runtime-overflow + if let hir::ExprLit(_) = arg.node { + ExprKind::Literal { + literal: cx.const_eval_literal(expr), + } + } else { + ExprKind::Unary { + op: UnOp::Neg, + arg: arg.to_ref(), + } + } + } + } + + hir::ExprStruct(_, ref fields, ref base) => { + match expr_ty.sty { + ty::TyStruct(adt, substs) => { + let field_refs = field_refs(&adt.variants[0], fields); + ExprKind::Adt { + adt_def: adt, + variant_index: 0, + substs: substs, + fields: field_refs, + base: base.as_ref().map(|base| { + FruInfo { + base: base.to_ref(), + field_types: cx.tcx.tables + .borrow() + .fru_field_types[&expr.id] + .clone() + } + }) + } + } + ty::TyEnum(adt, substs) => { + match cx.tcx.def_map.borrow()[&expr.id].full_def() { + Def::Variant(enum_id, variant_id) => { + debug_assert!(adt.did == enum_id); + assert!(base.is_none()); + + let index = adt.variant_index_with_id(variant_id); + let field_refs = field_refs(&adt.variants[index], fields); + ExprKind::Adt { + adt_def: adt, + variant_index: index, + substs: substs, + fields: field_refs, + base: None + } + } + ref def => { + span_bug!( + expr.span, + "unexpected def: {:?}", + def); + } + } + } + _ => { + span_bug!( + expr.span, + "unexpected type for struct literal: {:?}", + expr_ty); + } + } + } + + hir::ExprClosure(..) => { + let closure_ty = cx.tcx.expr_ty(expr); + let (def_id, substs) = match closure_ty.sty { + ty::TyClosure(def_id, ref substs) => (def_id, substs), + _ => { + span_bug!(expr.span, + "closure expr w/o closure type: {:?}", + closure_ty); + } + }; + let upvars = cx.tcx.with_freevars(expr.id, |freevars| { + freevars.iter() + .enumerate() + .map(|(i, fv)| capture_freevar(cx, expr, fv, substs.upvar_tys[i])) + .collect() + }); + ExprKind::Closure { + closure_id: def_id, + substs: &substs, + upvars: upvars, + } + } + + hir::ExprPath(..) => { + convert_path_expr(cx, expr) + } + + hir::ExprInlineAsm(ref asm, ref outputs, ref inputs) => { + ExprKind::InlineAsm { + asm: asm, + outputs: outputs.to_ref(), + inputs: inputs.to_ref() + } + } + + // Now comes the rote stuff: + + hir::ExprRepeat(ref v, ref c) => ExprKind::Repeat { + value: v.to_ref(), + count: TypedConstVal { + ty: cx.tcx.expr_ty(c), + span: c.span, + value: match const_eval::eval_const_expr(cx.tcx, c) { + ConstVal::Integral(ConstInt::Usize(u)) => u, + other => bug!("constant evaluation of repeat count yielded {:?}", other), + }, + } + }, + hir::ExprRet(ref v) => + ExprKind::Return { value: v.to_ref() }, + hir::ExprBreak(label) => + ExprKind::Break { label: label.map(|_| loop_label(cx, expr)) }, + hir::ExprAgain(label) => + ExprKind::Continue { label: label.map(|_| loop_label(cx, expr)) }, + hir::ExprMatch(ref discr, ref arms, _) => + ExprKind::Match { discriminant: discr.to_ref(), + arms: arms.iter().map(|a| convert_arm(cx, a)).collect() }, + hir::ExprIf(ref cond, ref then, ref otherwise) => + ExprKind::If { condition: cond.to_ref(), + then: block::to_expr_ref(cx, then), + otherwise: otherwise.to_ref() }, + hir::ExprWhile(ref cond, ref body, _) => + ExprKind::Loop { condition: Some(cond.to_ref()), + body: block::to_expr_ref(cx, body) }, + hir::ExprLoop(ref body, _) => + ExprKind::Loop { condition: None, + body: block::to_expr_ref(cx, body) }, + hir::ExprField(ref source, name) => { + let index = match cx.tcx.expr_ty_adjusted(source).sty { + ty::TyStruct(adt_def, _) => + adt_def.variants[0].index_of_field_named(name.node), + ref ty => + span_bug!( + expr.span, + "field of non-struct: {:?}", + ty), + }; + let index = index.unwrap_or_else(|| { + span_bug!( + expr.span, + "no index found for field `{}`", + name.node) + }); + ExprKind::Field { lhs: source.to_ref(), name: Field::new(index) } + } + hir::ExprTupField(ref source, index) => + ExprKind::Field { lhs: source.to_ref(), + name: Field::new(index.node as usize) }, + hir::ExprCast(ref source, _) => { + // Check to see if this cast is a "coercion cast", where the cast is actually done + // using a coercion (or is a no-op). + if let Some(&TyCastKind::CoercionCast) = cx.tcx.cast_kinds.borrow().get(&source.id) { + // Skip the actual cast itexpr, as it's now a no-op. + return source.make_mirror(cx); + } else { + ExprKind::Cast { source: source.to_ref() } + } + } + hir::ExprType(ref source, _) => + return source.make_mirror(cx), + hir::ExprBox(ref value) => + ExprKind::Box { + value: value.to_ref(), + value_extents: cx.tcx.region_maps.node_extent(value.id) + }, + hir::ExprVec(ref fields) => + ExprKind::Vec { fields: fields.to_ref() }, + hir::ExprTup(ref fields) => + ExprKind::Tuple { fields: fields.to_ref() }, + }; + + Expr { + temp_lifetime: temp_lifetime, + ty: expr_ty, + span: expr.span, + kind: kind, + } +} + fn method_callee<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &hir::Expr, method_call: ty::MethodCall) diff --git a/src/test/run-pass/mir_ascription_coercion.rs b/src/test/run-pass/mir_ascription_coercion.rs new file mode 100644 index 000000000000..b227be9c543b --- /dev/null +++ b/src/test/run-pass/mir_ascription_coercion.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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 the result of type ascription has adjustments applied + +#![feature(rustc_attrs, type_ascription)] + +#[rustc_mir] +fn main() { + let x = [1, 2, 3]; + // The RHS should coerce to &[i32] + let _y : &[i32] = &x : &[i32; 3]; +} diff --git a/src/test/run-pass/mir_coercion_casts.rs b/src/test/run-pass/mir_coercion_casts.rs new file mode 100644 index 000000000000..4d5c59276d75 --- /dev/null +++ b/src/test/run-pass/mir_coercion_casts.rs @@ -0,0 +1,22 @@ +// Copyright 2016 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 the coercion casts are handled properly + +#![feature(rustc_attrs)] + +#[rustc_mir] +fn main() { + // This should produce only a reification of f, + // not a fn -> fn cast as well + let _ = f as fn(&()); +} + +fn f<'a>(_: &'a ()) { }