use rustc_data_structures::control_flow_graph::ControlFlowGraph;
use hir::def_id::DefId;
use ty::subst::Substs;
- use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
+ use ty::{self, AdtDef, ClosureSubsts, Region, Ty};
use util::ppaux;
use rustc_back::slice;
use hir::InlineAsm;
pub promoted: IndexVec<Promoted, Mir<'tcx>>,
/// Return type of the function.
- pub return_ty: FnOutput<'tcx>,
+ pub return_ty: Ty<'tcx>,
/// Variables: these are stack slots corresponding to user variables. They may be
/// assigned many times.
pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
promoted: IndexVec<Promoted, Mir<'tcx>>,
- return_ty: FnOutput<'tcx>,
+ return_ty: Ty<'tcx>,
var_decls: IndexVec<Var, VarDecl<'tcx>>,
arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum StatementKind<'tcx> {
+ /// Write the RHS Rvalue to the LHS Lvalue.
Assign(Lvalue<'tcx>, Rvalue<'tcx>),
- SetDiscriminant{ lvalue: Lvalue<'tcx>, variant_index: usize },
+
+ /// Write the discriminant for a variant to the enum Lvalue.
+ SetDiscriminant { lvalue: Lvalue<'tcx>, variant_index: usize },
+
+ /// Start a live range for the storage of the local.
+ StorageLive(Lvalue<'tcx>),
+
+ /// End the current live range for the storage of the local.
+ StorageDead(Lvalue<'tcx>),
}
impl<'tcx> Debug for Statement<'tcx> {
use self::StatementKind::*;
match self.kind {
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
+ StorageLive(ref lv) => write!(fmt, "StorageLive({:?})", lv),
+ StorageDead(ref lv) => write!(fmt, "StorageDead({:?})", lv),
SetDiscriminant{lvalue: ref lv, variant_index: index} => {
write!(fmt, "discriminant({:?}) = {:?}", lv, index)
}
use middle::const_val::ConstVal;
use hir::def_id::DefId;
use ty::subst::Substs;
- use ty::{ClosureSubsts, FnOutput, Region, Ty};
+ use ty::{ClosureSubsts, Region, Ty};
use mir::repr::*;
use rustc_const_math::ConstUsize;
use rustc_data_structures::tuple_slice::TupleSlice;
//
// For the most part, we do not destructure things external to the
// MIR, e.g. types, spans, etc, but simply visit them and stop. This
- // avoids duplication with other visitors like `TypeFoldable`. But
- // there is one exception: we do destructure the `FnOutput` to reach
- // the type within. Just because.
+ // avoids duplication with other visitors like `TypeFoldable`.
//
// ## Updating
//
self.super_source_info(source_info);
}
- fn visit_fn_output(&mut self,
- fn_output: & $($mutability)* FnOutput<'tcx>) {
- self.super_fn_output(fn_output);
- }
-
fn visit_ty(&mut self,
ty: & $($mutability)* Ty<'tcx>) {
self.super_ty(ty);
self.visit_visibility_scope_data(scope);
}
- self.visit_fn_output(&$($mutability)* mir.return_ty);
+ self.visit_ty(&$($mutability)* mir.return_ty);
for var_decl in &$($mutability)* mir.var_decls {
self.visit_var_decl(var_decl);
StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
self.visit_lvalue(lvalue, LvalueContext::Store);
}
+ StatementKind::StorageLive(ref $($mutability)* lvalue) => {
+ self.visit_lvalue(lvalue, LvalueContext::StorageLive);
+ }
+ StatementKind::StorageDead(ref $($mutability)* lvalue) => {
+ self.visit_lvalue(lvalue, LvalueContext::StorageDead);
+ }
}
}
self.visit_visibility_scope(scope);
}
- fn super_fn_output(&mut self, fn_output: & $($mutability)* FnOutput<'tcx>) {
- match *fn_output {
- FnOutput::FnConverging(ref $($mutability)* ty) => {
- self.visit_ty(ty);
- }
- FnOutput::FnDiverging => {
- }
- }
- }
-
fn super_ty(&mut self, _ty: & $($mutability)* Ty<'tcx>) {
}
// Consumed as part of an operand
Consume,
+
+ // Starting and ending a storage live range
+ StorageLive,
+ StorageDead,
}
fn sort_string(&self, tcx: TyCtxt<'a, 'gcx, 'lcx>) -> String {
match self.sty {
ty::TyBool | ty::TyChar | ty::TyInt(_) |
- ty::TyUint(_) | ty::TyFloat(_) | ty::TyStr => self.to_string(),
+ ty::TyUint(_) | ty::TyFloat(_) | ty::TyStr | ty::TyNever => self.to_string(),
ty::TyTuple(ref tys) if tys.is_empty() => self.to_string(),
ty::TyEnum(def, _) => format!("enum `{}`", tcx.item_path_str(def.did)),
ty::TyArray(_, n) => format!("array of {} elements", n),
ty::TySlice(_) => "slice".to_string(),
ty::TyRawPtr(_) => "*-ptr".to_string(),
- ty::TyRef(_, _) => "&-ptr".to_string(),
+ ty::TyRef(region, tymut) => {
+ let tymut_string = tymut.to_string();
+ if tymut_string == "_" || //unknown type name,
+ tymut_string.len() > 10 || //name longer than saying "reference",
+ region.to_string() != "" //... or a complex type
+ {
+ match tymut {
+ ty::TypeAndMut{mutbl, ..} => {
+ format!("{}reference", match mutbl {
+ hir::Mutability::MutMutable => "mutable ",
+ _ => ""
+ })
+ }
+ }
+ } else {
+ format!("&{}", tymut_string)
+ }
+ }
ty::TyFnDef(..) => format!("fn item"),
ty::TyFnPtr(_) => "fn pointer".to_string(),
ty::TyTrait(ref inner) => {
// except according to those terms.
- use rustc::ty::{FnOutput, TyCtxt};
+ use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::util::nodemap::FnvHashMap;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
}
}
- struct MovePathDataBuilder<'a, 'tcx: 'a> {
- mir: &'a Mir<'tcx>,
+ struct MovePathDataBuilder<'tcx> {
pre_move_paths: Vec<PreMovePath<'tcx>>,
rev_lookup: MovePathLookup<'tcx>,
}
}
}
- impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> {
+ impl<'tcx> MovePathDataBuilder<'tcx> {
fn lookup(&mut self, lval: &Lvalue<'tcx>) -> Lookup<MovePathIndex> {
let proj = match *lval {
Lvalue::Var(var_idx) =>
// BlockContexts constructed on each iteration. (Moving is more
// straight-forward than mutable borrows in this instance.)
let mut builder = MovePathDataBuilder {
- mir: mir,
pre_move_paths: Vec::new(),
rev_lookup: MovePathLookup::new(mir),
};
Rvalue::InlineAsm { .. } => {}
}
}
+ StatementKind::StorageLive(_) |
+ StatementKind::StorageDead(_) => {}
StatementKind::SetDiscriminant{ .. } => {
span_bug!(stmt.source_info.span,
"SetDiscriminant should not exist during borrowck");
TerminatorKind::Return => {
let source = Location { block: bb,
index: bb_data.statements.len() };
- if let FnOutput::FnConverging(_) = bb_ctxt.builder.mir.return_ty {
- debug!("gather_moves Return on_move_out_lval return {:?}", source);
- bb_ctxt.on_move_out_lval(SK::Return, &Lvalue::ReturnPointer, source);
- } else {
- debug!("gather_moves Return on_move_out_lval \
- assuming unreachable return {:?}", source);
- }
+ debug!("gather_moves Return on_move_out_lval return {:?}", source);
+ bb_ctxt.on_move_out_lval(SK::Return, &Lvalue::ReturnPointer, source);
}
TerminatorKind::If { ref cond, targets: _ } => {
}
}
- struct BlockContext<'b, 'a: 'b, 'tcx: 'a> {
+ struct BlockContext<'b, 'tcx: 'b> {
_tcx: TyCtxt<'b, 'tcx, 'tcx>,
moves: &'b mut Vec<MoveOut>,
- builder: MovePathDataBuilder<'a, 'tcx>,
+ builder: MovePathDataBuilder<'tcx>,
path_map: &'b mut Vec<Vec<MoveOutIndex>>,
loc_map_bb: &'b mut Vec<Vec<MoveOutIndex>>,
}
- impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
+ impl<'b, 'tcx: 'b> BlockContext<'b, 'tcx> {
fn on_move_out_lval(&mut self,
stmt_kind: StmtKind,
lval: &Lvalue<'tcx>,
// Check for empty enum, because is_useful only works on inhabited types.
let pat_ty = cx.tcx.node_id_to_type(scrut.id);
if inlined_arms.is_empty() {
- if !pat_ty.is_empty(cx.tcx) {
+ if !pat_ty.is_uninhabited(cx.tcx) {
// We know the type is inhabited, so this must be wrong
let mut err = struct_span_err!(cx.tcx.sess, ex.span, E0002,
"non-exhaustive patterns: type {} is non-empty",
possibly adding wildcards or more match arms.");
err.emit();
}
- // If the type *is* empty, it's vacuously exhaustive
+ // If the type *is* uninhabited, it's vacuously exhaustive
return;
}
_: LoanCause) {
match kind {
MutBorrow => {
- span_err!(self.cx.tcx.sess, span, E0301,
+ struct_span_err!(self.cx.tcx.sess, span, E0301,
"cannot mutably borrow in a pattern guard")
+ .span_label(span, &format!("borrowed mutably in pattern guard"))
+ .emit();
}
ImmBorrow | UniqueImmBorrow => {}
}
fn mutate(&mut self, _: NodeId, span: Span, _: cmt, mode: MutateMode) {
match mode {
MutateMode::JustWrite | MutateMode::WriteAndRead => {
- span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard")
+ struct_span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard")
+ .span_label(span, &format!("assignment in pattern guard"))
+ .emit();
}
MutateMode::Init => {}
}
pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
fn_id: ast::NodeId,
arguments: A,
- return_ty: ty::FnOutput<'gcx>,
+ return_ty: Ty<'gcx>,
ast_block: &'gcx hir::Block)
-> (Mir<'tcx>, ScopeAuxiliaryVec)
where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
let span = tcx.map.span(item_id);
let mut builder = Builder::new(hir, span);
- let extent = ROOT_CODE_EXTENT;
+ let extent = tcx.region_maps.temporary_scope(ast_expr.id)
+ .unwrap_or(ROOT_CODE_EXTENT);
let mut block = START_BLOCK;
let _ = builder.in_scope(extent, block, |builder| {
let expr = builder.hir.mirror(ast_expr);
});
let ty = tcx.expr_ty_adjusted(ast_expr);
- builder.finish(vec![], IndexVec::new(), ty::FnConverging(ty))
+ builder.finish(vec![], IndexVec::new(), ty)
}
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fn finish(self,
upvar_decls: Vec<UpvarDecl>,
arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
- return_ty: ty::FnOutput<'tcx>)
+ return_ty: Ty<'tcx>)
-> (Mir<'tcx>, ScopeAuxiliaryVec) {
for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
if block.terminator.is_none() {
fn args_and_body<A>(&mut self,
mut block: BasicBlock,
- return_ty: ty::FnOutput<'tcx>,
+ return_ty: Ty<'tcx>,
arguments: A,
argument_extent: CodeExtent,
ast_block: &'gcx hir::Block)
}
// FIXME(#32959): temporary hack for the issue at hand
- let return_is_unit = if let ty::FnConverging(t) = return_ty {
- t.is_nil()
- } else {
- false
- };
+ let return_is_unit = return_ty.is_nil();
// start the first basic block and translate the body
unpack!(block = self.ast_block(&Lvalue::ReturnPointer, return_is_unit, block, ast_block));
use rustc::mir::repr::*;
use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
- use rustc::ty::{self, TyCtxt};
+ use rustc::ty::TyCtxt;
use syntax_pos::Span;
use build::Location;
if let Lvalue::Temp(index) = *lvalue {
// Ignore drops, if the temp gets promoted,
// then it's constant and thus drop is noop.
- if let LvalueContext::Drop = context {
- return;
+ // Storage live ranges are also irrelevant.
+ match context {
+ LvalueContext::Drop |
+ LvalueContext::StorageLive |
+ LvalueContext::StorageDead => return,
+ _ => {}
}
let temp = &mut self.temps[index];
let (mut rvalue, mut call) = (None, None);
let source_info = if stmt_idx < no_stmts {
let statement = &mut self.source[bb].statements[stmt_idx];
- let mut rhs = match statement.kind {
+ let rhs = match statement.kind {
StatementKind::Assign(_, ref mut rhs) => rhs,
- StatementKind::SetDiscriminant{ .. } =>
- span_bug!(statement.source_info.span,
- "cannot promote SetDiscriminant {:?}",
- statement),
+ _ => {
+ span_bug!(statement.source_info.span, "{:?} is not an assignment",
+ statement);
+ }
};
if self.keep_original {
rvalue = Some(rhs.clone());
let span = self.promoted.span;
let new_operand = Operand::Constant(Constant {
span: span,
- ty: self.promoted.return_ty.unwrap(),
+ ty: self.promoted.return_ty,
literal: Literal::Promoted {
index: Promoted::new(self.source.promoted.len())
}
StatementKind::Assign(_, ref mut rvalue) => {
mem::replace(rvalue, Rvalue::Use(new_operand))
}
- StatementKind::SetDiscriminant{ .. } => {
- span_bug!(statement.source_info.span,
- "cannot promote SetDiscriminant {:?}",
- statement);
- }
+ _ => bug!()
}
}
Candidate::ShuffleIndices(bb) => {
let statement = &mir[bb].statements[stmt_idx];
let dest = match statement.kind {
StatementKind::Assign(ref dest, _) => dest,
- StatementKind::SetDiscriminant{ .. } =>
- panic!("cannot promote SetDiscriminant"),
+ _ => {
+ span_bug!(statement.source_info.span,
+ "expected assignment to promote");
+ }
};
if let Lvalue::Temp(index) = *dest {
if temps[index] == TempState::PromotedOut {
parent_scope: None
}).into_iter().collect(),
IndexVec::new(),
- ty::FnConverging(ty),
+ ty,
IndexVec::new(),
IndexVec::new(),
IndexVec::new(),
for block in mir.basic_blocks_mut() {
block.statements.retain(|statement| {
match statement.kind {
- StatementKind::Assign(Lvalue::Temp(index), _) => {
+ StatementKind::Assign(Lvalue::Temp(index), _) |
+ StatementKind::StorageLive(Lvalue::Temp(index)) |
+ StatementKind::StorageDead(Lvalue::Temp(index)) => {
!promoted(index)
}
_ => true
}
}
- let return_ty = mir.return_ty.unwrap();
+ let return_ty = mir.return_ty;
self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
match self.mode {
fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
assert_eq!(self.location.block, bb);
- self.nest(|this| this.super_statement(bb, statement));
+ self.nest(|this| {
+ this.visit_source_info(&statement.source_info);
+ match statement.kind {
+ StatementKind::Assign(ref lvalue, ref rvalue) => {
+ this.visit_assign(bb, lvalue, rvalue);
+ }
+ StatementKind::SetDiscriminant { .. } |
+ StatementKind::StorageLive(_) |
+ StatementKind::StorageDead(_) => {}
+ }
+ });
self.location.statement_index += 1;
}
// Statics must be Sync.
if mode == Mode::Static {
- let ty = mir.return_ty.unwrap();
+ let ty = mir.return_ty;
tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| {
let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
let mut fulfillment_cx = traits::FulfillmentContext::new();
}
fn visit_mir(&mut self, mir: &Mir<'tcx>) {
- if let ty::FnConverging(t) = mir.return_ty {
- self.sanitize_type(&"return type", t);
- }
+ self.sanitize_type(&"return type", mir.return_ty);
for var_decl in &mir.var_decls {
self.sanitize_type(var_decl, var_decl.ty);
}
Lvalue::Static(def_id) =>
LvalueTy::Ty { ty: self.tcx().lookup_item_type(def_id).ty },
Lvalue::ReturnPointer => {
- if let ty::FnConverging(return_ty) = self.mir.return_ty {
- LvalueTy::Ty { ty: return_ty }
- } else {
- LvalueTy::Ty {
- ty: span_mirbug_and_err!(
- self, lvalue, "return in diverging function")
- }
- }
+ LvalueTy::Ty { ty: self.mir.return_ty }
}
Lvalue::Projection(ref proj) => {
let base_ty = self.sanitize_lvalue(&proj.base);
variant_index);
};
}
+ StatementKind::StorageLive(ref lv) |
+ StatementKind::StorageDead(ref lv) => {
+ match *lv {
+ Lvalue::Temp(_) | Lvalue::Var(_) => {}
+ _ => {
+ span_mirbug!(self, stmt, "bad lvalue: expected temp or var");
+ }
+ }
+ }
}
}
sig: &ty::FnSig<'tcx>,
destination: &Option<(Lvalue<'tcx>, BasicBlock)>) {
let tcx = self.tcx();
- match (destination, sig.output) {
- (&Some(..), ty::FnDiverging) => {
- span_mirbug!(self, term, "call to diverging function {:?} with dest", sig);
- }
- (&Some((ref dest, _)), ty::FnConverging(ty)) => {
+ match *destination {
+ Some((ref dest, _)) => {
let dest_ty = dest.ty(mir, tcx).to_ty(tcx);
- if let Err(terr) = self.sub_types(self.last_span, ty, dest_ty) {
+ if let Err(terr) = self.sub_types(self.last_span, sig.output, dest_ty) {
span_mirbug!(self, term,
"call dest mismatch ({:?} <- {:?}): {:?}",
- dest_ty, ty, terr);
+ dest_ty, sig.output, terr);
}
- }
- (&None, ty::FnDiverging) => {}
- (&None, ty::FnConverging(..)) => {
- span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
- }
+ },
+ None => {
+ // FIXME(canndrew): This is_never should probably be an is_uninhabited
+ if !sig.output.is_never() {
+ span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
+ }
+ },
}
}
let expr_ty = self.tcx.expr_ty(expr);
let def = match expr_ty.sty {
ty::TyFnDef(_, _, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
- output: ty::FnConverging(ty), ..
+ output: ty, ..
}), ..}) => ty,
_ => expr_ty
}.ty_adt_def().unwrap();
self.tcx.sess.add_lint(lint::builtin::PRIVATE_IN_PUBLIC,
node_id,
ty.span,
- format!("private type in public interface"));
+ format!("private type in public \
+ interface (error E0446)"));
}
}
}
let sig = ccx.tcx().erase_late_bound_regions(&ctor_ty.fn_sig());
let sig = ccx.tcx().normalize_associated_type(&sig);
- let result_ty = sig.output.unwrap();
+ let result_ty = sig.output;
// Get location to store the result. If the user does not care about
// the result, just make a stack slot
if !fcx.fn_ty.ret.is_ignore() {
let dest = fcx.get_ret_slot(bcx, "eret_slot");
let dest_val = adt::MaybeSizedValue::sized(dest); // Can return unsized value
- let repr = adt::represent_type(ccx, sig.output.unwrap());
+ let repr = adt::represent_type(ccx, sig.output);
let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize;
let mut arg_idx = 0;
for (i, arg_ty) in sig.inputs.into_iter().enumerate() {
};
unsafe {
llvm::LLVMSetInitializer(llglobal, llconst);
- let name =
+ let section_name =
cx.tcx().sess.cstore.metadata_section_name(&cx.sess().target.target);
- let name = CString::new(name).unwrap();
- llvm::LLVMSetSection(llglobal, name.as_ptr())
+ let name = CString::new(section_name).unwrap();
+ llvm::LLVMSetSection(llglobal, name.as_ptr());
+
+ // Also generate a .section directive to force no
+ // flags, at least for ELF outputs, so that the
+ // metadata doesn't get loaded into memory.
+ let directive = format!(".section {}", section_name);
+ let directive = CString::new(directive).unwrap();
+ llvm::LLVMSetModuleInlineAsm(cx.metadata_llmod(), directive.as_ptr())
}
return metadata;
}
assert_module_sources::assert_module_sources(tcx, &modules);
// Skip crate items and just output metadata in -Z no-trans mode.
- if tcx.sess.opts.no_trans {
+ if tcx.sess.opts.debugging_opts.no_trans {
let linker_info = LinkerInfo::new(&shared_ccx, &[]);
return CrateTranslation {
modules: modules,
use rustc::mir::visit as mir_visit;
use rustc::mir::visit::Visitor as MirVisitor;
+use rustc_const_eval as const_eval;
+
use syntax::abi::Abi;
use errors;
use syntax_pos::DUMMY_SP;
-use syntax::ast::NodeId;
use base::custom_coerce_unsize_info;
use context::SharedCrateContext;
use common::{fulfill_obligation, normalize_and_test_predicates, type_is_sized};
debug!("visiting operand {:?}", *operand);
let callee = match *operand {
- mir::Operand::Constant(mir::Constant { ty: &ty::TyS {
- sty: ty::TyFnDef(def_id, substs, _), ..
- }, .. }) => Some((def_id, substs)),
+ mir::Operand::Constant(ref constant) => {
+ if let ty::TyFnDef(def_id, substs, _) = constant.ty.sty {
+ // This is something that can act as a callee, proceed
+ Some((def_id, substs))
+ } else {
+ // This is not a callee, but we still have to look for
+ // references to `const` items
+ if let mir::Literal::Item { def_id, substs } = constant.literal {
+ let tcx = self.scx.tcx();
+ let substs = monomorphize::apply_param_substs(tcx,
+ self.param_substs,
+ &substs);
+
+ // If the constant referred to here is an associated
+ // item of a trait, we need to resolve it to the actual
+ // constant in the corresponding impl. Luckily
+ // const_eval::lookup_const_by_id() does that for us.
+ if let Some((expr, _)) = const_eval::lookup_const_by_id(tcx,
+ def_id,
+ Some(substs)) {
+ // The hir::Expr we get here is the initializer of
+ // the constant, what we really want is the item
+ // DefId.
+ let const_node_id = tcx.map.get_parent(expr.id);
+ let def_id = if tcx.map.is_inlined_node_id(const_node_id) {
+ tcx.sess.cstore.defid_for_inlined_node(const_node_id).unwrap()
+ } else {
+ tcx.map.local_def_id(const_node_id)
+ };
+
+ collect_const_item_neighbours(self.scx,
+ def_id,
+ substs,
+ self.output);
+ }
+ }
+
+ None
+ }
+ }
_ => None
};
ty::TyRef(..) |
ty::TyFnDef(..) |
ty::TyFnPtr(_) |
+ ty::TyNever |
ty::TyTrait(_) => {
/* nothing to do */
}
self.output.push(TransItem::Static(item.id));
}
hir::ItemConst(..) => {
- debug!("RootCollector: ItemConst({})",
- def_id_to_string(self.scx.tcx(),
- self.scx.tcx().map.local_def_id(item.id)));
- add_roots_for_const_item(self.scx, item.id, self.output);
+ // const items only generate translation items if they are
+ // actually used somewhere. Just declaring them is insufficient.
}
hir::ItemFn(_, _, _, _, ref generics, _) => {
if !generics.is_type_parameterized() {
// There are no translation items for constants themselves but their
// initializers might still contain something that produces translation items,
// such as cast that introduce a new vtable.
-fn add_roots_for_const_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
- const_item_node_id: NodeId,
- output: &mut Vec<TransItem<'tcx>>)
+fn collect_const_item_neighbours<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
+ def_id: DefId,
+ substs: &'tcx Substs<'tcx>,
+ output: &mut Vec<TransItem<'tcx>>)
{
- let def_id = scx.tcx().map.local_def_id(const_item_node_id);
-
// Scan the MIR in order to find function calls, closures, and
// drop-glue
let mir = errors::expect(scx.sess().diagnostic(), scx.get_mir(def_id),
|| format!("Could not find MIR for const: {:?}", def_id));
- let empty_substs = scx.empty_substs_for_def_id(def_id);
let visitor = MirNeighborCollector {
scx: scx,
mir: &mir,
output: output,
- param_substs: empty_substs
+ param_substs: substs
};
visit_mir_and_promoted(visitor, &mir);
use rustc::mir::traversal;
use common::{self, Block, BlockAndBuilder};
use glue;
+ use std::iter;
use super::rvalue;
pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>,
let local_types = mir.arg_decls.iter().map(|a| a.ty)
.chain(mir.var_decls.iter().map(|v| v.ty))
.chain(mir.temp_decls.iter().map(|t| t.ty))
- .chain(mir.return_ty.maybe_converging());
+ .chain(iter::once(mir.return_ty));
for (index, ty) in local_types.enumerate() {
let ty = bcx.monomorphize(&ty);
debug!("local {} has type {:?}", index, ty);
LvalueContext::Call => {
self.mark_assigned(index);
}
- LvalueContext::Consume => {
- }
+
+ LvalueContext::StorageLive |
+ LvalueContext::StorageDead |
+ LvalueContext::Consume => {}
+
LvalueContext::Store |
LvalueContext::Inspect |
LvalueContext::Borrow { .. } |
LvalueContext::Projection => {
self.mark_as_lvalue(index);
}
+
LvalueContext::Drop => {
let ty = lvalue.ty(self.mir, self.bcx.tcx());
let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx()));
it: &hir::ForeignItem,
n_tps: usize,
abi: Abi,
- inputs: Vec<ty::Ty<'tcx>>,
- output: ty::FnOutput<'tcx>) {
+ inputs: Vec<Ty<'tcx>>,
+ output: Ty<'tcx>) {
let tcx = ccx.tcx;
let def_id = tcx.map.local_def_id(it.id);
let i_ty = tcx.lookup_item_type(def_id);
}));
let i_n_tps = i_ty.generics.types.len(subst::FnSpace);
if i_n_tps != n_tps {
- span_err!(tcx.sess, it.span, E0094,
+ struct_span_err!(tcx.sess, it.span, E0094,
"intrinsic has wrong number of type \
parameters: found {}, expected {}",
- i_n_tps, n_tps);
+ i_n_tps, n_tps)
+ .span_label(it.span, &format!("expected {} type parameter", n_tps))
+ .emit();
} else {
require_same_types(ccx,
TypeOrigin::IntrinsicType(it.span),
return;
}
};
- (n_tps, inputs, ty::FnConverging(output))
+ (n_tps, inputs, output)
} else if &name[..] == "abort" || &name[..] == "unreachable" {
- (0, Vec::new(), ty::FnDiverging)
+ (0, Vec::new(), tcx.types.never)
} else {
let (n_tps, inputs, output) = match &name[..] {
"breakpoint" => (0, Vec::new(), tcx.mk_nil()),
abi: Abi::Rust,
sig: ty::Binder(FnSig {
inputs: vec![mut_u8],
- output: ty::FnOutput::FnConverging(tcx.mk_nil()),
+ output: tcx.mk_nil(),
variadic: false,
}),
});
return;
}
};
- (n_tps, inputs, ty::FnConverging(output))
+ (n_tps, inputs, output)
};
equate_intrinsic_type(ccx, it, n_tps, Abi::RustIntrinsic, inputs, output)
}
}
match_intrinsic_type_to_type(ccx, "return value", it.span,
&mut structural_to_nomimal,
- &intr.output, sig.output.unwrap());
+ &intr.output, sig.output);
return
}
None => {
};
equate_intrinsic_type(ccx, it, n_tps, Abi::PlatformIntrinsic,
- inputs, ty::FnConverging(output))
+ inputs, output)
}
// walk the expected type and the actual type in lock step, checking they're
// expects the types within the function to be consistent.
err_count_on_creation: usize,
- ret_ty: ty::FnOutput<'tcx>,
+ ret_ty: Ty<'tcx>,
ps: RefCell<UnsafetyState>,
let mut fcx = FnCtxt::new(inherited, fn_sig.output, body.id);
*fcx.ps.borrow_mut() = UnsafetyState::function(unsafety, unsafety_id);
- fn_sig.output = match fcx.ret_ty {
- ty::FnConverging(orig_ret_ty) => {
- fcx.require_type_is_sized(orig_ret_ty, decl.output.span(), traits::ReturnType);
- ty::FnConverging(fcx.instantiate_anon_types(&orig_ret_ty))
- }
- ty::FnDiverging => ty::FnDiverging
- };
- fcx.ret_ty = fn_sig.output;
+ fcx.require_type_is_sized(fcx.ret_ty, decl.output.span(), traits::ReturnType);
+ fcx.ret_ty = fcx.instantiate_anon_types(&fcx.ret_ty);
+ fn_sig.output = fcx.ret_ty;
{
let mut visit = GatherLocalsVisitor { fcx: &fcx, };
inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig);
- fcx.check_block_with_expected(body, match fcx.ret_ty {
- ty::FnConverging(result_type) => ExpectHasType(result_type),
- ty::FnDiverging => NoExpectation
- });
+ fcx.check_block_with_expected(body, ExpectHasType(fcx.ret_ty));
fcx
}
expected_type: Ty<'tcx>,
id: ast::NodeId) {
ccx.inherited(id).enter(|inh| {
- let fcx = FnCtxt::new(&inh, ty::FnConverging(expected_type), expr.id);
+ let fcx = FnCtxt::new(&inh, expected_type, expr.id);
fcx.require_type_is_sized(expected_type, expr.span, traits::ConstSized);
// Gather locals in statics (because of block expressions).
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn new(inh: &'a Inherited<'a, 'gcx, 'tcx>,
- rty: ty::FnOutput<'tcx>,
+ rty: Ty<'tcx>,
body_id: ast::NodeId)
-> FnCtxt<'a, 'gcx, 'tcx> {
FnCtxt {
debug!("write_ty({}, {:?}) in fcx {}",
node_id, ty, self.tag());
self.tables.borrow_mut().node_types.insert(node_id, ty);
+
+ // Add adjustments to !-expressions
+ if ty.is_never() {
+ if let Some(hir::map::NodeExpr(_)) = self.tcx.map.find(node_id) {
+ let adj = adjustment::AdjustNeverToAny(self.next_diverging_ty_var());
+ self.write_adjustment(node_id, adj);
+ }
+ }
}
pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts<'tcx>) {
let mut type_scheme = self.tcx.lookup_item_type(did);
if type_scheme.ty.is_fn() {
// Tuple variants have fn type even in type namespace, extract true variant type from it
- let fn_ret = self.tcx.no_late_bound_regions(&type_scheme.ty.fn_ret()).unwrap().unwrap();
+ let fn_ret = self.tcx.no_late_bound_regions(&type_scheme.ty.fn_ret()).unwrap();
type_scheme = ty::TypeScheme { ty: fn_ret, generics: type_scheme.generics }
}
let type_predicates = self.tcx.lookup_predicates(did);
pub fn write_nil(&self, node_id: ast::NodeId) {
self.write_ty(node_id, self.tcx.mk_nil());
}
+
+ pub fn write_never(&self, node_id: ast::NodeId) {
+ self.write_ty(node_id, self.tcx.types.never);
+ }
+
pub fn write_error(&self, node_id: ast::NodeId) {
self.write_ty(node_id, self.tcx.types.err);
}
}
pub fn expr_ty(&self, ex: &hir::Expr) -> Ty<'tcx> {
+ if let Some(&adjustment::AdjustNeverToAny(ref t))
+ = self.tables.borrow().adjustments.get(&ex.id) {
+ return t;
+ }
match self.tables.borrow().node_types.get(&ex.id) {
Some(&t) => t,
None => {
for ty in &self.unsolved_variables() {
let resolved = self.resolve_type_vars_if_possible(ty);
if self.type_var_diverges(resolved) {
- debug!("default_type_parameters: defaulting `{:?}` to `()` because it diverges",
+ debug!("default_type_parameters: defaulting `{:?}` to `!` because it diverges",
resolved);
- self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
+ self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
+ self.tcx.mk_diverging_default());
} else {
match self.type_is_unconstrained_numeric(resolved) {
UnconstrainedInt => {
for ty in &unsolved_variables {
let resolved = self.resolve_type_vars_if_possible(ty);
if self.type_var_diverges(resolved) {
- self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
+ self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
+ self.tcx.mk_diverging_default());
} else {
match self.type_is_unconstrained_numeric(resolved) {
UnconstrainedInt | UnconstrainedFloat => {
let _ = self.commit_if_ok(|_: &infer::CombinedSnapshot| {
for ty in &unbound_tyvars {
if self.type_var_diverges(ty) {
- self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
+ self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
+ self.tcx.mk_diverging_default());
} else {
match self.type_is_unconstrained_numeric(ty) {
UnconstrainedInt => {
// reporting for more then one conflict.
for ty in &unbound_tyvars {
if self.type_var_diverges(ty) {
- self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
+ self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
+ self.tcx.mk_diverging_default());
} else {
match self.type_is_unconstrained_numeric(ty) {
UnconstrainedInt => {
// extract method return type, which will be &T;
// all LB regions should have been instantiated during method lookup
let ret_ty = method.ty.fn_ret();
- let ret_ty = self.tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap();
+ let ret_ty = self.tcx.no_late_bound_regions(&ret_ty).unwrap();
// method returns &T, but the type as visible to user is T, so deref
ret_ty.builtin_deref(true, NoPreference).unwrap()
args_no_rcvr: &'gcx [P<hir::Expr>],
tuple_arguments: TupleArgumentsFlag,
expected: Expectation<'tcx>)
- -> ty::FnOutput<'tcx> {
+ -> Ty<'tcx> {
if method_fn_ty.references_error() {
let err_inputs = self.err_args(args_no_rcvr.len());
self.check_argument_types(sp, &err_inputs[..], &[], args_no_rcvr,
false, tuple_arguments);
- ty::FnConverging(self.tcx.types.err)
+ self.tcx.types.err
} else {
match method_fn_ty.sty {
ty::TyFnDef(_, _, ref fty) => {
}
if let Some(&arg_ty) = self.tables.borrow().node_types.get(&arg.id) {
- any_diverges = any_diverges || self.type_var_diverges(arg_ty);
+ // FIXME(canndrew): This is_never should probably be an is_uninhabited
+ any_diverges = any_diverges ||
+ self.type_var_diverges(arg_ty) ||
+ arg_ty.is_never();
}
}
if any_diverges && !warned {
fn write_call(&self,
call_expr: &hir::Expr,
- output: ty::FnOutput<'tcx>) {
- self.write_ty(call_expr.id, match output {
- ty::FnConverging(output_ty) => output_ty,
- ty::FnDiverging => self.next_diverging_ty_var()
- });
+ output: Ty<'tcx>) {
+ self.write_ty(call_expr.id, output);
}
// AST fragment checking
fn expected_types_for_fn_args(&self,
call_span: Span,
expected_ret: Expectation<'tcx>,
- formal_ret: ty::FnOutput<'tcx>,
+ formal_ret: Ty<'tcx>,
formal_args: &[Ty<'tcx>])
-> Vec<Ty<'tcx>> {
let expected_args = expected_ret.only_has_type(self).and_then(|ret_ty| {
- if let ty::FnConverging(formal_ret_ty) = formal_ret {
- self.commit_regions_if_ok(|| {
- // Attempt to apply a subtyping relationship between the formal
- // return type (likely containing type variables if the function
- // is polymorphic) and the expected return type.
- // No argument expectations are produced if unification fails.
- let origin = TypeOrigin::Misc(call_span);
- let ures = self.sub_types(false, origin, formal_ret_ty, ret_ty);
- // FIXME(#15760) can't use try! here, FromError doesn't default
- // to identity so the resulting type is not constrained.
- match ures {
- // FIXME(#32730) propagate obligations
- Ok(InferOk { obligations, .. }) => assert!(obligations.is_empty()),
- Err(e) => return Err(e),
- }
-
- // Record all the argument types, with the substitutions
- // produced from the above subtyping unification.
- Ok(formal_args.iter().map(|ty| {
- self.resolve_type_vars_if_possible(ty)
- }).collect())
- }).ok()
- } else {
- None
- }
+ self.commit_regions_if_ok(|| {
+ // Attempt to apply a subtyping relationship between the formal
+ // return type (likely containing type variables if the function
+ // is polymorphic) and the expected return type.
+ // No argument expectations are produced if unification fails.
+ let origin = TypeOrigin::Misc(call_span);
+ let ures = self.sub_types(false, origin, formal_ret, ret_ty);
+ // FIXME(#15760) can't use try! here, FromError doesn't default
+ // to identity so the resulting type is not constrained.
+ match ures {
+ // FIXME(#32730) propagate obligations
+ Ok(InferOk { obligations, .. }) => assert!(obligations.is_empty()),
+ Err(e) => return Err(e),
+ }
+
+ // Record all the argument types, with the substitutions
+ // produced from the above subtyping unification.
+ Ok(formal_args.iter().map(|ty| {
+ self.resolve_type_vars_if_possible(ty)
+ }).collect())
+ }).ok()
}).unwrap_or(vec![]);
debug!("expected_types_for_fn_args(formal={:?} -> {:?}, expected={:?} -> {:?})",
formal_args, formal_ret,
}
self.write_nil(id);
}
- hir::ExprBreak(_) => { self.write_ty(id, self.next_diverging_ty_var()); }
- hir::ExprAgain(_) => { self.write_ty(id, self.next_diverging_ty_var()); }
+ hir::ExprBreak(_) => { self.write_never(id); }
+ hir::ExprAgain(_) => { self.write_never(id); }
hir::ExprRet(ref expr_opt) => {
- match self.ret_ty {
- ty::FnConverging(result_type) => {
- if let Some(ref e) = *expr_opt {
- self.check_expr_coercable_to_type(&e, result_type);
- } else {
- let eq_result = self.eq_types(false,
- TypeOrigin::Misc(expr.span),
- result_type,
- tcx.mk_nil())
- // FIXME(#32730) propagate obligations
- .map(|InferOk { obligations, .. }| assert!(obligations.is_empty()));
- if eq_result.is_err() {
- struct_span_err!(tcx.sess, expr.span, E0069,
- "`return;` in a function whose return type is not `()`")
- .span_label(expr.span, &format!("return type is not ()"))
- .emit();
- }
- }
- }
- ty::FnDiverging => {
- if let Some(ref e) = *expr_opt {
- self.check_expr(&e);
- }
- struct_span_err!(tcx.sess, expr.span, E0166,
- "`return` in a function declared as diverging")
- .span_label(expr.span, &format!("diverging function cannot return"))
+ if let Some(ref e) = *expr_opt {
+ self.check_expr_coercable_to_type(&e, self.ret_ty);
+ } else {
+ let eq_result = self.eq_types(false,
+ TypeOrigin::Misc(expr.span),
+ self.ret_ty,
+ tcx.mk_nil())
+ // FIXME(#32730) propagate obligations
+ .map(|InferOk { obligations, .. }| assert!(obligations.is_empty()));
+ if eq_result.is_err() {
+ struct_span_err!(tcx.sess, expr.span, E0069,
+ "`return;` in a function whose return type is not `()`")
+ .span_label(expr.span, &format!("return type is not ()"))
.emit();
}
}
- self.write_ty(id, self.next_diverging_ty_var());
+ self.write_never(id);
}
hir::ExprAssign(ref lhs, ref rhs) => {
self.check_expr_with_lvalue_pref(&lhs, PreferMutLvalue);
let tcx = self.tcx;
if !tcx.expr_is_lval(&lhs) {
- span_err!(tcx.sess, expr.span, E0070,
- "invalid left-hand side expression");
+ struct_span_err!(
+ tcx.sess, expr.span, E0070,
+ "invalid left-hand side expression")
+ .span_label(
+ expr.span,
+ &format!("left-hand of expression not valid"))
+ .emit();
}
let lhs_ty = self.expr_ty(&lhs);
hir::ExprLoop(ref body, _) => {
self.check_block_no_value(&body);
if !may_break(tcx, expr.id, &body) {
- self.write_ty(id, self.next_diverging_ty_var());
+ self.write_never(id);
} else {
self.write_nil(id);
}
"unreachable statement".to_string());
warned = true;
}
- any_diverges = any_diverges || self.type_var_diverges(s_ty);
+ // FIXME(canndrew): This is_never should probably be an is_uninhabited
+ any_diverges = any_diverges ||
+ self.type_var_diverges(s_ty) ||
+ s_ty.is_never();
any_err = any_err || s_ty.references_error();
}
match blk.expr {
let tcx = self.tcx;
if !tcx.expr_is_lval(lhs_expr) {
- span_err!(tcx.sess, lhs_expr.span, E0067, "invalid left-hand side expression");
+ struct_span_err!(
+ tcx.sess, lhs_expr.span,
+ E0067, "invalid left-hand side expression")
+ .span_label(
+ lhs_expr.span,
+ &format!("invalid expression for left-hand side"))
+ .emit();
}
}
// extract return type for method; all late bound regions
// should have been instantiated by now
let ret_ty = method_ty.fn_ret();
- Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap())
+ Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap())
}
None => {
Err(())
// Allows tuple structs and variants in more contexts,
// Permits numeric fields in struct expressions and patterns.
- (active, relaxed_adts, "1.12.0", Some(35626))
+ (active, relaxed_adts, "1.12.0", Some(35626)),
+
+ // The `!` type
+ (active, never_type, "1.13.0", Some(35121))
);
declare_features! (
(accepted, issue_5723_bootstrap, "1.0.0", None),
(accepted, macro_rules, "1.0.0", None),
// Allows using #![no_std]
- (accepted, no_std, "1.0.0", None),
+ (accepted, no_std, "1.6.0", None),
(accepted, slicing_syntax, "1.0.0", None),
(accepted, struct_variant, "1.0.0", None),
// These are used to test this portion of the compiler, they don't actually
gate_feature_post!(&self, conservative_impl_trait, ty.span,
"`impl Trait` is experimental");
}
+ ast::TyKind::Never => {
+ gate_feature_post!(&self, never_type, ty.span,
+ "The `!` type is experimental");
+ },
_ => {}
}
visit::walk_ty(self, ty)
}
+ fn visit_fn_ret_ty(&mut self, ret_ty: &ast::FunctionRetTy) {
+ if let ast::FunctionRetTy::Ty(ref output_ty) = *ret_ty {
+ match output_ty.node {
+ ast::TyKind::Never => return,
+ _ => (),
+ };
+ self.visit_ty(output_ty)
+ }
+ }
+
fn visit_expr(&mut self, e: &ast::Expr) {
match e.node {
ast::ExprKind::Box(_) => {
visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate);
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum UnstableFeatures {
/// Hard errors for unstable features are active, as on
/// beta/stable channels.
/// Parse optional return type [ -> TY ] in function decl
pub fn parse_ret_ty(&mut self) -> PResult<'a, FunctionRetTy> {
if self.eat(&token::RArrow) {
- if self.eat(&token::Not) {
- Ok(FunctionRetTy::None(self.last_span))
- } else {
- Ok(FunctionRetTy::Ty(self.parse_ty()?))
- }
+ Ok(FunctionRetTy::Ty(self.parse_ty()?))
} else {
let pos = self.span.lo;
Ok(FunctionRetTy::Default(mk_sp(pos, pos)))
} else {
TyKind::Tup(ts)
}
+ } else if self.eat(&token::Not) {
+ TyKind::Never
} else if self.check(&token::BinOp(token::Star)) {
// STAR POINTER (bare pointer?)
self.bump();
}
/// Parse a structure field
- fn parse_name_and_ty(&mut self, pr: Visibility,
- attrs: Vec<Attribute> ) -> PResult<'a, StructField> {
- let lo = match pr {
- Visibility::Inherited => self.span.lo,
- _ => self.last_span.lo,
- };
+ fn parse_name_and_ty(&mut self,
+ lo: BytePos,
+ vis: Visibility,
+ attrs: Vec<Attribute>)
+ -> PResult<'a, StructField> {
let name = self.parse_ident()?;
self.expect(&token::Colon)?;
let ty = self.parse_ty_sum()?;
Ok(StructField {
span: mk_sp(lo, self.last_span.hi),
ident: Some(name),
- vis: pr,
+ vis: vis,
id: ast::DUMMY_NODE_ID,
ty: ty,
attrs: attrs,
/// Parse a structure field declaration
pub fn parse_single_struct_field(&mut self,
+ lo: BytePos,
vis: Visibility,
attrs: Vec<Attribute> )
-> PResult<'a, StructField> {
- let a_var = self.parse_name_and_ty(vis, attrs)?;
+ let a_var = self.parse_name_and_ty(lo, vis, attrs)?;
match self.token {
token::Comma => {
self.bump();
/// Parse an element of a struct definition
fn parse_struct_decl_field(&mut self) -> PResult<'a, StructField> {
let attrs = self.parse_outer_attributes()?;
+ let lo = self.span.lo;
let vis = self.parse_visibility(true)?;
- self.parse_single_struct_field(vis, attrs)
+ self.parse_single_struct_field(lo, vis, attrs)
}
// If `allow_path` is false, just parse the `pub` in `pub(path)` (but still parse `pub(crate)`)